From 1876c26df8e69a665d13e62018e2b42fb1aa67b4 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Mon, 18 Mar 2024 18:56:17 +0100 Subject: [PATCH 001/119] Refactor: improve styles --- .../TradeWizardSelectOfferView.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java index fff796de69..e9120e16b8 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java @@ -60,9 +60,9 @@ @Slf4j class TradeWizardSelectOfferView extends View { - private final static int TABLE_WIDTH = 800; - private final HBox noMatchingOffersBox; + private static final int TABLE_WIDTH = 800; + private final HBox noMatchingOffersBox; private final BisqTableView tableView; private final Label headlineLabel, subtitleLabel; private Button goBackButton, browseOfferbookButton; @@ -170,9 +170,7 @@ private void maybeConfigTableView() { } // Maker - String peer = model.getDirection() == Direction.BUY ? - Res.get("offer.seller") : - Res.get("offer.buyer"); + String peer = model.getDirection() == Direction.BUY ? Res.get("offer.seller") : Res.get("offer.buyer"); tableView.getColumns().add(new BisqTableColumn.Builder() .title(peer) .left() @@ -203,9 +201,9 @@ private void maybeConfigTableView() { } // BTC amount - String baseAmountTitle = model.getDirection().isBuy() ? - Res.get("bisqEasy.tradeWizard.review.table.baseAmount.buyer") : - Res.get("bisqEasy.tradeWizard.review.table.baseAmount.seller"); + String baseAmountTitle = model.getDirection().isBuy() + ? Res.get("bisqEasy.tradeWizard.review.table.baseAmount.buyer") + : Res.get("bisqEasy.tradeWizard.review.table.baseAmount.seller"); tableView.getColumns().add(new BisqTableColumn.Builder() .title(baseAmountTitle) .minWidth(160) @@ -262,8 +260,6 @@ public void updateItem(final ListItem item, boolean empty) { private Callback, TableCell> getReputationCellFactory() { return new Callback<>() { - - @Override public TableCell call(TableColumn column) { return new TableCell<>() { @@ -301,7 +297,6 @@ public void updateItem(final ListItem item, boolean empty) { private Callback, TableCell> getSelectButtonCellFactory() { return column -> new TableCell<>() { - private final Button button = new Button(Res.get("bisqEasy.tradeWizard.selectOffer.table.select")); private TableRow tableRow; private Subscription selectedItemPin; From 0c656ca141154983de8a53f385166f600824a0dc Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Mon, 18 Mar 2024 19:34:33 +0100 Subject: [PATCH 002/119] Improve matching offers table styles --- .../TradeWizardSelectOfferView.java | 9 ++-- .../src/main/resources/css/bisq_easy.css | 44 ++++--------------- 2 files changed, 15 insertions(+), 38 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java index e9120e16b8..9495e14b18 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java @@ -85,14 +85,16 @@ class TradeWizardSelectOfferView extends View(model.getSortedList()); tableView.getStyleClass().add("bisq-easy-trade-wizard-select-offer"); tableView.setMinWidth(TABLE_WIDTH); - tableView.setMaxWidth(tableView.getMinWidth()); // fits 4 rows tableView.setMaxHeight(262); // 4 * 55 (row height) + 40 (header height) + 2 (border) + VBox tableContainer = new VBox(tableView); + tableContainer.getStyleClass().add("matching-offers-table-container"); + noMatchingOffersBox = new HBox(25); VBox.setMargin(noMatchingOffersBox, new Insets(10, 0, 0, 0)); - root.getChildren().addAll(Spacer.fillVBox(), headlineLabel, subtitleLabel, tableView, noMatchingOffersBox, Spacer.fillVBox()); + root.getChildren().addAll(Spacer.fillVBox(), headlineLabel, subtitleLabel, tableContainer, noMatchingOffersBox, Spacer.fillVBox()); } @Override @@ -232,7 +234,7 @@ public TableCell call(TableColumn column { userName.setId("chat-user-name"); - int size = 20; + int size = 30; catIcon.setFitWidth(size); catIcon.setFitHeight(size); StackPane catIconWithRing = ImageUtil.addRingToNode(catIcon, size, 1.5, "-bisq-dark-grey-50"); @@ -269,6 +271,7 @@ public TableCell call(TableColumn column @Override public void updateItem(final ListItem item, boolean empty) { super.updateItem(item, empty); + if (item != null && !empty) { getTableRow().setOnMouseClicked(e -> reputationScoreDisplay.useWhiteAcceptStar()); diff --git a/apps/desktop/desktop/src/main/resources/css/bisq_easy.css b/apps/desktop/desktop/src/main/resources/css/bisq_easy.css index 06b08ecdd5..ad9df251b4 100644 --- a/apps/desktop/desktop/src/main/resources/css/bisq_easy.css +++ b/apps/desktop/desktop/src/main/resources/css/bisq_easy.css @@ -725,9 +725,13 @@ /******************************************************************************* - * TradeWizardTakeOfferView.tableView * + * TradeWizardTakeOfferView.tableView * ******************************************************************************/ +.matching-offers-table-container { + -fx-background-color: -bisq-dark-grey-20; +} + .bisq-easy-trade-wizard-select-trade.table-view { -fx-font-size: 1.15em; } @@ -736,42 +740,12 @@ -fx-pref-height: 40px; } -.bisq-easy-trade-wizard-select-offer.table-view .table-row-cell { - -fx-pref-height: 55px; -} - -.bisq-easy-trade-wizard-select-offer.table-view .table-row-cell .table-cell { - -fx-background-color: -bisq-dark-grey-40; -} - -.bisq-easy-trade-wizard-select-offer.table-view .table-row-cell:hover .table-cell { - -fx-background-color: -bisq-darker-grey; -} - -.bisq-easy-trade-wizard-select-offer.table-view .table-row-cell:selected .table-cell { - -fx-background-color: -bisq2-green; -} - -.bisq-easy-trade-wizard-select-offer.table-view .table-row-cell:empty { - -fx-border-color: derive(-bisq-dark-grey-40, -30%); -} - -.bisq-easy-trade-wizard-select-offer.table-view .table-row-cell:empty .table-cell { - -fx-background-color: derive(-bisq-dark-grey-40, -15%); -} - -.bisq-easy-trade-wizard-select-offer.table-view .table-row-cell:empty:hover .table-cell { - -fx-background-color: derive(-bisq-dark-grey-40, -15%); -} - -.bisq-easy-trade-wizard-select-offer.table-view .table-row-cell:selected .button.white-button .text { - -fx-fill: -bisq-black !important; - -fx-font-family: "IBM Plex Sans Medium" !important; +.bisq-easy-trade-wizard-select-offer.table-view .column-header .label { + -fx-alignment: center; } -.bisq-easy-trade-wizard-select-offer.table-view .table-row-cell:hover .button.outlined-button .text { - -fx-fill: -bisq2-green; - -fx-font-family: "IBM Plex Sans Medium"; +.bisq-easy-trade-wizard-select-offer.table-view .table-row-cell { + -fx-pref-height: 55px; } /* the text property from list view need to set to apply the color to the button label */ From 1b934cdf922506a99ecb891b5c361fd3c1a9c72f Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Mon, 18 Mar 2024 19:59:25 +0100 Subject: [PATCH 003/119] Remove selection column and use the marker as reference instead --- .../TradeWizardSelectOfferView.java | 96 ++++--------------- 1 file changed, 17 insertions(+), 79 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java index 9495e14b18..2ae3d03173 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java @@ -67,6 +67,7 @@ class TradeWizardSelectOfferView extends View { + if (item != null) { + controller.onSelectRow(item); + } + }); } @Override @@ -143,6 +150,8 @@ protected void onViewDetached() { if (browseOfferbookButton != null) { browseOfferbookButton.setOnAction(null); } + + tableViewSelectionPin.unsubscribe(); } private Pair getBoxPair(String title, String info) { @@ -171,6 +180,9 @@ private void maybeConfigTableView() { return; } + // Selection marker + tableView.getColumns().add(tableView.getSelectionMarkerColumn()); + // Maker String peer = model.getDirection() == Direction.BUY ? Res.get("offer.seller") : Res.get("offer.buyer"); tableView.getColumns().add(new BisqTableColumn.Builder() @@ -213,16 +225,9 @@ private void maybeConfigTableView() { .comparator(Comparator.comparing(ListItem::getBaseAmountAsLong)) .build()); - tableView.getColumns().add(new BisqTableColumn.Builder() - .minWidth(170) - .isSortable(false) - .setCellFactory(getSelectButtonCellFactory()) - .right() - .build()); isTableViewConfigured = true; } - private Callback, TableCell> getMakerCellFactory() { return new Callback<>() { @Override @@ -239,7 +244,7 @@ public TableCell call(TableColumn column catIcon.setFitHeight(size); StackPane catIconWithRing = ImageUtil.addRingToNode(catIcon, size, 1.5, "-bisq-dark-grey-50"); hBox = new HBox(10, catIconWithRing, userName); - hBox.setAlignment(Pos.CENTER_LEFT); + hBox.setAlignment(Pos.CENTER); } @Override @@ -272,6 +277,10 @@ public TableCell call(TableColumn column public void updateItem(final ListItem item, boolean empty) { super.updateItem(item, empty); + { + reputationScoreDisplay.setAlignment(Pos.CENTER); + } + if (item != null && !empty) { getTableRow().setOnMouseClicked(e -> reputationScoreDisplay.useWhiteAcceptStar()); @@ -298,77 +307,6 @@ public void updateItem(final ListItem item, boolean empty) { }; } - private Callback, TableCell> getSelectButtonCellFactory() { - return column -> new TableCell<>() { - private final Button button = new Button(Res.get("bisqEasy.tradeWizard.selectOffer.table.select")); - private TableRow tableRow; - private Subscription selectedItemPin; - - { - button.setMinWidth(160); - button.setMaxWidth(160); - } - - @Override - public void updateItem(final ListItem item, boolean empty) { - super.updateItem(item, empty); - - if (item != null && !empty) { - button.setOnAction(e -> { - tableView.getSelectionModel().select(item); - controller.onSelect(item); - }); - - tableRow = getTableRow(); - tableRow.setOnMouseEntered(e -> { - if (!tableRow.isSelected()) { - button.setVisible(true); - button.getStyleClass().remove("white-button"); - button.getStyleClass().add("outlined-button"); - } - }); - tableRow.setOnMouseExited(e -> { - button.getStyleClass().remove("outlined-button"); - if (!tableRow.isSelected()) { - button.setVisible(tableView.getSelectionModel().getSelectedItem() == null); - button.getStyleClass().remove("white-button"); - } - }); - tableRow.setOnMouseClicked(e -> controller.onSelectRow(item)); - - selectedItemPin = EasyBind.subscribe(tableView.getSelectionModel().selectedItemProperty(), - selectedItem -> { - if (item.equals(selectedItem)) { - button.setVisible(true); - button.getStyleClass().remove("outlined-button"); - button.getStyleClass().add("white-button"); - button.setText(Res.get("bisqEasy.tradeWizard.selectOffer.table.reviewTakeOffer")); - } else { - button.setVisible(selectedItem == null); - button.getStyleClass().remove("white-button"); - button.setText(Res.get("bisqEasy.tradeWizard.selectOffer.table.select")); - } - }); - - setGraphic(button); - } else { - button.setOnAction(null); - if (tableRow != null) { - tableRow.setOnMouseEntered(null); - tableRow.setOnMouseExited(null); - tableRow.setOnMouseClicked(null); - tableRow = null; - } - if (selectedItemPin != null) { - selectedItemPin.unsubscribe(); - selectedItemPin = null; - } - setGraphic(null); - } - } - }; - } - @ToString @EqualsAndHashCode @Getter From 66061143808769ad953c01390507aa15d97db4ba Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Mon, 18 Mar 2024 20:17:33 +0100 Subject: [PATCH 004/119] Show cursor hand when hovering in a row --- apps/desktop/desktop/src/main/resources/css/bisq_easy.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/desktop/desktop/src/main/resources/css/bisq_easy.css b/apps/desktop/desktop/src/main/resources/css/bisq_easy.css index ad9df251b4..82baea450d 100644 --- a/apps/desktop/desktop/src/main/resources/css/bisq_easy.css +++ b/apps/desktop/desktop/src/main/resources/css/bisq_easy.css @@ -744,6 +744,10 @@ -fx-alignment: center; } +.bisq-easy-trade-wizard-select-offer.table-view .table-row-cell:hover { + -fx-cursor: hand; +} + .bisq-easy-trade-wizard-select-offer.table-view .table-row-cell { -fx-pref-height: 55px; } From ae58304bf8db1be42dc6b92b2c08504d4cd05e78 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Mon, 18 Mar 2024 20:59:08 +0100 Subject: [PATCH 005/119] Use reputation cell to mark selected row --- .../TradeWizardSelectOfferView.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java index 2ae3d03173..1aa37ada03 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java @@ -67,7 +67,6 @@ class TradeWizardSelectOfferView extends View { - if (item != null) { - controller.onSelectRow(item); - } - }); } @Override @@ -150,8 +143,6 @@ protected void onViewDetached() { if (browseOfferbookButton != null) { browseOfferbookButton.setOnAction(null); } - - tableViewSelectionPin.unsubscribe(); } private Pair getBoxPair(String title, String info) { @@ -272,17 +263,22 @@ public TableCell call(TableColumn column return new TableCell<>() { private final ReputationScoreDisplay reputationScoreDisplay = new ReputationScoreDisplay(); private Subscription selectedItemPin; + private TableRow tableRow; + + { + reputationScoreDisplay.setAlignment(Pos.CENTER); + } @Override public void updateItem(final ListItem item, boolean empty) { super.updateItem(item, empty); - { - reputationScoreDisplay.setAlignment(Pos.CENTER); - } - if (item != null && !empty) { - getTableRow().setOnMouseClicked(e -> reputationScoreDisplay.useWhiteAcceptStar()); + tableRow = getTableRow(); + tableRow.setOnMouseClicked(e -> { + reputationScoreDisplay.useWhiteAcceptStar(); + controller.onSelectRow(item); + }); selectedItemPin = EasyBind.subscribe(tableView.getSelectionModel().selectedItemProperty(), selectedItem -> { @@ -296,10 +292,14 @@ public void updateItem(final ListItem item, boolean empty) { reputationScoreDisplay.setReputationScore(item.getReputationScore()); setGraphic(reputationScoreDisplay); } else { - setGraphic(null); + if (tableRow != null) { + tableRow.setOnMouseClicked(null); + tableRow = null; + } if (selectedItemPin != null) { selectedItemPin.unsubscribe(); } + setGraphic(null); } } }; From e3f5c30db484103548b5f42bf520386ea75dc775 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Mon, 18 Mar 2024 21:23:03 +0100 Subject: [PATCH 006/119] Improve styles of matching offers table --- .../select_offer/TradeWizardSelectOfferView.java | 2 +- .../desktop/src/main/resources/css/bisq_easy.css | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java index 1aa37ada03..dc3b3c23a4 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferView.java @@ -86,7 +86,7 @@ class TradeWizardSelectOfferView extends View Date: Mon, 18 Mar 2024 23:21:34 +0100 Subject: [PATCH 007/119] Improve visibility of messages with scroll down indicator * Apply a gradient buffer when scroll down icon is shown to make a smooth disappearance and improve experience. * Change icons to better fit the scroll down icon and number of messages indicator in the chat window. Resolves #1776 --- .../chatMessages/ChatMessagesListView.java | 17 +++++++++++++---- .../desktop/src/main/resources/css/chat.css | 7 +++++++ .../images/icons/scroll-down-green.png | Bin 300 -> 335 bytes .../images/icons/scroll-down-green@2x.png | Bin 470 -> 456 bytes .../images/icons/scroll-down-grey.png | Bin 336 -> 330 bytes .../images/icons/scroll-down-grey@2x.png | Bin 494 -> 441 bytes .../images/icons/scroll-down-white.png | Bin 252 -> 265 bytes .../images/icons/scroll-down-white@2x.png | Bin 449 -> 419 bytes 8 files changed, 20 insertions(+), 4 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesListView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesListView.java index c7bab04871..d534bd231e 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesListView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesListView.java @@ -755,6 +755,7 @@ private static class View extends bisq.desktop.common.view.View scrollBar = Optional.empty(); private Subscription hasUnreadMessagesPin, showScrolledDownButtonPin; private Timeline fadeInScrollDownBadgeTimeline; @@ -774,22 +775,25 @@ private View(Model model, Controller controller) { listView.setSelectionModel(new NoSelectionModel<>()); VBox.setVgrow(listView, Priority.ALWAYS); - scrollDownImageView = new ImageView(); + scrollDownBackground = new Pane(); + scrollDownBackground.getStyleClass().add("scroll-down-bg"); + scrollDownImageView = new ImageView(); scrollDownBadge = new Badge(scrollDownImageView); scrollDownBadge.setMaxSize(25, 25); scrollDownBadge.getStyleClass().add("chat-messages-badge"); scrollDownBadge.setPosition(Pos.BOTTOM_RIGHT); - scrollDownBadge.setBadgeInsets(new Insets(20, 10, 0, 50)); + scrollDownBadge.setBadgeInsets(new Insets(20, 10, -2, 55)); scrollDownBadge.setCursor(Cursor.HAND); scrollDownTooltip = new BisqTooltip(Res.get("chat.listView.scrollDown")); Tooltip.install(scrollDownBadge, scrollDownTooltip); StackPane.setAlignment(scrollDownBadge, Pos.BOTTOM_CENTER); - StackPane.setMargin(scrollDownBadge, new Insets(0, 0, 10, 0)); + StackPane.setAlignment(scrollDownBackground, Pos.BOTTOM_CENTER); + StackPane.setMargin(scrollDownBackground, new Insets(0, 15, 0, 0)); root.setAlignment(Pos.CENTER); - root.getChildren().addAll(listView, scrollDownBadge); + root.getChildren().addAll(listView, scrollDownBackground, scrollDownBadge); } @Override @@ -809,6 +813,9 @@ protected void onViewAttached() { } }); + scrollDownBackground.visibleProperty().bind(model.getShowScrolledDownButton()); + scrollDownBackground.managedProperty().bind(model.getShowScrolledDownButton()); + scrollDownBadge.textProperty().bind(model.numUnReadMessages); scrollDownBadge.setOpacity(0); @@ -853,6 +860,8 @@ protected void onViewAttached() { protected void onViewDetached() { scrollBar.ifPresent(scrollbar -> scrollbar.valueProperty().unbindBidirectional(model.getScrollValue())); model.scrollBarVisible.unbind(); + scrollDownBackground.visibleProperty().unbind(); + scrollDownBackground.managedProperty().unbind(); scrollDownBadge.textProperty().unbind(); hasUnreadMessagesPin.unsubscribe(); showScrolledDownButtonPin.unsubscribe(); diff --git a/apps/desktop/desktop/src/main/resources/css/chat.css b/apps/desktop/desktop/src/main/resources/css/chat.css index 3ef76ff4ae..19cab22a07 100644 --- a/apps/desktop/desktop/src/main/resources/css/chat.css +++ b/apps/desktop/desktop/src/main/resources/css/chat.css @@ -473,6 +473,13 @@ -fx-underline: true; } +.scroll-down-bg { + -fx-min-height: 38; + -fx-max-height: 38; + -fx-pref-height: 38; + -fx-background-color: linear-gradient(to top, rgba(28, 28, 28, 1) 0%, rgba(28, 28, 28, 0.9) 40%, rgba(28, 28, 28, 0) 100%); /* -bisq-dark-grey-20 */ +} + .chat-messages-badge.bisq-badge .badge-pane { -fx-background-color: transparent; } diff --git a/apps/desktop/desktop/src/main/resources/images/icons/scroll-down-green.png b/apps/desktop/desktop/src/main/resources/images/icons/scroll-down-green.png index 96286abddefa31547da47f6007611ed6da4e1744..ed68c4b45e53da4f47cd3f88e6cf7b2d2c4a2508 100644 GIT binary patch delta 271 zcmV+q0r38;0?z`FNq_N4L_t(|+G1cB1*2dTjDk@x3P!=80AcGq5`b8M4)zEDaR7+T z2xY7X;s-z+xGHGJ1sYju0JTQ}h#i@LL_SmwByb+6pnwLJg3LS*wF3n58KDyWKnmHy z4M3a-^!*1)gBD252jT!E;R!%<3mK6V1wa+S;+mqMM+;gI_OPi3^8DvVtC8(4wnl#A66aK~E@X(E$@Ki%AK3VuF@v9}^SwP&QFP zOP0lWf}Vlcpe5U4sO1XKcr-whx&V#;2gI7iNM;^I4tk=3mYT(20Ia|#2LJ>Z0QseN VNC+0Bod5s;00>D%PDHLkV1l>hXOsW{ delta 235 zcmVqP*g6jZdfUYaWGDdX^FX`-hznN* z?f5`L3;TgM0Ei8lfW&+t1_>qr@qVBJ0~%NgGIKxF4zL4278e5X1*jSWsO1S%v@`)~ zDa=D4yBZnMf&tmW1(XCmAu}1VxUL@>95B}<0u^2$BWUL%`DF3~)WQWYHH_qFA|hxB zI-Zf7V4{W|Mryd8oKRw?7x002ovPDHLkV1m!aR#5-| diff --git a/apps/desktop/desktop/src/main/resources/images/icons/scroll-down-green@2x.png b/apps/desktop/desktop/src/main/resources/images/icons/scroll-down-green@2x.png index db0bd05e4abfef2a16837beada149c785572c0c5..f3a9cabc17018cb0608bf139f354eccdd81f1246 100644 GIT binary patch delta 393 zcmcb{e1ds`XML8Zi(^Q|tv55y<~BPBxRnPvdM#*K(kxTkQ+wjB>=I@VR=tCkzY}WC z=)O}ZaRADl@k-V{6t(3+s>FrwORg$?RNgmt`-TTH3>aX8!;4KH%VPExOfQa>H;X?j zbf|Dy&+bXLrx}M{Kd|TV#ND%Izn8A(tesbRBmCXBNiV10-mL$m`Hg;R`{aMK{Wa2L z%C4W9;#uJ?q{Ba_dfwzm|3&J!WHx;j{r4n{mG8snUDJNrvbWY-9s2cX?uw-ctB;%7 z@3_>O*t+=LTr0I*l}D9D_Ejg2o|o(468u3OmkHbW8G)8<|#ojKblD?@`5^;$syK zS;cN_QSlUo2Y)wgN%1KPCk4Kx;!_nKth5Eib10nLu%_ZU70wb^Qt{jhCo8R?xP-#l z8qTS>q{1SBrzkG1a5klLD6XNfSi=d5I}L?Z0uNN&X(%kFbb{h43ad3dQgJbbZxXmi zaZ!cUlpd(KxWZO`&6KP;00000000006Z{G=0P@xa1rSBe^a=m~002ovPDHLkV1gyI Bw4(q3 diff --git a/apps/desktop/desktop/src/main/resources/images/icons/scroll-down-grey.png b/apps/desktop/desktop/src/main/resources/images/icons/scroll-down-grey.png index fc07a54f498cde9271eccd0e296b88f9039dd697..175ba8b65af35c708e54c66a2d48b4d6e0db490e 100644 GIT binary patch delta 266 zcmV+l0rmdS0?GoANq_7~L_t(|+U%6!4T3Nbh1*WR2#f$DxIx`OCqOq~1Kq$8bQ3xP zBQS#R%#k#W*0YH5$GzlexgPh@uYfTsl}e>jss2KJ-|rCB4}2H$$5;E-Iu%iwH~6KS4`9+u8K-a=m!42z?H1T5B^+K0^NvZ7P>B7NeJ-74KMr zp64R8MaCKQMvAuBn9XZG$0k_{eEy@fmo5B8WAq}l-~0{w`G;Sky8r_K=W=-*X^~B^ Q00000NkvXXt^-0~g2-!n(*OVf delta 272 zcmV+r0q_3G0?-1GNq_Q5L_t(|+G1cB1*2dTi~>qPLqmfAz3pNIG8BOHc_7{Z#Dzfq z2O3(~55xgLY{<^wTEFae170~Hw1z*3Nz`=NG#9RRYp5Qr~8)fhl6PoScu2~bO6 z9s=3b$cPpU$QCZ3B?KH!grHpx#0p4`4+M(uKvu^{PB2kJ4RB!fG7YEU;y;C Wg7l|zt|I^d00{s|MNUMnLSTZ_-){&4Nj} z!q+d^K$Z1$;e`K_md!{yoFBjNfeZr%FmQZPJK_1yS*v?aPwQW;+45M1dmWdawOw5e z)8~H6a8>d6aG_sjpEgLZ^PBYYWPR#W^&+*mttJ*L*Wa1ZZ}Dd1v`oc^vYs1sKHR@O zL*#!@g``F5^YkBQo(ftVT>m=g+II<&edaA+Czmgr#rC_+jz7#azxeHy4HwR8%PstH zo_9u2Nzwy78C%OKPQs_T*v;iM-7ii$w3aD4$mZ&U$!%`VT6Ziz2y}f<6p!4&arU5U z5%;XjZ@$yg91qKF+H`KqJN_p^AN$*8`8T9A?mlx#;QWszjkjASI0oK7s>*-IDcJmo z+Oq8jCU&jP53c5wS-Im?x5xWNWzq2Jt0CMn0Wp7*o3b9vdNg(R1^4>RdsrkGOyj;S T)5&Vy3koGqS3j3^P6+@L_t(|+U%G=PQx%1hi!U+j*RpMJwmua8QH?d2sg+LdIUPc zR5n(RY{?NiGSVCHEICrJ>Nauw+?VM0Nw2Q_C(^I)tA{8B0000000000008DhRaJL$ z&$Q?)Y-xBeisERD`JPGG7aG1Olcr3@&S|%>p<&aW;jo68M7RtO{pSx~Jmo3Mfl?4^=wN0RR9100000h~Q6v Z0RS8YA^z)!G>-rP002ovPDHLkV1jyL%WePw diff --git a/apps/desktop/desktop/src/main/resources/images/icons/scroll-down-white.png b/apps/desktop/desktop/src/main/resources/images/icons/scroll-down-white.png index 26307a9ec1163865314ab95258205ba73c671285..aa2d438f6d0478b39730df5f52b6a5a0121c392b 100644 GIT binary patch delta 200 zcmV;(05|{q0f_>TNq?(JL_t(|+G1cB1*2dTjDk@x3P!=80Kb0yN&sR3I@lus!~rl) z01*EN;`=~sKtoFnpmu=lasUbJhjL)JfCiQppxLn=Cee>BL5-k?2JLz@v-{B%fr9)! zk^+i?9xZ79L(LArrC0%o&r=%oSb`R0rUEe<$qIU+f|dwFNI?pEl7f~9%ZUnlvV)e~ zpr=aEk{R^W30g9Po_aw`V$k<9&=Wv|p6s9n2rvK;w%Fj(&ioz#0000r)uV23e=xrAhkf8vi?*r-iv~&Q- zY>*vLyA(hI`+@jB6rTrT0~%Or05uzC&w6Zv?}0dhik2oo&BXK^EEtd^|5FW|iFaZAE;y)R_xb6S|002ovPDHLkV1iRbP$2*S diff --git a/apps/desktop/desktop/src/main/resources/images/icons/scroll-down-white@2x.png b/apps/desktop/desktop/src/main/resources/images/icons/scroll-down-white@2x.png index a08abc628196c625bb921ea2e6cf0ccc78624c61..270087c99953af3a54786a3b1c0fb7b814c2a761 100644 GIT binary patch delta 355 zcmV-p0i6E91ET|wNq* zx2UE)k|Kw0hmX9GvTg$liaoY!%DyJDp4qaK$lzkHJ@v#*XMfbw*~BQtTM7@=bK2p! zJMtPhN2PXVg5s6JXj0@ba7cnY#xyWO@!4bV-00#JpP=yg0=&ve^smCpCr7g4%El&5 zaV_>qQC$1Q-%|0-{#*A|7GamJL5O=qs?<#E6JqUIFk%m|)*wyWkIZ84?4#I3)tFoj z_w|_KN)?v6ibsx(^sM4md=j3zQL*ZyzgRv33;@s$2XeVuK$8Ff002ovPDHLkV1l0` Bs~P|R delta 385 zcmV-{0e=3Y1Hl83Nq=KWL_t(|+U%IoZG$inhD{#)oDtd$IzreW-N4--*&rJ*0@)zl z#ND9Xpnc}0Jxhm#C`OQS_)Py#`tgH7x`R(AVPgOQ0000000000z?Nv5=HcJ;L>m?5 zn{wJvvnTH9(%(4pMw=UDpK+sm?xEY$A?y|W$vvgt;0NW|T7UbNS+h^H+$epOzbKD8 zy10R64L{&>Qk-k`1I-%l(X}@^%4bi3Y!r_u+)e1s{ZfJAM;>}-UDPSewZ-c8(YpCf z)guq9n&O=)97{dPOzn!_rSL>m)G7Wag(tpAHHt5$uy(~o6xOP^sKVM5UrpiEiYHMx zmEuViUR!ZB3VKT`u2Nws#Z@c3wBjigE~FHvrHcG#2VQwSM)t$c`q^50UAneHQgK>p z%3K}83CoMrNy|>EVJ^6&%=t>;-ZHmirF*8hz9}p?#q~~Mfhk_+d>lMiac2Mk00000 f001cAM}PqU8kQ??mu|l;00000NkvXXu0mjfU@Et6 From a5fe4a9ba75ba1323c883d8de5b0e1649102e405 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Tue, 19 Mar 2024 19:55:50 +0100 Subject: [PATCH 008/119] Hide horizontal scrollbar in private chats list --- .../bisq/desktop/main/content/chat/priv/PrivateChatsView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/priv/PrivateChatsView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/priv/PrivateChatsView.java index 01fb322a76..1e77507561 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/priv/PrivateChatsView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/priv/PrivateChatsView.java @@ -170,6 +170,7 @@ private void addOpenChatsSelectionList() { tableView = new BisqTableView<>(getModel().getSortedList()); tableView.getStyleClass().add("private-chats-selection-list"); tableView.allowVerticalScrollbar(); + tableView.hideHorizontalScrollbar(); configTableView(); VBox.setVgrow(tableView, Priority.ALWAYS); From 748a1b74ca77705c8513ede6ba00d9f5064538f8 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Tue, 19 Mar 2024 20:25:11 +0100 Subject: [PATCH 009/119] Set height correctly for favourite markets --- .../bisq_easy/offerbook/BisqEasyOfferbookController.java | 2 +- .../main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java | 2 ++ apps/desktop/desktop/src/main/resources/css/bisq_easy.css | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookController.java index f5fe699c02..a390eda7fe 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookController.java @@ -320,7 +320,7 @@ private void updateFilteredMarketChannelItems() { private void updateFavouriteMarketChannelItems() { model.getFavouriteMarketChannelItems().setPredicate(item -> model.getFavouriteMarkets().contains(item.getMarket())); - double padding = 15; + double padding = 21; double tableViewHeight = (model.getFavouriteMarketChannelItems().size() * MARKET_SELECTION_LIST_CELL_HEIGHT) + padding; model.getFavouritesTableViewHeight().set(tableViewHeight); } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java index aa6e727a86..4ba077eb78 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java @@ -188,7 +188,9 @@ private void updateTableViewSelection(MarketChannelItem selectedItem) { } private void updateFavouritesTableViewHeight(double height) { + favouritesTableView.setMinHeight(height); favouritesTableView.setPrefHeight(height); + favouritesTableView.setMaxHeight(height); } private void setOfferDirectionOrOwnerFilter(DropdownFilterMenuItem filterMenuItem) { diff --git a/apps/desktop/desktop/src/main/resources/css/bisq_easy.css b/apps/desktop/desktop/src/main/resources/css/bisq_easy.css index 06b08ecdd5..a71a71e47a 100644 --- a/apps/desktop/desktop/src/main/resources/css/bisq_easy.css +++ b/apps/desktop/desktop/src/main/resources/css/bisq_easy.css @@ -285,6 +285,7 @@ .favourites-list { -fx-padding: 0 0 10 0; -fx-border-width: 0 0 1 0; + -fx-border-insets: 0 0 10 0; -fx-border-color: -bisq-dark-grey-50; } From 884633a73f5ad8403a6f211d898428dc3752c2e6 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Tue, 19 Mar 2024 21:03:25 +0100 Subject: [PATCH 010/119] Improve styles in market selection column --- .../BisqEasyOfferbookController.java | 1 + .../offerbook/BisqEasyOfferbookModel.java | 2 +- .../offerbook/BisqEasyOfferbookView.java | 20 +++++++++++++------ .../src/main/resources/css/bisq_easy.css | 16 ++++++++++----- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookController.java index a390eda7fe..f4372a7cb7 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookController.java @@ -149,6 +149,7 @@ public void onActivate() { settingsService.setCookie(CookieKey.MARKETS_FILTER, model.getSelectedMarketsFilter().get().name()); updateFilteredMarketChannelItems(); } + model.getShouldShowAppliedFilters().set(filter == Filters.Markets.WITH_OFFERS); }); marketPriceByCurrencyMapPin = marketPriceService.getMarketPriceByCurrencyMap().addObserver(() -> { diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookModel.java index a190ecb4e7..5751562de0 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookModel.java @@ -38,7 +38,7 @@ public final class BisqEasyOfferbookModel extends ChatModel { private final BooleanProperty offerOnly = new SimpleBooleanProperty(); private final BooleanProperty isTradeChannelVisible = new SimpleBooleanProperty(); - private final BooleanProperty showFilterOverlay = new SimpleBooleanProperty(); // TODO: remove this + private final BooleanProperty shouldShowAppliedFilters = new SimpleBooleanProperty(); private final ObservableList marketChannelItems = FXCollections.observableArrayList(p -> new Observable[]{p.getNumOffers()}); private final FilteredList filteredMarketChannelItems = new FilteredList<>(marketChannelItems); private final SortedList sortedMarketChannelItems = new SortedList<>(filteredMarketChannelItems); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java index 4ba077eb78..c163136f32 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java @@ -57,7 +57,8 @@ public final class BisqEasyOfferbookView extends ChatView { if (item != null) { getController().onSelectMarketChannelItem(item); @@ -150,9 +150,10 @@ protected void onViewAttached() { selectedPeerReputationFilterPin = EasyBind.subscribe(getModel().getSelectedPeerReputationFilter(), filter -> updateSelectedFilterInDropdownMenu(filter, filterOffersByPeerReputationMenu)); selectedMarketSortTypePin = EasyBind.subscribe(getModel().getSelectedMarketSortType(), this::updateMarketSortType); - favouritesTableViewHeightPin = EasyBind.subscribe(getModel().getFavouritesTableViewHeight(), height -> updateFavouritesTableViewHeight(height.doubleValue())); + shouldShowAppliedFiltersPin = EasyBind.subscribe(getModel().getShouldShowAppliedFilters(), + this::updateAppliedFiltersSectionStyles); sortByMostOffers.setOnAction(e -> getController().onSortMarkets(MarketSortType.NUM_OFFERS)); sortByNameAZ.setOnAction(e -> getController().onSortMarkets(MarketSortType.ASC)); @@ -223,6 +224,7 @@ protected void onViewDetached() { selectedPeerReputationFilterPin.unsubscribe(); selectedMarketSortTypePin.unsubscribe(); favouritesTableViewHeightPin.unsubscribe(); + shouldShowAppliedFiltersPin.unsubscribe(); sortByMostOffers.setOnAction(null); sortByNameAZ.setOnAction(null); @@ -273,9 +275,8 @@ private void addMarketSelectionList() { subheader.getStyleClass().add("market-selection-subheader"); setUpWithOffersFiltersDisplayHint(); - HBox appliedFiltersSection = new HBox(withOffersDisplayHint); + appliedFiltersSection = new HBox(withOffersDisplayHint); appliedFiltersSection.setAlignment(Pos.CENTER_RIGHT); - appliedFiltersSection.getStyleClass().add("market-selection-applied-filters"); HBox.setHgrow(appliedFiltersSection, Priority.ALWAYS); favouritesTableView = new BisqTableView<>(getModel().getFavouriteMarketChannelItems()); @@ -502,6 +503,13 @@ private void updateSelectedFilterInDropdownMenu(T selectedFilter, DropdownMe }); } + private void updateAppliedFiltersSectionStyles(boolean shouldShowAppliedFilters) { + appliedFiltersSection.getStyleClass().clear(); + appliedFiltersSection.getStyleClass().add(shouldShowAppliedFilters + ? "market-selection-show-applied-filters" + : "market-selection-no-filters"); + } + private String createPeerReputationLabel(Filters.PeerReputation filter, String label) { switch (filter) { case AT_LEAST_FOUR_STARS: diff --git a/apps/desktop/desktop/src/main/resources/css/bisq_easy.css b/apps/desktop/desktop/src/main/resources/css/bisq_easy.css index a71a71e47a..71a6e27f99 100644 --- a/apps/desktop/desktop/src/main/resources/css/bisq_easy.css +++ b/apps/desktop/desktop/src/main/resources/css/bisq_easy.css @@ -217,12 +217,18 @@ -fx-text-fill: -fx-light-text-color; } -.market-selection-applied-filters { +.market-selection-no-filters { + -fx-padding: 10 0 0 0; +} + +.market-selection-show-applied-filters { + -fx-min-height: 35; -fx-pref-height: 35; + -fx-max-height: 35; -fx-padding: 5; } -.market-selection-applied-filters .filter-display-hint { +.market-selection-show-applied-filters .filter-display-hint { -fx-background-color: transparent; -fx-border-color: -bisq-mid-grey-20; -fx-border-width: 1; @@ -233,15 +239,15 @@ -fx-padding: 0 0 0 5; } -.market-selection-applied-filters .filter-display-hint:hover { +.market-selection-show-applied-filters .filter-display-hint:hover { -fx-border-color: -bisq-white; } -.market-selection-applied-filters .filter-display-hint:hover .label { +.market-selection-show-applied-filters .filter-display-hint:hover .label { -fx-text-fill: -bisq-white; } -.market-selection-applied-filters .filter-display-hint .label { +.market-selection-show-applied-filters .filter-display-hint .label { -fx-text-fill: -fx-mid-text-color; } From 5fb3e703f9cfb79be46eb8505d8ee885bfdf9d0c Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Tue, 19 Mar 2024 21:27:59 +0100 Subject: [PATCH 011/119] Add only favourites filter to market selection list --- .../BisqEasyOfferbookController.java | 2 +- .../offerbook/BisqEasyOfferbookView.java | 84 +++++++++++++------ i18n/src/main/resources/bisq_easy.properties | 2 +- 3 files changed, 62 insertions(+), 26 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookController.java index f4372a7cb7..a63165eead 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookController.java @@ -149,7 +149,7 @@ public void onActivate() { settingsService.setCookie(CookieKey.MARKETS_FILTER, model.getSelectedMarketsFilter().get().name()); updateFilteredMarketChannelItems(); } - model.getShouldShowAppliedFilters().set(filter == Filters.Markets.WITH_OFFERS); + model.getShouldShowAppliedFilters().set(filter == Filters.Markets.WITH_OFFERS || filter == Filters.Markets.FAVOURITES); }); marketPriceByCurrencyMapPin = marketPriceService.getMarketPriceByCurrencyMap().addObserver(() -> { diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java index c163136f32..0048d7ad33 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java @@ -62,15 +62,16 @@ public final class BisqEasyOfferbookView extends ChatView filterShowAll, filterWithOffers; + private DropdownFilterMenuItem filterShowAll, filterWithOffers, filterFavourites; private DropdownFilterMenuItem>> allOffers, myOffers, buyOffers, sellOffers, allReputations, fiveStars, atLeastFourStars, atLeastThreeStars, atLeastTwoStars, atLeastOneStar; private DropdownTitleMenuItem atLeastTitle; private CheckBox hideUserMessagesCheckbox; - private Label channelHeaderIcon, marketPrice, removeWithOffersFilter; - private HBox appliedFiltersSection, withOffersDisplayHint; - private ImageView defaultCloseIcon, activeCloseIcon; + private Label channelHeaderIcon, marketPrice, removeWithOffersFilter, removeFavouritesFilter; + private HBox appliedFiltersSection, withOffersDisplayHint, onlyFavouritesDisplayHint; + private ImageView withOffersRemoveFilterDefaultIcon, withOffersRemoveFilterActiveIcon, + favouritesRemoveFilterDefaultIcon, favouritesRemoveFilterActiveIcon; public BisqEasyOfferbookView(BisqEasyOfferbookModel model, BisqEasyOfferbookController controller, @@ -124,6 +125,8 @@ protected void onViewAttached() { marketPrice.textProperty().bind(getModel().getMarketPrice()); withOffersDisplayHint.visibleProperty().bind(getModel().getSelectedMarketsFilter().isEqualTo(Filters.Markets.WITH_OFFERS)); withOffersDisplayHint.managedProperty().bind(getModel().getSelectedMarketsFilter().isEqualTo(Filters.Markets.WITH_OFFERS)); + onlyFavouritesDisplayHint.visibleProperty().bind(getModel().getSelectedMarketsFilter().isEqualTo(Filters.Markets.FAVOURITES)); + onlyFavouritesDisplayHint.managedProperty().bind(getModel().getSelectedMarketsFilter().isEqualTo(Filters.Markets.FAVOURITES)); favouritesTableView.visibleProperty().bind(Bindings.isNotEmpty(getModel().getFavouriteMarketChannelItems())); favouritesTableView.managedProperty().bind(Bindings.isNotEmpty(getModel().getFavouriteMarketChannelItems())); @@ -161,6 +164,7 @@ protected void onViewAttached() { filterWithOffers.setOnAction(e -> getModel().getSelectedMarketsFilter().set(Filters.Markets.WITH_OFFERS)); filterShowAll.setOnAction(e -> getModel().getSelectedMarketsFilter().set(Filters.Markets.ALL)); + filterFavourites.setOnAction(e -> getModel().getSelectedMarketsFilter().set(Filters.Markets.FAVOURITES)); allOffers.setOnAction(e -> setOfferDirectionOrOwnerFilter(allOffers)); myOffers.setOnAction(e -> setOfferDirectionOrOwnerFilter(myOffers)); @@ -177,8 +181,12 @@ protected void onViewAttached() { createOfferButton.setOnAction(e -> getController().onCreateOffer()); removeWithOffersFilter.setOnMouseClicked(e -> getModel().getSelectedMarketsFilter().set(Filters.Markets.ALL)); - withOffersDisplayHint.setOnMouseEntered(e -> removeWithOffersFilter.setGraphic(activeCloseIcon)); - withOffersDisplayHint.setOnMouseExited(e -> removeWithOffersFilter.setGraphic(defaultCloseIcon)); + withOffersDisplayHint.setOnMouseEntered(e -> removeWithOffersFilter.setGraphic(withOffersRemoveFilterActiveIcon)); + withOffersDisplayHint.setOnMouseExited(e -> removeWithOffersFilter.setGraphic(withOffersRemoveFilterDefaultIcon)); + + removeFavouritesFilter.setOnMouseClicked(e -> getModel().getSelectedMarketsFilter().set(Filters.Markets.ALL)); + onlyFavouritesDisplayHint.setOnMouseEntered(e -> removeFavouritesFilter.setGraphic(favouritesRemoveFilterActiveIcon)); + onlyFavouritesDisplayHint.setOnMouseExited(e -> removeFavouritesFilter.setGraphic(favouritesRemoveFilterDefaultIcon)); } private void updateTableViewSelection(MarketChannelItem selectedItem) { @@ -211,6 +219,8 @@ protected void onViewDetached() { marketPrice.textProperty().unbind(); withOffersDisplayHint.visibleProperty().unbind(); withOffersDisplayHint.managedProperty().unbind(); + onlyFavouritesDisplayHint.visibleProperty().unbind(); + onlyFavouritesDisplayHint.managedProperty().unbind(); favouritesTableView.visibleProperty().unbind(); favouritesTableView.managedProperty().unbind(); @@ -231,6 +241,7 @@ protected void onViewDetached() { sortByNameZA.setOnAction(null); filterWithOffers.setOnAction(null); filterShowAll.setOnAction(null); + filterFavourites.setOnAction(null); allOffers.setOnAction(null); myOffers.setOnAction(null); buyOffers.setOnAction(null); @@ -247,6 +258,10 @@ protected void onViewDetached() { withOffersDisplayHint.setOnMouseEntered(null); withOffersDisplayHint.setOnMouseExited(null); + removeFavouritesFilter.setOnMouseClicked(null); + onlyFavouritesDisplayHint.setOnMouseEntered(null); + onlyFavouritesDisplayHint.setOnMouseExited(null); + getModel().getFavouriteMarketChannelItems().removeListener(listChangeListener); } @@ -274,8 +289,28 @@ private void addMarketSelectionList() { subheader.setAlignment(Pos.CENTER); subheader.getStyleClass().add("market-selection-subheader"); - setUpWithOffersFiltersDisplayHint(); - appliedFiltersSection = new HBox(withOffersDisplayHint); + // TODO: Introduce new icons with proper scale + withOffersRemoveFilterDefaultIcon = ImageUtil.getImageViewById("close"); + withOffersRemoveFilterDefaultIcon.setScaleX(0.4); + withOffersRemoveFilterDefaultIcon.setScaleY(0.4); + withOffersRemoveFilterActiveIcon = ImageUtil.getImageViewById("close-white"); + withOffersRemoveFilterActiveIcon.setScaleX(0.4); + withOffersRemoveFilterActiveIcon.setScaleY(0.4); + removeWithOffersFilter = createAndGetRemoveFilterLabel(withOffersRemoveFilterDefaultIcon); + withOffersDisplayHint = createAndGetDisplayHintHBox( + Res.get("bisqEasy.offerbook.dropdownMenu.sortAndFilterMarkets.withOffers"), removeWithOffersFilter); + + favouritesRemoveFilterDefaultIcon = ImageUtil.getImageViewById("close"); + favouritesRemoveFilterDefaultIcon.setScaleX(0.4); + favouritesRemoveFilterDefaultIcon.setScaleY(0.4); + favouritesRemoveFilterActiveIcon = ImageUtil.getImageViewById("close-white"); + favouritesRemoveFilterActiveIcon.setScaleX(0.4); + favouritesRemoveFilterActiveIcon.setScaleY(0.4); + removeFavouritesFilter = createAndGetRemoveFilterLabel(favouritesRemoveFilterDefaultIcon); + onlyFavouritesDisplayHint = createAndGetDisplayHintHBox( + Res.get("bisqEasy.offerbook.dropdownMenu.sortAndFilterMarkets.favourites"), removeFavouritesFilter); + + appliedFiltersSection = new HBox(withOffersDisplayHint, onlyFavouritesDisplayHint); appliedFiltersSection.setAlignment(Pos.CENTER_RIGHT); HBox.setHgrow(appliedFiltersSection, Priority.ALWAYS); @@ -302,21 +337,20 @@ private void addMarketSelectionList() { marketSelectionList.getStyleClass().add("chat-container"); } - private void setUpWithOffersFiltersDisplayHint() { - Label withOffersLabel = new Label(Res.get("bisqEasy.offerbook.dropdownMenu.sortAndFilterMarkets.withOffers")); - withOffersLabel.getStyleClass().add("small-text"); - removeWithOffersFilter = new Label(); - defaultCloseIcon = ImageUtil.getImageViewById("close"); - defaultCloseIcon.setScaleX(0.4); - defaultCloseIcon.setScaleY(0.4); - activeCloseIcon = ImageUtil.getImageViewById("close-white"); - activeCloseIcon.setScaleX(0.4); - activeCloseIcon.setScaleY(0.4); - removeWithOffersFilter.setGraphic(defaultCloseIcon); - removeWithOffersFilter.setCursor(Cursor.HAND); - withOffersDisplayHint = new HBox(withOffersLabel, removeWithOffersFilter); - withOffersDisplayHint.setAlignment(Pos.CENTER); - withOffersDisplayHint.getStyleClass().add("filter-display-hint"); + private Label createAndGetRemoveFilterLabel(ImageView defaultCloseIcon) { + Label removeFilterLabel = new Label(); + removeFilterLabel.setGraphic(defaultCloseIcon); + removeFilterLabel.setCursor(Cursor.HAND); + return removeFilterLabel; + } + + private HBox createAndGetDisplayHintHBox(String labelText, Label removeFilter) { + Label label = new Label(labelText); + label.getStyleClass().add("small-text"); + HBox displayHintHBox = new HBox(label, removeFilter); + displayHintHBox.setAlignment(Pos.CENTER); + displayHintHBox.getStyleClass().add("filter-display-hint"); + return displayHintHBox; } private DropdownMenu createAndGetSortAndFilterMarketsMenu() { @@ -345,11 +379,13 @@ private DropdownMenu createAndGetSortAndFilterMarketsMenu() { Res.get("bisqEasy.offerbook.dropdownMenu.sortAndFilterMarkets.filterTitle")); filterWithOffers = new DropdownFilterMenuItem<>("check-white", "check-white", Res.get("bisqEasy.offerbook.dropdownMenu.sortAndFilterMarkets.withOffers"), Filters.Markets.WITH_OFFERS); + filterFavourites = new DropdownFilterMenuItem<>("check-white", "check-white", + Res.get("bisqEasy.offerbook.dropdownMenu.sortAndFilterMarkets.favourites"), Filters.Markets.FAVOURITES); filterShowAll = new DropdownFilterMenuItem<>("check-white", "check-white", Res.get("bisqEasy.offerbook.dropdownMenu.sortAndFilterMarkets.all"), Filters.Markets.ALL); dropdownMenu.addMenuItems(sortTitle, sortByMostOffers, sortByNameAZ, sortByNameZA, separator, filterTitle, - filterWithOffers, filterShowAll); + filterWithOffers, filterFavourites, filterShowAll); return dropdownMenu; } diff --git a/i18n/src/main/resources/bisq_easy.properties b/i18n/src/main/resources/bisq_easy.properties index ce07b1ffc0..43e6427c66 100644 --- a/i18n/src/main/resources/bisq_easy.properties +++ b/i18n/src/main/resources/bisq_easy.properties @@ -395,7 +395,7 @@ bisqEasy.offerbook.dropdownMenu.sortAndFilterMarkets.nameAZ=Name A-Z bisqEasy.offerbook.dropdownMenu.sortAndFilterMarkets.nameZA=Name Z-A bisqEasy.offerbook.dropdownMenu.sortAndFilterMarkets.filterTitle=Show markets: bisqEasy.offerbook.dropdownMenu.sortAndFilterMarkets.withOffers=With offers -bisqEasy.offerbook.dropdownMenu.sortAndFilterMarkets.favourites=Favourites +bisqEasy.offerbook.dropdownMenu.sortAndFilterMarkets.favourites=Only favourites bisqEasy.offerbook.dropdownMenu.sortAndFilterMarkets.all=All bisqEasy.offerbook.dropdownMenu.filterOffersByDirectionOrOwner.tooltip=Filter by offer type From 549f1ccb8125b297939c568420b91476557728ce Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Tue, 19 Mar 2024 22:10:59 +0100 Subject: [PATCH 012/119] Show selected market after applying filter --- .../content/bisq_easy/offerbook/BisqEasyOfferbookView.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java index 0048d7ad33..5c731a74ba 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java @@ -506,6 +506,9 @@ private void updateSelectedMarketFilter(Filters.Markets marketFilter) { .filter(menuItem -> menuItem instanceof DropdownFilterMenuItem) .map(menuItem -> (DropdownFilterMenuItem) menuItem) .forEach(menuItem -> menuItem.updateSelection(marketFilter == menuItem.getFilter())); + + favouritesTableView.getSelectionModel().select(getModel().getSelectedMarketChannelItem().get()); + marketsTableView.getSelectionModel().select(getModel().getSelectedMarketChannelItem().get()); } private void updateMarketSortType(MarketSortType marketSortType) { From 86f2a822c45556964a1abc779b81d26d578baa34 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Tue, 19 Mar 2024 22:15:05 +0100 Subject: [PATCH 013/119] Add empty placeholder for market channel items list --- .../main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java index 5c731a74ba..0b9fbfe046 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java @@ -326,6 +326,7 @@ private void addMarketSelectionList() { marketsTableView.allowVerticalScrollbar(); marketsTableView.hideHorizontalScrollbar(); marketsTableView.setFixedCellSize(getController().getMarketSelectionListCellHeight()); + marketsTableView.setPlaceholder(new Label()); configTableView(marketsTableView); VBox.setVgrow(marketsTableView, Priority.ALWAYS); From fddede1d37d54691286b57396b076df6b205ec98 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 10:07:27 +0700 Subject: [PATCH 014/119] Increase minNumConnectedPeers to 12 for seed node --- apps/seed-node-app/src/main/resources/seed_node.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/seed-node-app/src/main/resources/seed_node.conf b/apps/seed-node-app/src/main/resources/seed_node.conf index d887feb6d8..d0d77a11b2 100644 --- a/apps/seed-node-app/src/main/resources/seed_node.conf +++ b/apps/seed-node-app/src/main/resources/seed_node.conf @@ -75,7 +75,7 @@ application { } peerGroup { - minNumConnectedPeers=8 + minNumConnectedPeers=12 maxNumConnectedPeers=20 minNumReportedPeers=1 } From 5cebdd204429b9cbf30be9a925224cae49d8101d Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 10:08:45 +0700 Subject: [PATCH 015/119] Add minNumOutboundConnectedPeers config item --- apps/desktop/desktop-app/src/main/resources/desktop.conf | 1 + apps/rest-api-app/src/main/resources/rest_api.conf | 1 + apps/seed-node-app/src/main/resources/seed_node.conf | 1 + network/network/src/integrationTest/resources/test.conf | 1 + .../network/p2p/services/peergroup/PeerGroupService.java | 9 ++++++--- 5 files changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/desktop/desktop-app/src/main/resources/desktop.conf b/apps/desktop/desktop-app/src/main/resources/desktop.conf index f30201f52c..f7995658ea 100644 --- a/apps/desktop/desktop-app/src/main/resources/desktop.conf +++ b/apps/desktop/desktop-app/src/main/resources/desktop.conf @@ -94,6 +94,7 @@ application { peerGroup { minNumConnectedPeers=8 + minNumOutboundConnectedPeers=3 maxNumConnectedPeers=12 minNumReportedPeers=1 } diff --git a/apps/rest-api-app/src/main/resources/rest_api.conf b/apps/rest-api-app/src/main/resources/rest_api.conf index d16f172314..2f5372fb5c 100644 --- a/apps/rest-api-app/src/main/resources/rest_api.conf +++ b/apps/rest-api-app/src/main/resources/rest_api.conf @@ -94,6 +94,7 @@ application { peerGroup { minNumConnectedPeers=8 + minNumOutboundConnectedPeers=3 maxNumConnectedPeers=12 minNumReportedPeers=1 } diff --git a/apps/seed-node-app/src/main/resources/seed_node.conf b/apps/seed-node-app/src/main/resources/seed_node.conf index d0d77a11b2..8172725af0 100644 --- a/apps/seed-node-app/src/main/resources/seed_node.conf +++ b/apps/seed-node-app/src/main/resources/seed_node.conf @@ -76,6 +76,7 @@ application { peerGroup { minNumConnectedPeers=12 + minNumOutboundConnectedPeers=4 maxNumConnectedPeers=20 minNumReportedPeers=1 } diff --git a/network/network/src/integrationTest/resources/test.conf b/network/network/src/integrationTest/resources/test.conf index 30055ba5a2..08e893fae7 100644 --- a/network/network/src/integrationTest/resources/test.conf +++ b/network/network/src/integrationTest/resources/test.conf @@ -34,6 +34,7 @@ bisq { # Apply to i2p and tor services. peerGroupConfig { minNumConnectedPeers=8 + minNumOutboundConnectedPeers=3 maxNumConnectedPeers=12 minNumReportedPeers=1 } diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupService.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupService.java index ff0da854d9..5173736e1f 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupService.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupService.java @@ -17,7 +17,6 @@ package bisq.network.p2p.services.peergroup; -import bisq.common.util.MathUtils; import bisq.network.common.Address; import bisq.network.common.TransportType; import bisq.network.p2p.node.Connection; @@ -44,17 +43,20 @@ public class PeerGroupService implements PersistenceClient { @Getter public static class Config { private final int minNumConnectedPeers; + private final int minNumOutboundConnectedPeers; private final int maxNumConnectedPeers; private final int minNumReportedPeers; public Config() { - this(8, 12, 1); + this(8, 3, 12, 1); } public Config(int minNumConnectedPeers, + int minNumOutboundConnectedPeers, int maxNumConnectedPeers, int minNumReportedPeers) { this.minNumConnectedPeers = minNumConnectedPeers; + this.minNumOutboundConnectedPeers = minNumOutboundConnectedPeers; this.maxNumConnectedPeers = maxNumConnectedPeers; this.minNumReportedPeers = minNumReportedPeers; } @@ -62,6 +64,7 @@ public Config(int minNumConnectedPeers, public static Config from(com.typesafe.config.Config typesafeConfig) { return new PeerGroupService.Config( typesafeConfig.getInt("minNumConnectedPeers"), + typesafeConfig.getInt("minNumOutboundConnectedPeers"), typesafeConfig.getInt("maxNumConnectedPeers"), typesafeConfig.getInt("minNumReportedPeers")); } @@ -136,7 +139,7 @@ public boolean isSeed(Connection connection) { } public int getMinOutboundConnections() { - return MathUtils.roundDoubleToInt(config.getMinNumConnectedPeers() * 0.4); + return config.getMinNumOutboundConnectedPeers(); } public int getMaxInboundConnections() { From c0595c4b430b6c45b861dd038379347038e69af8 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 10:10:29 +0700 Subject: [PATCH 016/119] Refactor: Remove "Node " from logs where node is logged as we include the string "Node " in the node.toString() method. --- .../p2p/node/InboundConnectionsManager.java | 2 +- .../services/data/broadcast/Broadcaster.java | 2 +- .../data/inventory/InventoryHandler.java | 2 +- .../services/peergroup/PeerGroupManager.java | 20 +++++++++---------- .../exchange/PeerExchangeRequestHandler.java | 8 ++++---- .../exchange/PeerExchangeService.java | 14 ++++++------- .../peergroup/keepalive/KeepAliveHandler.java | 6 +++--- .../peergroup/keepalive/KeepAliveService.java | 4 ++-- .../NetworkLoadExchangeHandler.java | 6 +++--- .../NetworkLoadExchangeService.java | 4 ++-- 10 files changed, 34 insertions(+), 34 deletions(-) diff --git a/network/network/src/main/java/bisq/network/p2p/node/InboundConnectionsManager.java b/network/network/src/main/java/bisq/network/p2p/node/InboundConnectionsManager.java index 00ceb9a66c..38084a78e5 100644 --- a/network/network/src/main/java/bisq/network/p2p/node/InboundConnectionsManager.java +++ b/network/network/src/main/java/bisq/network/p2p/node/InboundConnectionsManager.java @@ -121,7 +121,7 @@ public void handleInboundConnection(SocketChannel socketChannel) { peerAddress, myCapability.getAddress()); if (isAlreadyConnectedToPeer(peerAddress)) { - log.warn("Node {} have already an InboundConnection from {}. This can happen when a " + "handshake was in progress while we received a new connection from that address. " + "We will close the socket of that new connection and use the existing instead.", this, peerAddress); + log.warn("{} have already an InboundConnection from {}. This can happen when a " + "handshake was in progress while we received a new connection from that address. " + "We will close the socket of that new connection and use the existing instead.", this, peerAddress); closeChannel(networkEnvelopeSocketChannel); } else { connectionByChannel.put(socketChannel, inboundConnection); diff --git a/network/network/src/main/java/bisq/network/p2p/services/data/broadcast/Broadcaster.java b/network/network/src/main/java/bisq/network/p2p/services/data/broadcast/Broadcaster.java index 1e4a031f09..ad6eaa93a3 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/data/broadcast/Broadcaster.java +++ b/network/network/src/main/java/bisq/network/p2p/services/data/broadcast/Broadcaster.java @@ -88,7 +88,7 @@ public CompletableFuture doBroadcast(BroadcastMessage broadcast allConnections.stream() .limit(numBroadcasts) .forEach(connection -> { - log.debug("Node {} broadcast to {}", node, connection.getPeerAddress()); + log.debug("{} broadcast to {}", node, connection.getPeerAddress()); try { node.send(broadcastMessage, connection); numSuccess.incrementAndGet(); diff --git a/network/network/src/main/java/bisq/network/p2p/services/data/inventory/InventoryHandler.java b/network/network/src/main/java/bisq/network/p2p/services/data/inventory/InventoryHandler.java index 4a638805c5..65e16ff976 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/data/inventory/InventoryHandler.java +++ b/network/network/src/main/java/bisq/network/p2p/services/data/inventory/InventoryHandler.java @@ -78,7 +78,7 @@ public void onNetworkMessage(EnvelopePayloadMessage envelopePayloadMessage) { connection.getConnectionMetrics().addRtt(System.currentTimeMillis() - ts); future.complete(response.getInventory()); } else { - log.warn("Node {} received InventoryResponse from {} with invalid nonce {}. Request nonce was {}. Connection={}", + log.warn("{} received InventoryResponse from {} with invalid nonce {}. Request nonce was {}. Connection={}", node, connection.getPeerAddress(), response.getRequestNonce(), nonce, connection.getId()); } } diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java index bf2d4565a6..87493cd4d1 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java @@ -174,7 +174,7 @@ public void shutdown() { } private void doInitialize() { - log.info("Node {} called initialize", node); + log.info("{} called initialize", node); String nodeInfo = node.getNodeInfo(); State state = getState().get(); switch (state) { @@ -223,7 +223,7 @@ public void removeSeedNodeAddress(Address seedNodeAddress) { /////////////////////////////////////////////////////////////////////////////////////////////////// private void doHouseKeeping() { - log.debug("Node {} called runBlockingTasks", node); + log.debug("{} called runBlockingTasks", node); try { closeBanned(); maybeCloseDuplicateConnections(); @@ -244,7 +244,7 @@ private void doHouseKeeping() { } private void closeBanned() { - log.debug("Node {} called closeBanned", node); + log.debug("{} called closeBanned", node); node.getAllActiveConnections() .filter(Connection::isRunning) .filter(connection -> banList.isBanned(connection.getPeerAddress())) @@ -256,7 +256,7 @@ private void closeBanned() { * Remove duplicate connections (inbound connections which have an outbound connection with the same address) */ private void maybeCloseDuplicateConnections() { - log.debug("Node {} called maybeCloseDuplicateConnections", node); + log.debug("{} called maybeCloseDuplicateConnections", node); Set
outboundAddresses = node.getActiveOutboundConnections() .map(Connection::getPeerAddress) .collect(Collectors.toSet()); @@ -270,7 +270,7 @@ private void maybeCloseDuplicateConnections() { } private void maybeCloseConnectionsToSeeds() { - log.debug("Node {} called maybeCloseConnectionsToSeeds", node); + log.debug("{} called maybeCloseConnectionsToSeeds", node); Comparator comparator = peerGroupService.getConnectionAgeComparator().reversed(); // reversed as we use skip node.getAllActiveConnections() .filter(this::mayDisconnect) @@ -284,7 +284,7 @@ private void maybeCloseConnectionsToSeeds() { } private void maybeCloseAgedConnections() { - log.debug("Node {} called maybeCloseAgedConnections", node); + log.debug("{} called maybeCloseAgedConnections", node); node.getAllActiveConnections() .filter(this::mayDisconnect) .filter(connection -> connection.getConnectionMetrics().getAge() > config.getMaxAge()) @@ -296,7 +296,7 @@ private void maybeCloseAgedConnections() { } private void maybeCloseExceedingInboundConnections() { - log.debug("Node {} called maybeCloseExceedingInboundConnections", node); + log.debug("{} called maybeCloseExceedingInboundConnections", node); Comparator comparator = peerGroupService.getConnectionAgeComparator().reversed(); node.getActiveInboundConnections() .filter(this::mayDisconnect) @@ -309,7 +309,7 @@ private void maybeCloseExceedingInboundConnections() { } private void maybeCloseExceedingConnections() { - log.debug("Node {} called maybeCloseExceedingConnections", node); + log.debug("{} called maybeCloseExceedingConnections", node); Comparator comparator = peerGroupService.getConnectionAgeComparator().reversed(); node.getAllActiveConnections() .filter(this::mayDisconnect) @@ -322,13 +322,13 @@ private void maybeCloseExceedingConnections() { } private void maybeCreateConnections() { - log.debug("Node {} called maybeCreateConnections", node); + log.debug("{} called maybeCreateConnections", node); int minNumConnectedPeers = peerGroupService.getMinNumConnectedPeers(); // We want to have at least 40% of our minNumConnectedPeers as outbound connections if (getMissingOutboundConnections() <= 0) { // We have enough outbound connections, lets check if we have sufficient connections in total if (node.getNumConnections() >= minNumConnectedPeers) { - log.debug("Node {} has sufficient connections", node); + log.debug("{} has sufficient connections", node); CompletableFuture.completedFuture(null); return; } diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeRequestHandler.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeRequestHandler.java index 060b4589e8..a5822964f9 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeRequestHandler.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeRequestHandler.java @@ -48,7 +48,7 @@ class PeerExchangeRequestHandler implements Connection.Listener { } CompletableFuture> request(Set peersForPeerExchange) { - log.debug("Node {} send PeerExchangeRequest to {} with {} peers", + log.debug("{} send PeerExchangeRequest to {} with {} peers", node, connection.getPeerAddress(), peersForPeerExchange.size()); ts = System.currentTimeMillis(); try { @@ -69,15 +69,15 @@ public void onNetworkMessage(EnvelopePayloadMessage envelopePayloadMessage) { /* String addresses = StringUtils.truncate(response.peers().stream() .map(peer -> peer.getAddress().toString()) .collect(Collectors.toList()).toString()); - log.debug("Node {} received PeerExchangeResponse from {} with {}", + log.debug("{} received PeerExchangeResponse from {} with {}", node, connection.getPeerAddress(), addresses);*/ - log.info("Node {} received PeerExchangeResponse from {} with {} peers", + log.info("{} received PeerExchangeResponse from {} with {} peers", node, connection.getPeerAddress(), response.getPeers().size()); connection.getConnectionMetrics().addRtt(System.currentTimeMillis() - ts); removeListeners(); future.complete(new HashSet<>(response.getPeers())); } else { - log.warn("Node {} received a PeerExchangeResponse from {} with an invalid nonce. response.nonce()={}, nonce={}", + log.warn("{} received a PeerExchangeResponse from {} with an invalid nonce. response.nonce()={}, nonce={}", node, connection.getPeerAddress(), response.getNonce(), nonce); } } diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeService.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeService.java index 1fc8c49b52..237408c061 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeService.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeService.java @@ -77,12 +77,12 @@ public void shutdown() { public void onMessage(EnvelopePayloadMessage envelopePayloadMessage, Connection connection, NetworkId networkId) { if (envelopePayloadMessage instanceof PeerExchangeRequest) { PeerExchangeRequest request = (PeerExchangeRequest) envelopePayloadMessage; - //log.debug("Node {} received PeerExchangeRequest with myPeers {}", node, request.peers()); + //log.debug("{} received PeerExchangeRequest with myPeers {}", node, request.peers()); Address peerAddress = connection.getPeerAddress(); List myPeers = new ArrayList<>(peerExchangeStrategy.getPeersForReporting(peerAddress)); peerExchangeStrategy.addReportedPeers(new HashSet<>(request.getPeers()), peerAddress); NETWORK_IO_POOL.submit(() -> node.send(new PeerExchangeResponse(request.getNonce(), myPeers), connection)); - log.debug("Node {} sent PeerExchangeResponse with my myPeers {}", node, myPeers); + log.debug("{} sent PeerExchangeResponse with my myPeers {}", node, myPeers); } } @@ -126,7 +126,7 @@ private CompletableFuture doPeerExchange(List
candidates) { "was not completed")); } - log.info("Node {} starts peer exchange with: {}", node, + log.info("{} starts peer exchange with: {}", node, StringUtils.truncate(candidates.stream() .map(Address::toString) .collect(Collectors.toList()) @@ -157,10 +157,10 @@ private CompletableFuture doPeerExchange(List
candidates) { } if (numFailures.get() + numSuccess.get() == candidates.size()) { - log.info("Node {} completed peer exchange to {} candidates. {} requests successfully completed.", + log.info("{} completed peer exchange to {} candidates. {} requests successfully completed.", node, candidates.size(), numSuccess); if (peerExchangeStrategy.shouldRedoInitialPeerExchange(numSuccess.get(), candidates.size())) { - log.info("Node {} repeats the initial peer exchange after {} sec as it has not reached sufficient connections " + + log.info("{} repeats the initial peer exchange after {} sec as it has not reached sufficient connections " + "or received sufficient peers", node, doInitialPeerExchangeDelaySec); scheduler.ifPresent(Scheduler::stop); scheduler = Optional.of(Scheduler.run(this::startInitialPeerExchange) @@ -204,7 +204,7 @@ private boolean doPeerExchange(Address peerAddress) { // We request and wait for response Set reportedPeers = handler.request(myPeers).join(); - log.info("Node {} completed peer exchange with {} and received {} reportedPeers.", + log.info("{} completed peer exchange with {} and received {} reportedPeers.", node, peerAddress, reportedPeers.size()); peerExchangeStrategy.addReportedPeers(reportedPeers, peerAddress); requestHandlerMap.remove(key); @@ -216,7 +216,7 @@ private boolean doPeerExchange(Address peerAddress) { requestHandlerMap.remove(key); } } - log.debug("Node {} failed to do a peer exchange with {}.", + log.debug("{} failed to do a peer exchange with {}.", node, peerAddress, throwable); return false; } diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/keepalive/KeepAliveHandler.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/keepalive/KeepAliveHandler.java index 68012851a3..886751a3a2 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/keepalive/KeepAliveHandler.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/keepalive/KeepAliveHandler.java @@ -48,7 +48,7 @@ class KeepAliveHandler implements Connection.Listener { } CompletableFuture request() { - log.info("Node {} send Ping to {} with nonce {}. Connection={}", + log.info("{} send Ping to {} with nonce {}. Connection={}", node, connection.getPeerAddress(), nonce, connection.getId()); ts = System.currentTimeMillis(); supplyAsync(() -> node.send(new Ping(nonce), connection), NetworkService.NETWORK_IO_POOL) @@ -66,13 +66,13 @@ public void onNetworkMessage(EnvelopePayloadMessage envelopePayloadMessage) { if (envelopePayloadMessage instanceof Pong) { Pong pong = (Pong) envelopePayloadMessage; if (pong.getRequestNonce() == nonce) { - log.info("Node {} received Pong from {} with nonce {}. Connection={}", + log.info("{} received Pong from {} with nonce {}. Connection={}", node, connection.getPeerAddress(), pong.getRequestNonce(), connection.getId()); removeListeners(); connection.getConnectionMetrics().addRtt(System.currentTimeMillis() - ts); future.complete(null); } else { - log.warn("Node {} received Pong from {} with invalid nonce {}. Request nonce was {}. Connection={}", + log.warn("{} received Pong from {} with invalid nonce {}. Request nonce was {}. Connection={}", node, connection.getPeerAddress(), pong.getRequestNonce(), nonce, connection.getId()); } } diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/keepalive/KeepAliveService.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/keepalive/KeepAliveService.java index 1329ec7edf..350ddc8de3 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/keepalive/KeepAliveService.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/keepalive/KeepAliveService.java @@ -101,9 +101,9 @@ public void sendPing(Connection connection) { public void onMessage(EnvelopePayloadMessage envelopePayloadMessage, Connection connection, NetworkId networkId) { if (envelopePayloadMessage instanceof Ping) { Ping ping = (Ping) envelopePayloadMessage; - log.debug("Node {} received Ping with nonce {} from {}", node, ping.getNonce(), connection.getPeerAddress()); + log.debug("{} received Ping with nonce {} from {}", node, ping.getNonce(), connection.getPeerAddress()); NetworkService.NETWORK_IO_POOL.submit(() -> node.send(new Pong(ping.getNonce()), connection)); - log.debug("Node {} sent Pong with nonce {} to {}. Connection={}", node, ping.getNonce(), connection.getPeerAddress(), connection.getId()); + log.debug("{} sent Pong with nonce {} to {}. Connection={}", node, ping.getNonce(), connection.getPeerAddress(), connection.getId()); } } diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/network_load/NetworkLoadExchangeHandler.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/network_load/NetworkLoadExchangeHandler.java index e0e00dd0f3..d0c2cc543c 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/network_load/NetworkLoadExchangeHandler.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/network_load/NetworkLoadExchangeHandler.java @@ -50,7 +50,7 @@ class NetworkLoadExchangeHandler implements Connection.Listener { CompletableFuture request() { NetworkLoad myNetworkLoad = node.getNetworkLoadSnapshot().getCurrentNetworkLoad(); - log.info("Node {} send NetworkLoadRequest to {} with nonce {} and my networkLoad {}. Connection={}", + log.info("{} send NetworkLoadRequest to {} with nonce {} and my networkLoad {}. Connection={}", node, connection.getPeerAddress(), nonce, myNetworkLoad, connection.getId()); ts = System.currentTimeMillis(); supplyAsync(() -> node.send(new NetworkLoadExchangeRequest(nonce, myNetworkLoad), connection), NetworkService.NETWORK_IO_POOL) @@ -69,14 +69,14 @@ public void onNetworkMessage(EnvelopePayloadMessage envelopePayloadMessage) { NetworkLoadExchangeResponse response = (NetworkLoadExchangeResponse) envelopePayloadMessage; if (response.getRequestNonce() == nonce) { NetworkLoad peersNetworkLoad = response.getNetworkLoad(); - log.info("Node {} received NetworkLoadResponse from {} with nonce {} and peers networkLoad {}. Connection={}", + log.info("{} received NetworkLoadResponse from {} with nonce {} and peers networkLoad {}. Connection={}", node, connection.getPeerAddress(), response.getRequestNonce(), peersNetworkLoad, connection.getId()); removeListeners(); connection.getPeersNetworkLoadSnapshot().updateNetworkLoad(peersNetworkLoad); connection.getConnectionMetrics().addRtt(System.currentTimeMillis() - ts); future.complete(null); } else { - log.warn("Node {} received NetworkLoadResponse from {} with invalid nonce {}. Request nonce was {}. Connection={}", + log.warn("{} received NetworkLoadResponse from {} with invalid nonce {}. Request nonce was {}. Connection={}", node, connection.getPeerAddress(), response.getRequestNonce(), nonce, connection.getId()); } } diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/network_load/NetworkLoadExchangeService.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/network_load/NetworkLoadExchangeService.java index cb9811eea4..f8007d9896 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/network_load/NetworkLoadExchangeService.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/network_load/NetworkLoadExchangeService.java @@ -94,7 +94,7 @@ public void onMessage(EnvelopePayloadMessage envelopePayloadMessage, Connection if (envelopePayloadMessage instanceof NetworkLoadExchangeRequest) { NetworkLoadExchangeRequest request = (NetworkLoadExchangeRequest) envelopePayloadMessage; NetworkLoad peersNetworkLoad = request.getNetworkLoad(); - log.info("Node {} received NetworkLoadRequest with nonce {} and peers networkLoad {} from {}", + log.info("{} received NetworkLoadRequest with nonce {} and peers networkLoad {} from {}", node, request.getNonce(), peersNetworkLoad, connection.getPeerAddress()); connection.getPeersNetworkLoadSnapshot().updateNetworkLoad(peersNetworkLoad); NetworkLoad myNetworkLoad = node.getNetworkLoadSnapshot().getCurrentNetworkLoad(); @@ -102,7 +102,7 @@ public void onMessage(EnvelopePayloadMessage envelopePayloadMessage, Connection myNetworkLoad); NetworkService.NETWORK_IO_POOL.submit(() -> node.send(response, connection)); - log.info("Node {} sent NetworkLoadResponse with nonce {} and my networkLoad {} to {}. Connection={}", + log.info("{} sent NetworkLoadResponse with nonce {} and my networkLoad {} to {}. Connection={}", node, request.getNonce(), myNetworkLoad, connection.getPeerAddress(), connection.getId()); } } From e1706bfffa5c200f00f53f75e18c659f323d7616 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 10:12:47 +0700 Subject: [PATCH 017/119] Add minNumOutboundConnectedPeers config item --- .../oracle-node-app/src/main/resources/oracle_node.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/oracle-node/oracle-node-app/src/main/resources/oracle_node.conf b/apps/oracle-node/oracle-node-app/src/main/resources/oracle_node.conf index f96df5a6f1..77a577600b 100644 --- a/apps/oracle-node/oracle-node-app/src/main/resources/oracle_node.conf +++ b/apps/oracle-node/oracle-node-app/src/main/resources/oracle_node.conf @@ -98,6 +98,7 @@ application { peerGroup { minNumConnectedPeers=8 + minNumOutboundConnectedPeers=3 maxNumConnectedPeers=12 minNumReportedPeers=1 } From b941860b555fd8f0a49f0e2db3af2f0ea972f9fa Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 10:13:26 +0700 Subject: [PATCH 018/119] Add util methods for Comparator and delegate methods to Connection --- .../bisq/network/p2p/node/Connection.java | 29 +++++++++++++++++-- .../node/network_load/ConnectionMetrics.java | 2 +- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/network/network/src/main/java/bisq/network/p2p/node/Connection.java b/network/network/src/main/java/bisq/network/p2p/node/Connection.java index e36d982d5f..c40db22ab3 100644 --- a/network/network/src/main/java/bisq/network/p2p/node/Connection.java +++ b/network/network/src/main/java/bisq/network/p2p/node/Connection.java @@ -35,6 +35,8 @@ import java.io.EOFException; import java.io.IOException; import java.net.Socket; +import java.util.Comparator; +import java.util.Date; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.Future; @@ -52,6 +54,14 @@ */ @Slf4j public abstract class Connection { + public static Comparator comparingDateDescending() { + return comparingDate().reversed(); + } + + public static Comparator comparingDate() { + return Comparator.comparingLong(Connection::getCreated); + } + protected interface Handler { void handleNetworkMessage(EnvelopePayloadMessage envelopePayloadMessage, AuthorizationToken authorizationToken, @@ -168,9 +178,24 @@ public boolean isRunning() { return !isStopped(); } + public Address getPeersAddress() { + return getPeersCapability().getAddress(); + } + + public long getCreated() { + return getConnectionMetrics().getCreated(); + } + + public Date getCreationDate() { + return getConnectionMetrics().getCreationDate(); + } + + public boolean createdBefore(long date) { + return getCreated() < date; + } @Override public String toString() { - return "'" + getClass().getSimpleName() + " [peerAddress=" + getPeersCapability().getAddress() + + return "'" + getClass().getSimpleName() + " [peerAddress=" + getPeersAddress() + ", keyId=" + getId() + "]'"; } @@ -277,7 +302,7 @@ boolean isStopped() { /////////////////////////////////////////////////////////////////////////////////////////////////// private String getThreadNameId() { - return StringUtils.truncate(getPeersCapability().getAddress().toString() + "-" + id.substring(0, 8)); + return StringUtils.truncate(getPeersAddress().toString() + "-" + id.substring(0, 8)); } private boolean isInputStreamActive() { diff --git a/network/network/src/main/java/bisq/network/p2p/node/network_load/ConnectionMetrics.java b/network/network/src/main/java/bisq/network/p2p/node/network_load/ConnectionMetrics.java index 3363e3860a..69357303ad 100644 --- a/network/network/src/main/java/bisq/network/p2p/node/network_load/ConnectionMetrics.java +++ b/network/network/src/main/java/bisq/network/p2p/node/network_load/ConnectionMetrics.java @@ -46,7 +46,7 @@ public class ConnectionMetrics { private final List rrtList = new CopyOnWriteArrayList<>(); public ConnectionMetrics() { - created = new Date().getTime(); + created = System.currentTimeMillis(); } public Date getCreationDate() { From e09887ed235dddc635d560620ade914c15e208b4 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 10:14:54 +0700 Subject: [PATCH 019/119] Set log level for CancellationException to debug if inventory request failed --- .../services/data/inventory/InventoryRequestService.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/network/network/src/main/java/bisq/network/p2p/services/data/inventory/InventoryRequestService.java b/network/network/src/main/java/bisq/network/p2p/services/data/inventory/InventoryRequestService.java index d21a5dce46..0ffdc95a5f 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/data/inventory/InventoryRequestService.java +++ b/network/network/src/main/java/bisq/network/p2p/services/data/inventory/InventoryRequestService.java @@ -40,6 +40,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -213,7 +214,11 @@ private List> requestFromPeers() { }); } if (throwable != null) { - log.warn("Inventory request failed.", throwable); + if (throwable instanceof CancellationException) { + log.debug("Inventory request failed.", throwable); + } else { + log.info("Inventory request failed.", throwable); + } } }); }) From 9e4acd1e8f4132879ce6451886b60499372f3a41 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 10:17:17 +0700 Subject: [PATCH 020/119] Refactor: Rename methods --- .../services/peergroup/PeerGroupManager.java | 18 +++++++++--------- .../exchange/PeerExchangeService.java | 4 ++-- .../exchange/PeerExchangeStrategy.java | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java index 87493cd4d1..486bb7af9b 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java @@ -261,7 +261,7 @@ private void maybeCloseDuplicateConnections() { .map(Connection::getPeerAddress) .collect(Collectors.toSet()); node.getActiveInboundConnections() - .filter(this::mayDisconnect) + .filter(this::allowDisconnect) .filter(inbound -> outboundAddresses.contains(inbound.getPeerAddress())) .peek(inbound -> log.info("{} -> {}: Send CloseConnectionMessage as we have an " + "outbound connection with the same address.", @@ -273,7 +273,7 @@ private void maybeCloseConnectionsToSeeds() { log.debug("{} called maybeCloseConnectionsToSeeds", node); Comparator comparator = peerGroupService.getConnectionAgeComparator().reversed(); // reversed as we use skip node.getAllActiveConnections() - .filter(this::mayDisconnect) + .filter(this::allowDisconnect) .filter(peerGroupService::isSeed) .sorted(comparator) .skip(config.getMaxSeeds()) @@ -286,7 +286,7 @@ private void maybeCloseConnectionsToSeeds() { private void maybeCloseAgedConnections() { log.debug("{} called maybeCloseAgedConnections", node); node.getAllActiveConnections() - .filter(this::mayDisconnect) + .filter(this::allowDisconnect) .filter(connection -> connection.getConnectionMetrics().getAge() > config.getMaxAge()) .peek(connection -> log.info("{} -> {}: Send CloseConnectionMessage as the connection age " + "is too old.", @@ -299,7 +299,7 @@ private void maybeCloseExceedingInboundConnections() { log.debug("{} called maybeCloseExceedingInboundConnections", node); Comparator comparator = peerGroupService.getConnectionAgeComparator().reversed(); node.getActiveInboundConnections() - .filter(this::mayDisconnect) + .filter(this::allowDisconnect) .sorted(comparator) .skip(peerGroupService.getMaxInboundConnections()) .peek(connection -> log.info("{} -> {}: Send CloseConnectionMessage as we have too many inbound connections.", @@ -312,7 +312,7 @@ private void maybeCloseExceedingConnections() { log.debug("{} called maybeCloseExceedingConnections", node); Comparator comparator = peerGroupService.getConnectionAgeComparator().reversed(); node.getAllActiveConnections() - .filter(this::mayDisconnect) + .filter(this::allowDisconnect) .sorted(comparator) .skip(peerGroupService.getMaxNumConnectedPeers()) .peek(connection -> log.info("{} -> {}: Send CloseConnectionMessage as we have too many connections.", @@ -338,7 +338,7 @@ private void maybeCreateConnections() { // The calculation how many connections we need is done inside PeerExchangeService/PeerExchangeStrategy log.info("We have not sufficient connections and call peerExchangeService.doFurtherPeerExchange"); // It is an async call. We do not wait for the result. - peerExchangeService.startFurtherPeerExchange(); + peerExchangeService.extendPeerGroup(); } private void maybeRemoveReportedPeers() { @@ -390,11 +390,11 @@ private void setState(PeerGroupManager.State newState) { // Utils /////////////////////////////////////////////////////////////////////////////////////////////////// - private boolean mayDisconnect(Connection connection) { - return notBootstrapping(connection) && connection.isRunning(); + private boolean allowDisconnect(Connection connection) { + return isNotBootstrapping(connection) && connection.isRunning(); } - private boolean notBootstrapping(Connection connection) { + private boolean isNotBootstrapping(Connection connection) { return connection.getConnectionMetrics().getAge() > config.getBootstrapTime(); } diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeService.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeService.java index 237408c061..473eb9192b 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeService.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeService.java @@ -103,8 +103,8 @@ public CompletableFuture startInitialPeerExchange() { return doPeerExchange(peerExchangeStrategy.getAddressesForInitialPeerExchange()).orTimeout(2, MINUTES); } - public void startFurtherPeerExchange() { - doPeerExchange(peerExchangeStrategy.getAddressesForFurtherPeerExchange()).orTimeout(2, MINUTES); + public void extendPeerGroup() { + doPeerExchange(peerExchangeStrategy.getAddressesForExtendingPeerGroup()).orTimeout(2, MINUTES); } /** diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java index 8370642f87..4afeaea9da 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java @@ -96,15 +96,15 @@ List
getAddressesForInitialPeerExchange() { // After bootstrap, we might want to add more connections and use the peer exchange protocol for that. // We do not want to use seed nodes or already existing connections in that case. - List
getAddressesForFurtherPeerExchange() { - List
candidates = getCandidates(getPriorityListForFurtherPeerExchange()); + List
getAddressesForExtendingPeerGroup() { + List
candidates = getCandidates(getPriorityListForExtendingPeerGroup()); if (candidates.isEmpty()) { // It can be that we don't have peers anymore which we have not already connected in the past. // We reset the usedAddresses and try again. It is likely that some peers have different peers to // send now. log.debug("We reset the usedAddresses and try again to connect to peers we tried in the past."); usedAddresses.clear(); - candidates = getCandidates(getPriorityListForFurtherPeerExchange()); + candidates = getCandidates(getPriorityListForExtendingPeerGroup()); } usedAddresses.addAll(candidates); return candidates; @@ -148,7 +148,7 @@ private List
getPriorityListForInitialPeerExchange() { return priorityList; } - private List
getPriorityListForFurtherPeerExchange() { + private List
getPriorityListForExtendingPeerGroup() { List
priorityList = new ArrayList<>(getReportedPeerAddresses()); priorityList.addAll(getPersistedAddresses()); return priorityList; From 8ff949e6eb75eb3c5335aa0268b2f2254079fed2 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 10:57:57 +0700 Subject: [PATCH 021/119] Add isNotOutDated filter --- .../peergroup/exchange/PeerExchangeStrategy.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java index 4afeaea9da..3548d7daaa 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java @@ -112,7 +112,8 @@ List
getAddressesForExtendingPeerGroup() { boolean shouldRedoInitialPeerExchange(int numSuccess, int numRequests) { int numFailed = numRequests - numSuccess; - return numFailed > numRequests / 2 || + int maxFailures = numRequests / 2; + return numFailed > maxFailures || peerGroupService.getAllConnectedPeers(node).count() < peerGroupService.getTargetNumConnectedPeers() || peerGroupService.getReportedPeers().size() < peerGroupService.getMinNumReportedPeers(); } @@ -172,6 +173,7 @@ private List
getReportedPeerAddresses() { private List
getPersistedAddresses() { return peerGroupService.getPersistedPeers().stream() .filter(this::isValidNonSeedPeer) + .filter(this::isNotOutDated) .sorted(Comparator.comparing(Peer::getDate)) .limit(config.getNumPersistedPeersAtBoostrap()) .map(Peer::getAddress) @@ -211,7 +213,7 @@ void addReportedPeers(Set reportedPeers, Address reporterAddress) { Set filtered = reportedPeers.stream() .filter(peer -> notSameAddress(reporterAddress, peer)) .filter(this::isValidNonSeedPeer) - .filter(this::isNotAged) + .filter(this::isNotOutDated) .sorted(Comparator.comparing(Peer::getDate).reversed()) .limit(REPORTED_PEERS_LIMIT) .collect(Collectors.toSet()); @@ -242,7 +244,7 @@ private boolean isValidNonSeedPeer(Peer peer) { return isValidNonSeedPeer(peer.getAddress()); } - private boolean isNotAged(Peer peer) { + private boolean isNotOutDated(Peer peer) { return peer.getAge() < MAX_AGE; } @@ -267,7 +269,7 @@ private Stream getAllConnectedPeers() { private Stream getReportedPeers() { return peerGroupService.getReportedPeers().stream() .filter(this::isValidNonSeedPeer) - .filter(this::isNotAged) + .filter(this::isNotOutDated) .sorted(Comparator.comparing(Peer::getDate).reversed()); } From a2001ee515de6b05d747620d0c31dc8b5d51aae1 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 11:05:15 +0700 Subject: [PATCH 022/119] Add missing reverse to sorting at getPersistedAddresses --- .../p2p/services/peergroup/exchange/PeerExchangeStrategy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java index 3548d7daaa..4f92ba9121 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java @@ -174,7 +174,7 @@ private List
getPersistedAddresses() { return peerGroupService.getPersistedPeers().stream() .filter(this::isValidNonSeedPeer) .filter(this::isNotOutDated) - .sorted(Comparator.comparing(Peer::getDate)) + .sorted(Comparator.comparing(Peer::getDate).reversed()) .limit(config.getNumPersistedPeersAtBoostrap()) .map(Peer::getAddress) .collect(Collectors.toList()); From ce5e63d9f36782687b89e08f89a18368525e2bae Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 11:06:42 +0700 Subject: [PATCH 023/119] Replace `sorted(Comparator.comparing(Peer::getDate).reversed())` with `sorted()` as we use descending order in `Peer.compareTo` --- .../services/peergroup/exchange/PeerExchangeStrategy.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java index 4f92ba9121..159b18146d 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java @@ -174,7 +174,7 @@ private List
getPersistedAddresses() { return peerGroupService.getPersistedPeers().stream() .filter(this::isValidNonSeedPeer) .filter(this::isNotOutDated) - .sorted(Comparator.comparing(Peer::getDate).reversed()) + .sorted() .limit(config.getNumPersistedPeersAtBoostrap()) .map(Peer::getAddress) .collect(Collectors.toList()); @@ -214,7 +214,7 @@ void addReportedPeers(Set reportedPeers, Address reporterAddress) { .filter(peer -> notSameAddress(reporterAddress, peer)) .filter(this::isValidNonSeedPeer) .filter(this::isNotOutDated) - .sorted(Comparator.comparing(Peer::getDate).reversed()) + .sorted() .limit(REPORTED_PEERS_LIMIT) .collect(Collectors.toSet()); peerGroupService.addReportedPeers(filtered); @@ -263,14 +263,14 @@ private boolean notSameAddress(Address address, Peer peer) { private Stream getAllConnectedPeers() { return peerGroupService.getAllConnectedPeers(node) .filter(this::isValidNonSeedPeer) - .sorted(Comparator.comparing(Peer::getDate).reversed()); + .sorted(); } private Stream getReportedPeers() { return peerGroupService.getReportedPeers().stream() .filter(this::isValidNonSeedPeer) .filter(this::isNotOutDated) - .sorted(Comparator.comparing(Peer::getDate).reversed()); + .sorted(); } private List
getShuffled(Collection
addresses) { From bedf06f73f175cc251c53e69c9ffccffeae643aa Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 11:11:45 +0700 Subject: [PATCH 024/119] Add handling of timeouts to startInitialPeerExchange and extendPeerGroup. We apply the exception to resultFuture and in case of startInitialPeerExchange we start the scheduler for repeating a call to startInitialPeerExchange. --- .../exchange/PeerExchangeService.java | 32 +++++++++++++++++-- .../exchange/PeerExchangeStrategy.java | 6 ++-- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeService.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeService.java index 473eb9192b..777d0f2f19 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeService.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeService.java @@ -100,11 +100,37 @@ public void onDisconnect(Connection connection, CloseReason closeReason) { /////////////////////////////////////////////////////////////////////////////////////////////////// public CompletableFuture startInitialPeerExchange() { - return doPeerExchange(peerExchangeStrategy.getAddressesForInitialPeerExchange()).orTimeout(2, MINUTES); + List
candidates = peerExchangeStrategy.getAddressesForInitialPeerExchange(); + log.info("startInitialPeerExchange {}", candidates); + return doPeerExchange(candidates) + .orTimeout(2, MINUTES) + .whenComplete((nil, throwable) -> { + if (throwable != null) { + if (resultFuture != null) { + resultFuture.completeExceptionally(throwable); + + scheduler.ifPresent(Scheduler::stop); + scheduler = Optional.of(Scheduler.run(this::startInitialPeerExchange) + .after(doInitialPeerExchangeDelaySec, TimeUnit.SECONDS) + .name("PeerExchangeService.scheduler-" + StringUtils.truncate(node.toString(), 10))); + doInitialPeerExchangeDelaySec = Math.min(20, doInitialPeerExchangeDelaySec * 2); + } + } + }); } - public void extendPeerGroup() { - doPeerExchange(peerExchangeStrategy.getAddressesForExtendingPeerGroup()).orTimeout(2, MINUTES); + public CompletableFuture extendPeerGroup() { + List
candidates = peerExchangeStrategy.getAddressesForExtendingPeerGroup(); + log.info("extendPeerGroup {}", candidates); + return doPeerExchange(candidates) + .orTimeout(2, MINUTES) + .whenComplete((nil, throwable) -> { + if (throwable != null) { + if (resultFuture != null) { + resultFuture.completeExceptionally(throwable); + } + } + }); } /** diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java index 159b18146d..b3b83531fd 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeStrategy.java @@ -210,15 +210,15 @@ Set getPeersForReporting(Address requesterAddress) { } void addReportedPeers(Set reportedPeers, Address reporterAddress) { - Set filtered = reportedPeers.stream() + Set peers = reportedPeers.stream() .filter(peer -> notSameAddress(reporterAddress, peer)) .filter(this::isValidNonSeedPeer) .filter(this::isNotOutDated) .sorted() .limit(REPORTED_PEERS_LIMIT) .collect(Collectors.toSet()); - peerGroupService.addReportedPeers(filtered); - peerGroupService.addPersistedPeers(filtered); + peerGroupService.addReportedPeers(peers); + peerGroupService.addPersistedPeers(peers); } From bd09b0ce1e16612d5b04bd146fb7b8c2564e35dd Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 11:16:29 +0700 Subject: [PATCH 025/119] Add Node.Listener and call maybeCreateConnections at onDisconnect. This should ensure that we quickly get out optimal peer group size in case we lose connections. We add a 2 sec. delay to avoid that we get called too frequently in case of batch disconnects. --- .../services/peergroup/PeerGroupManager.java | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java index 486bb7af9b..3bbd19454e 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java @@ -20,6 +20,8 @@ import bisq.common.timer.Scheduler; import bisq.network.NetworkService; import bisq.network.common.Address; +import bisq.network.identity.NetworkId; +import bisq.network.p2p.message.EnvelopePayloadMessage; import bisq.network.p2p.node.CloseReason; import bisq.network.p2p.node.Connection; import bisq.network.p2p.node.Node; @@ -46,7 +48,7 @@ import static java.util.concurrent.TimeUnit.SECONDS; @Slf4j -public class PeerGroupManager { +public class PeerGroupManager implements Node.Listener { public enum State { NEW, STARTING, @@ -123,7 +125,7 @@ public static Config from(PeerGroupService.Config peerGroupConfig, private final KeepAliveService keepAliveService; private final NetworkLoadExchangeService networkLoadExchangeService; private Optional scheduler = Optional.empty(); - + private Optional maybeCreateConnectionsScheduler = Optional.empty(); @Getter public AtomicReference state = new AtomicReference<>(PeerGroupManager.State.NEW); @@ -161,18 +163,40 @@ public PeerGroupManager(Node node, public void initialize() { // blocking + node.addListener(this); Failsafe.with(retryPolicy).run(this::doInitialize); } public void shutdown() { setState(State.STOPPING); + node.removeListener(this); peerExchangeService.shutdown(); keepAliveService.shutdown(); networkLoadExchangeService.shutdown(); scheduler.ifPresent(Scheduler::stop); + maybeCreateConnectionsScheduler.ifPresent(Scheduler::stop); setState(State.TERMINATED); } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // Node.Listener + /////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void onMessage(EnvelopePayloadMessage envelopePayloadMessage, Connection connection, NetworkId networkId) { + } + + @Override + public void onConnection(Connection connection) { + } + + @Override + public void onDisconnect(Connection connection, CloseReason closeReason) { + maybeCreateConnectionsScheduler.ifPresent(Scheduler::stop); + maybeCreateConnectionsScheduler = Optional.of(Scheduler.run(this::maybeCreateConnections).after(2000)); + } + private void doInitialize() { log.info("{} called initialize", node); String nodeInfo = node.getNodeInfo(); @@ -236,6 +260,8 @@ private void doHouseKeeping() { Thread.sleep(100); maybeCloseExceedingConnections(); Thread.sleep(100); + maybeCreateConnectionsScheduler.ifPresent(Scheduler::stop); + maybeCreateConnectionsScheduler = Optional.empty(); maybeCreateConnections(); maybeRemoveReportedPeers(); maybeRemovePersistedPeers(); From c712a798230f39e7a10bd4126f3ab96c0a9db577 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 11:20:57 +0700 Subject: [PATCH 026/119] Replace `peerGroupService.getConnectionAgeComparator().reversed()` with `Connection.comparingDateDescending()` --- .../network/p2p/services/peergroup/PeerGroupManager.java | 9 +++------ .../network/p2p/services/peergroup/PeerGroupService.java | 5 ----- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java index 3bbd19454e..1be2ea346d 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java @@ -297,11 +297,10 @@ private void maybeCloseDuplicateConnections() { private void maybeCloseConnectionsToSeeds() { log.debug("{} called maybeCloseConnectionsToSeeds", node); - Comparator comparator = peerGroupService.getConnectionAgeComparator().reversed(); // reversed as we use skip node.getAllActiveConnections() .filter(this::allowDisconnect) .filter(peerGroupService::isSeed) - .sorted(comparator) + .sorted(Connection.comparingDateDescending()) // As we use skip we sort by descending creationDate so that we close the oldest connections .skip(config.getMaxSeeds()) .peek(connection -> log.info("{} -> {}: Send CloseConnectionMessage as we have too " + "many connections to seeds.", @@ -323,10 +322,9 @@ private void maybeCloseAgedConnections() { private void maybeCloseExceedingInboundConnections() { log.debug("{} called maybeCloseExceedingInboundConnections", node); - Comparator comparator = peerGroupService.getConnectionAgeComparator().reversed(); node.getActiveInboundConnections() .filter(this::allowDisconnect) - .sorted(comparator) + .sorted(Connection.comparingDateDescending()) // As we use skip we sort by descending creationDate so that we close the oldest connections .skip(peerGroupService.getMaxInboundConnections()) .peek(connection -> log.info("{} -> {}: Send CloseConnectionMessage as we have too many inbound connections.", node, connection.getPeersCapability().getAddress())) @@ -336,10 +334,9 @@ private void maybeCloseExceedingInboundConnections() { private void maybeCloseExceedingConnections() { log.debug("{} called maybeCloseExceedingConnections", node); - Comparator comparator = peerGroupService.getConnectionAgeComparator().reversed(); node.getAllActiveConnections() .filter(this::allowDisconnect) - .sorted(comparator) + .sorted(Connection.comparingDateDescending()) // As we use skip we sort by descending creationDate so that we close the oldest connections .skip(peerGroupService.getMaxNumConnectedPeers()) .peek(connection -> log.info("{} -> {}: Send CloseConnectionMessage as we have too many connections.", node, connection.getPeersCapability().getAddress())) diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupService.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupService.java index 5173736e1f..377b6844fe 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupService.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupService.java @@ -29,7 +29,6 @@ import lombok.extern.slf4j.Slf4j; import java.util.Collection; -import java.util.Comparator; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.stream.Stream; @@ -146,10 +145,6 @@ public int getMaxInboundConnections() { return config.getMaxNumConnectedPeers() - getMinOutboundConnections(); } - public Comparator getConnectionAgeComparator() { - return Comparator.comparing(connection -> connection.getConnectionMetrics().getCreationDate()); - } - /////////////////////////////////////////////////////////////////////////////////////////////////// // Peers From 994ea59df9b2a48240f45f2f1b5b8975389993b8 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 11:50:37 +0700 Subject: [PATCH 027/119] Refactor: Replace `connection.getPeersCapability().getAddress()` with `connection.getAddress()` --- .../network/p2p/services/peergroup/PeerGroupManager.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java index 1be2ea346d..cfce6c07ff 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java @@ -304,7 +304,7 @@ private void maybeCloseConnectionsToSeeds() { .skip(config.getMaxSeeds()) .peek(connection -> log.info("{} -> {}: Send CloseConnectionMessage as we have too " + "many connections to seeds.", - node, connection.getPeersCapability().getAddress())) + node, connection.getPeerAddress())) .forEach(connection -> node.closeConnectionGracefully(connection, CloseReason.TOO_MANY_CONNECTIONS_TO_SEEDS)); } @@ -315,7 +315,7 @@ private void maybeCloseAgedConnections() { .filter(connection -> connection.getConnectionMetrics().getAge() > config.getMaxAge()) .peek(connection -> log.info("{} -> {}: Send CloseConnectionMessage as the connection age " + "is too old.", - node, connection.getPeersCapability().getAddress())) + node, connection.getPeerAddress())) .forEach(connection -> node.closeConnectionGracefully(connection, CloseReason.AGED_CONNECTION)); } @@ -327,7 +327,7 @@ private void maybeCloseExceedingInboundConnections() { .sorted(Connection.comparingDateDescending()) // As we use skip we sort by descending creationDate so that we close the oldest connections .skip(peerGroupService.getMaxInboundConnections()) .peek(connection -> log.info("{} -> {}: Send CloseConnectionMessage as we have too many inbound connections.", - node, connection.getPeersCapability().getAddress())) + node, connection.getPeerAddress())) .forEach(connection -> node.closeConnectionGracefully(connection, CloseReason.TOO_MANY_INBOUND_CONNECTIONS)); } @@ -339,7 +339,7 @@ private void maybeCloseExceedingConnections() { .sorted(Connection.comparingDateDescending()) // As we use skip we sort by descending creationDate so that we close the oldest connections .skip(peerGroupService.getMaxNumConnectedPeers()) .peek(connection -> log.info("{} -> {}: Send CloseConnectionMessage as we have too many connections.", - node, connection.getPeersCapability().getAddress())) + node, connection.getPeerAddress())) .forEach(connection -> node.closeConnectionGracefully(connection, CloseReason.TOO_MANY_CONNECTIONS)); } From 2c65a9c347db9ecd1d4aac68dfaabfda7234b8cd Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 12:35:16 +0700 Subject: [PATCH 028/119] Fix bug with not updated peers. We used the EqualsAndHashCode.Include at Capability. When we called add/addAll we only add items when the Capability would be different, which is normally not the case if just the created date changed. Thus we did not update the age of the peers. We make it now more explicit to set the address as the identity field and use a hashmap in the service to manage updates. We only update peers if the one to get added is newer. --- .../network/p2p/services/peergroup/Peer.java | 11 ++- .../services/peergroup/PeerGroupService.java | 68 ++++++++++++++++--- .../services/peergroup/PeerGroupStore.java | 33 +++++---- 3 files changed, 86 insertions(+), 26 deletions(-) diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/Peer.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/Peer.java index dd5a5821d1..74d7e5f5fa 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/Peer.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/Peer.java @@ -29,26 +29,35 @@ import javax.annotation.Nonnull; import java.util.Date; +/** + * We use only the address for EqualsAndHashCode as unique identifier as the other data can be + * updated and we do not consider those updates as a new peer instance. + */ @Getter @ToString @EqualsAndHashCode(onlyExplicitlyIncluded = true) public final class Peer implements NetworkProto, Comparable { @EqualsAndHashCode.Include + private final Address address; + private final Capability capability; private final NetworkLoad networkLoad; private final boolean isOutboundConnection; private final long created; + public Peer(Capability capability, NetworkLoad networkLoad, boolean isOutboundConnection) { this(capability, networkLoad, isOutboundConnection, System.currentTimeMillis()); } - public Peer(Capability capability, NetworkLoad networkLoad, boolean isOutboundConnection, long created) { + private Peer(Capability capability, NetworkLoad networkLoad, boolean isOutboundConnection, long created) { this.capability = capability; this.networkLoad = networkLoad; this.isOutboundConnection = isOutboundConnection; this.created = created; + address = capability.getAddress(); + verify(); } diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupService.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupService.java index 377b6844fe..a79e108a20 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupService.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupService.java @@ -29,8 +29,11 @@ import lombok.extern.slf4j.Slf4j; import java.util.Collection; +import java.util.HashSet; +import java.util.Map; import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Stream; /** @@ -77,8 +80,7 @@ public static Config from(com.typesafe.config.Config typesafeConfig) { @Getter private final Set
seedNodeAddresses; private final BanList banList; - @Getter - private final Set reportedPeers = new CopyOnWriteArraySet<>(); + private final Map reportedPeersByAddress = new ConcurrentHashMap<>(); public PeerGroupService(PersistenceService persistenceService, TransportType transportType, @@ -99,20 +101,54 @@ public PeerGroupService(PersistenceService persistenceService, // Persisted peers /////////////////////////////////////////////////////////////////////////////////////////////////// + public Map getPersistedPeersByAddress() { + return persistableStore.getPersistedPeersByAddress(); + } + public Set getPersistedPeers() { - return persistableStore.getPersistedPeers(); + return new HashSet<>(getPersistedPeersByAddress().values()); } - public void addPersistedPeers(Set peers) { - if (getPersistedPeers().addAll(peers)) { + public boolean addPersistedPeer(Peer peer) { + boolean wasAdded = doAddPersistedPeer(peer); + if (wasAdded) { persist(); } + return wasAdded; } - public void removePersistedPeers(Collection peers) { - if (getPersistedPeers().removeAll(peers)) { + private boolean doAddPersistedPeer(Peer peer) { + return doAddPeer(peer, getPersistedPeersByAddress()); + } + + private boolean doAddPeer(Peer peerToAdd, Map map) { + Address address = peerToAdd.getAddress(); + if (map.containsKey(address)) { + if (peerToAdd.getCreated() > map.get(address).getCreated()) { + map.put(address, peerToAdd); + return true; + } else { + return false; + } + } else { + map.put(address, peerToAdd); + return true; + } + } + + public boolean addPersistedPeers(Set peers) { + AtomicBoolean wasAdded = new AtomicBoolean(); + peers.forEach(peer -> wasAdded.set(doAddPersistedPeer(peer) || wasAdded.get())); + if (wasAdded.get()) { persist(); } + return wasAdded.get(); + } + + public void removePersistedPeers(Collection peers) { + Map persistedPeersById = getPersistedPeersByAddress(); + peers.forEach(peer -> persistedPeersById.remove(peer.getAddress())); + persist(); } @@ -120,12 +156,22 @@ public void removePersistedPeers(Collection peers) { // Reported peers /////////////////////////////////////////////////////////////////////////////////////////////////// - public void addReportedPeers(Set peers) { - reportedPeers.addAll(peers); + public Set getReportedPeers() { + return new HashSet<>(reportedPeersByAddress.values()); + } + + private boolean addReportedPeer(Peer peer) { + return doAddPeer(peer, reportedPeersByAddress); + } + + public boolean addReportedPeers(Set peers) { + AtomicBoolean wasAdded = new AtomicBoolean(); + peers.forEach(peer -> wasAdded.set(addReportedPeer(peer) || wasAdded.get())); + return wasAdded.get(); } public void removeReportedPeers(Collection peers) { - reportedPeers.removeAll(peers); + peers.forEach(peer -> reportedPeersByAddress.remove(peer.getAddress())); } diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupStore.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupStore.java index 1b27ba068e..4acd80e7eb 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupStore.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupStore.java @@ -19,39 +19,40 @@ import bisq.common.proto.ProtoResolver; import bisq.common.proto.UnresolvableProtobufMessageException; +import bisq.network.common.Address; import bisq.persistence.PersistableStore; import com.google.protobuf.InvalidProtocolBufferException; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -@Getter @Slf4j public final class PeerGroupStore implements PersistableStore { - private final Set persistedPeers = new CopyOnWriteArraySet<>(); + private final Map persistedPeersByAddress = new ConcurrentHashMap<>(); public PeerGroupStore() { } - private PeerGroupStore(Set persistedPeers) { - this.persistedPeers.addAll(persistedPeers); + private PeerGroupStore(Map persistedPeersByAddress) { + this.persistedPeersByAddress.putAll(persistedPeersByAddress); } @Override public bisq.network.protobuf.PeerGroupStore toProto() { - return bisq.network.protobuf.PeerGroupStore.newBuilder().addAllPersistedPeers(persistedPeers.stream() + return bisq.network.protobuf.PeerGroupStore.newBuilder().addAllPersistedPeers(persistedPeersByAddress.values().stream() .map(Peer::toProto) .collect(Collectors.toSet())) .build(); } public static PeerGroupStore fromProto(bisq.network.protobuf.PeerGroupStore proto) { - return new PeerGroupStore(proto.getPersistedPeersList().stream() - .map(Peer::fromProto).collect(Collectors.toSet())); + Map persistedPeersById = proto.getPersistedPeersList().stream() + .map(Peer::fromProto) + .collect(Collectors.toMap(Peer::getAddress, e -> e)); + return new PeerGroupStore(persistedPeersById); } @Override @@ -67,12 +68,16 @@ public ProtoResolver> getResolver() { @Override public PeerGroupStore getClone() { - return new PeerGroupStore(new HashSet<>(persistedPeers)); + return new PeerGroupStore(new HashMap<>(persistedPeersByAddress)); } @Override public void applyPersisted(PeerGroupStore persisted) { - persistedPeers.clear(); - persistedPeers.addAll(persisted.getPersistedPeers()); + persistedPeersByAddress.clear(); + persistedPeersByAddress.putAll(persisted.getPersistedPeersByAddress()); + } + + Map getPersistedPeersByAddress() { + return persistedPeersByAddress; } } \ No newline at end of file From a6f8e5ed0216f1dd743a3311ce369d3e1d574093 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 12:52:31 +0700 Subject: [PATCH 029/119] Cleanup. Replace getPeersAddress with getPeerAddress. Remove getPeersAddress --- .../src/main/java/bisq/network/p2p/node/Connection.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/network/network/src/main/java/bisq/network/p2p/node/Connection.java b/network/network/src/main/java/bisq/network/p2p/node/Connection.java index c40db22ab3..03d496f870 100644 --- a/network/network/src/main/java/bisq/network/p2p/node/Connection.java +++ b/network/network/src/main/java/bisq/network/p2p/node/Connection.java @@ -178,10 +178,6 @@ public boolean isRunning() { return !isStopped(); } - public Address getPeersAddress() { - return getPeersCapability().getAddress(); - } - public long getCreated() { return getConnectionMetrics().getCreated(); } @@ -195,7 +191,7 @@ public boolean createdBefore(long date) { } @Override public String toString() { - return "'" + getClass().getSimpleName() + " [peerAddress=" + getPeersAddress() + + return "'" + getClass().getSimpleName() + " [peerAddress=" + getPeerAddress() + ", keyId=" + getId() + "]'"; } @@ -302,7 +298,7 @@ boolean isStopped() { /////////////////////////////////////////////////////////////////////////////////////////////////// private String getThreadNameId() { - return StringUtils.truncate(getPeersAddress().toString() + "-" + id.substring(0, 8)); + return StringUtils.truncate(getPeerAddress().toString() + "-" + id.substring(0, 8)); } private boolean isInputStreamActive() { From 97225e6aecb4f0863de68daa78a55f5acfbeafe4 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 12:53:40 +0700 Subject: [PATCH 030/119] Use connection.createdBefore(maxAgeDate) instead of connection.getConnectionMetrics().getAge() > config.getMaxAge() --- .../bisq/network/p2p/services/peergroup/PeerGroupManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java index cfce6c07ff..8b52a955ee 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java @@ -310,9 +310,10 @@ private void maybeCloseConnectionsToSeeds() { private void maybeCloseAgedConnections() { log.debug("{} called maybeCloseAgedConnections", node); + long maxAgeDate = System.currentTimeMillis() - config.getMaxAge(); node.getAllActiveConnections() .filter(this::allowDisconnect) - .filter(connection -> connection.getConnectionMetrics().getAge() > config.getMaxAge()) + .filter(connection -> connection.createdBefore(maxAgeDate)) .peek(connection -> log.info("{} -> {}: Send CloseConnectionMessage as the connection age " + "is too old.", node, connection.getPeerAddress())) From 99aa3248653052a5a9af5dabc76cce00085ff9ee Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 20 Mar 2024 13:14:22 +0700 Subject: [PATCH 031/119] Cleanup, Renaming --- .../services/peergroup/PeerGroupManager.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java index 8b52a955ee..e9bf97dbcb 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/PeerGroupManager.java @@ -318,7 +318,6 @@ private void maybeCloseAgedConnections() { "is too old.", node, connection.getPeerAddress())) .forEach(connection -> node.closeConnectionGracefully(connection, CloseReason.AGED_CONNECTION)); - } private void maybeCloseExceedingInboundConnections() { @@ -330,7 +329,6 @@ private void maybeCloseExceedingInboundConnections() { .peek(connection -> log.info("{} -> {}: Send CloseConnectionMessage as we have too many inbound connections.", node, connection.getPeerAddress())) .forEach(connection -> node.closeConnectionGracefully(connection, CloseReason.TOO_MANY_INBOUND_CONNECTIONS)); - } private void maybeCloseExceedingConnections() { @@ -342,13 +340,11 @@ private void maybeCloseExceedingConnections() { .peek(connection -> log.info("{} -> {}: Send CloseConnectionMessage as we have too many connections.", node, connection.getPeerAddress())) .forEach(connection -> node.closeConnectionGracefully(connection, CloseReason.TOO_MANY_CONNECTIONS)); - } private void maybeCreateConnections() { log.debug("{} called maybeCreateConnections", node); int minNumConnectedPeers = peerGroupService.getMinNumConnectedPeers(); - // We want to have at least 40% of our minNumConnectedPeers as outbound connections if (getMissingOutboundConnections() <= 0) { // We have enough outbound connections, lets check if we have sufficient connections in total if (node.getNumConnections() >= minNumConnectedPeers) { @@ -370,9 +366,9 @@ private void maybeRemoveReportedPeers() { int exceeding = reportedPeers.size() - config.getMaxReported(); if (exceeding > 0) { reportedPeers.sort(Comparator.comparing(Peer::getDate)); - List candidates = reportedPeers.subList(0, Math.min(exceeding, reportedPeers.size())); - log.info("Remove {} reported peers: {}", candidates.size(), candidates); - peerGroupService.removeReportedPeers(candidates); + List outDated = reportedPeers.subList(0, Math.min(exceeding, reportedPeers.size())); + log.info("Remove {} reported peers: {}", outDated.size(), outDated); + peerGroupService.removeReportedPeers(outDated); } } @@ -381,9 +377,9 @@ private void maybeRemovePersistedPeers() { int exceeding = persistedPeers.size() - config.getMaxPersisted(); if (exceeding > 0) { persistedPeers.sort(Comparator.comparing(Peer::getDate)); - List candidates = persistedPeers.subList(0, Math.min(exceeding, persistedPeers.size())); - log.info("Remove {} persisted peers: {}", candidates.size(), candidates); - peerGroupService.removePersistedPeers(candidates); + List outDated = persistedPeers.subList(0, Math.min(exceeding, persistedPeers.size())); + log.info("Remove {} persisted peers: {}", outDated.size(), outDated); + peerGroupService.removePersistedPeers(outDated); } } @@ -425,5 +421,4 @@ private boolean isNotBootstrapping(Connection connection) { private int getMissingOutboundConnections() { return peerGroupService.getMinOutboundConnections() - (int) node.getActiveOutboundConnections().count(); } - } \ No newline at end of file From 42a57c4b49073274212e9e428749a99700bf7cdc Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Wed, 20 Mar 2024 07:55:23 +0100 Subject: [PATCH 032/119] Remove unnecessary line --- .../main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java index 0b9fbfe046..d9d2473fdb 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java @@ -508,7 +508,6 @@ private void updateSelectedMarketFilter(Filters.Markets marketFilter) { .map(menuItem -> (DropdownFilterMenuItem) menuItem) .forEach(menuItem -> menuItem.updateSelection(marketFilter == menuItem.getFilter())); - favouritesTableView.getSelectionModel().select(getModel().getSelectedMarketChannelItem().get()); marketsTableView.getSelectionModel().select(getModel().getSelectedMarketChannelItem().get()); } From 4e528b50b9a1cfc2555cb34b8e1718dff28eff76 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Wed, 20 Mar 2024 23:36:54 +0100 Subject: [PATCH 033/119] Add alert banner for official communications --- .../bisq/desktop/main/MainController.java | 25 ++-- .../main/java/bisq/desktop/main/MainView.java | 5 +- .../main/alert/AlertBannerController.java | 53 +++++++ .../desktop/main/alert/AlertBannerModel.java | 33 +++++ .../desktop/main/alert/AlertBannerView.java | 133 ++++++++++++++++++ .../notification/NotificationPanelView.java | 1 + .../src/main/resources/css/application.css | 32 ++++- 7 files changed, 266 insertions(+), 16 deletions(-) create mode 100644 apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java create mode 100644 apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java create mode 100644 apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java index 996a8e164d..997c686439 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java @@ -20,6 +20,7 @@ import bisq.application.ApplicationService; import bisq.bisq_easy.NavigationTarget; import bisq.bonded_roles.security_manager.alert.AlertService; +import bisq.bonded_roles.security_manager.alert.AlertType; import bisq.bonded_roles.security_manager.alert.AuthorizedAlertData; import bisq.common.observable.collection.CollectionObserver; import bisq.desktop.ServiceProvider; @@ -28,6 +29,7 @@ import bisq.desktop.common.view.Navigation; import bisq.desktop.common.view.NavigationController; import bisq.desktop.components.overlay.Popup; +import bisq.desktop.main.alert.AlertBannerController; import bisq.desktop.main.content.ContentController; import bisq.desktop.main.left.LeftNavController; import bisq.desktop.main.notification.NotificationPanelController; @@ -53,6 +55,7 @@ public class MainController extends NavigationController { private final SettingsService settingsService; private final UpdaterService updaterService; private final ApplicationService.Config config; + private final AlertBannerController alertBannerController; public MainController(ServiceProvider serviceProvider) { super(NavigationTarget.MAIN); @@ -66,11 +69,13 @@ public MainController(ServiceProvider serviceProvider) { leftNavController = new LeftNavController(serviceProvider); TopPanelController topPanelController = new TopPanelController(serviceProvider); NotificationPanelController notificationPanelController = new NotificationPanelController(serviceProvider); + alertBannerController = new AlertBannerController(); view = new MainView(model, this, leftNavController.getView().getRoot(), topPanelController.getView().getRoot(), - notificationPanelController.getView().getRoot()); + notificationPanelController.getView().getRoot(), + alertBannerController.getView().getRoot()); } @Override @@ -105,18 +110,12 @@ public void add(AuthorizedAlertData authorizedAlertData) { } settingsService.getConsumedAlertIds().add(authorizedAlertData.getId()); Optional optionalMessage = authorizedAlertData.getMessage(); - switch (authorizedAlertData.getAlertType()) { - case INFO: - optionalMessage.ifPresentOrElse(message -> new Popup().attention(message).show(), - () -> log.warn("optionalMessage not present")); - break; - case WARN: - case EMERGENCY: - optionalMessage.ifPresentOrElse(message -> new Popup().warning(message).show(), - () -> log.warn("optionalMessage not present")); - break; - case BAN: - break; + + if (optionalMessage.isPresent()) { + log.info("Showing alert with message {}", optionalMessage.get()); + alertBannerController.showAlert(optionalMessage.get(), authorizedAlertData.getAlertType()); + } else { + log.warn("optionalMessage not present"); } }); } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainView.java index 125f1099a9..adbe2827f7 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainView.java @@ -34,12 +34,13 @@ public MainView(MainModel model, MainController controller, AnchorPane leftNav, HBox topPanel, - BorderPane notificationPanel) { + BorderPane notificationPanel, + BorderPane alertBanner) { super(new HBox(), model, controller); anchorPane = new AnchorPane(); VBox.setVgrow(anchorPane, Priority.ALWAYS); - VBox vBox = new VBox(topPanel, notificationPanel, anchorPane); + VBox vBox = new VBox(topPanel, notificationPanel, alertBanner, anchorPane); vBox.setFillWidth(true); HBox.setHgrow(vBox, Priority.ALWAYS); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java new file mode 100644 index 0000000000..169f214abc --- /dev/null +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java @@ -0,0 +1,53 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.desktop.main.alert; + +import bisq.bonded_roles.security_manager.alert.AlertType; +import bisq.desktop.common.view.Controller; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class AlertBannerController implements Controller { + private final AlertBannerModel model; + @Getter + private final AlertBannerView view; + + public AlertBannerController() { + model = new AlertBannerModel(); + view = new AlertBannerView(model, this); + } + + @Override + public void onActivate() { + } + + @Override + public void onDeactivate() { + } + + public void showAlert(String message, AlertType alertType) { + model.getMessage().set(message); + model.getAlertType().set(alertType); + model.getIsAlertVisible().set(true); + } + + void onClose() { + model.getIsAlertVisible().set(false); + } +} diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java new file mode 100644 index 0000000000..f01312828a --- /dev/null +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java @@ -0,0 +1,33 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.desktop.main.alert; + +import bisq.bonded_roles.security_manager.alert.AlertType; +import bisq.desktop.common.view.Model; +import javafx.beans.property.*; +import lombok.Getter; + +@Getter +public class AlertBannerModel implements Model { + private final BooleanProperty isAlertVisible = new SimpleBooleanProperty(); + private final StringProperty message = new SimpleStringProperty(); + private final ObjectProperty alertType = new SimpleObjectProperty<>(); + + public AlertBannerModel() { + } +} diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java new file mode 100644 index 0000000000..9835a8055b --- /dev/null +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java @@ -0,0 +1,133 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.desktop.main.alert; + +import bisq.bonded_roles.security_manager.alert.AlertType; +import bisq.desktop.common.Transitions; +import bisq.desktop.common.view.View; +import bisq.desktop.components.containers.Spacer; +import bisq.desktop.components.controls.BisqIconButton; +import javafx.animation.Timeline; +import javafx.beans.value.ChangeListener; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.layout.*; +import lombok.extern.slf4j.Slf4j; +import org.fxmisc.easybind.EasyBind; +import org.fxmisc.easybind.Subscription; + +@Slf4j +public class AlertBannerView extends View { + public static final int DURATION = Transitions.DEFAULT_DURATION / 2; + + Label content = new Label(); + private final Button closeButton; + private final HBox banner; + private final ChangeListener contentHeightListener; + private Timeline slideInRightTimeline, slideOutTopTimeline; + private Subscription isVisiblePin, alertTypePin; + + public AlertBannerView(AlertBannerModel model, AlertBannerController controller) { + super(new BorderPane(), model, controller); + + root.setManaged(false); + root.setVisible(false); + + content.setWrapText(true); + + closeButton = BisqIconButton.createIconButton("close-white"); + + banner = new HBox(content, Spacer.fillHBox(), closeButton); + banner.setAlignment(Pos.TOP_CENTER); + banner.setPadding(new Insets(10, 10, 10, 15)); + + contentHeightListener = ((observable, oldValue, newValue) -> banner.setMinHeight(newValue.doubleValue() + 25)); // padding = 25 + + root.setCenter(banner); + root.setPadding(new Insets(20, 40, 20, 40)); + } + + @Override + protected void onViewAttached() { + content.textProperty().bind(model.getMessage()); + + isVisiblePin = EasyBind.subscribe(model.getIsAlertVisible(), isVisible -> { + if (slideInRightTimeline != null) { + slideInRightTimeline.stop(); + slideInRightTimeline = null; + root.setTranslateX(0); + root.setOpacity(1); + } + if (slideOutTopTimeline != null) { + slideOutTopTimeline.stop(); + slideOutTopTimeline = null; + root.setTranslateY(0); + root.setOpacity(0); + root.setManaged(false); + root.setVisible(false); + } + if (isVisible) { + root.setManaged(true); + root.setVisible(true); + root.setTranslateY(0); + slideInRightTimeline = Transitions.slideInRight(root, DURATION, () -> { + }); + } else { + root.setTranslateX(0); + root.setTranslateY(0); + slideOutTopTimeline = Transitions.slideAndFadeOutTop(root, DURATION, () -> { + root.setManaged(false); + root.setVisible(false); + }); + } + }); + + alertTypePin = EasyBind.subscribe(model.getAlertType(), this::addAlertTypeStyleClass); + + content.heightProperty().addListener(contentHeightListener); + + closeButton.setOnAction(e -> controller.onClose()); + } + + @Override + protected void onViewDetached() { + content.textProperty().unbind(); + + isVisiblePin.unsubscribe(); + alertTypePin.unsubscribe(); + + content.heightProperty().removeListener(contentHeightListener); + + closeButton.setOnAction(null); + } + + private void addAlertTypeStyleClass(AlertType alertType) { + banner.getStyleClass().clear(); + String alertTypeClass; + if (alertType == AlertType.INFO) { + alertTypeClass = "info-banner"; + } else if (alertType == AlertType.WARN) { + alertTypeClass = "warn-banner"; + } else { + alertTypeClass = "emergency-banner"; + } + banner.getStyleClass().addAll("alert-banner", alertTypeClass); + } +} diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/notification/NotificationPanelView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/notification/NotificationPanelView.java index 8c6245ae21..c5602a5f28 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/notification/NotificationPanelView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/notification/NotificationPanelView.java @@ -36,6 +36,7 @@ @Slf4j public class NotificationPanelView extends View { public static final int DURATION = Transitions.DEFAULT_DURATION / 2; + private final Label notificationHeadline; private final Button closeButton; private final Hyperlink hyperlink; diff --git a/apps/desktop/desktop/src/main/resources/css/application.css b/apps/desktop/desktop/src/main/resources/css/application.css index 2e3b8d2db4..06089c4dce 100644 --- a/apps/desktop/desktop/src/main/resources/css/application.css +++ b/apps/desktop/desktop/src/main/resources/css/application.css @@ -116,9 +116,39 @@ -fx-background-color: -bisq-dark-grey-20; } +/******************************************************************************* + * AlertBanner * + ******************************************************************************/ + +.alert-banner { + -fx-fill: -bisq-dark-grey-50; + -fx-text-fill: -bisq-dark-grey-50; + -fx-font-size: 1.15em; + -fx-font-family: "IBM Plex Sans"; +} + +.info-banner, +.warn-banner, +.emergency-banner { + -fx-background-radius: 8; + -fx-border-color: transparent; +} + +.info-banner { + -fx-background-color: -bisq-mid-grey-10; +} + +.warn-banner { + -fx-background-color: -bisq2-yellow-dim-50; +} + +.emergency-banner { + -fx-background-color: -bisq2-red-dim-50; +} + /******************************************************************************* - * NotificationPane * + * NotificationPane * ******************************************************************************/ .notification-pane { From 538b04aefc496149b7485801b6e4391b9e52692b Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Thu, 21 Mar 2024 10:34:32 +0700 Subject: [PATCH 034/119] Improve logs --- .../data/inventory/InventoryRequestService.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/network/network/src/main/java/bisq/network/p2p/services/data/inventory/InventoryRequestService.java b/network/network/src/main/java/bisq/network/p2p/services/data/inventory/InventoryRequestService.java index 0ffdc95a5f..57784359b5 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/data/inventory/InventoryRequestService.java +++ b/network/network/src/main/java/bisq/network/p2p/services/data/inventory/InventoryRequestService.java @@ -40,10 +40,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.concurrent.CancellationException; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; @@ -151,7 +148,12 @@ private void requestInventory() { .whenComplete((list, throwable) -> { requestsPending.set(false); if (throwable != null) { - log.error("requestFromPeers failed", throwable); + if (throwable instanceof CompletionException && + throwable.getCause() instanceof CancellationException) { + log.debug("requestFromPeers failed", throwable); + } else { + log.error("requestFromPeers failed", throwable); + } } else if (list == null) { log.error("requestFromPeers completed with result list = null"); } else { From 9f1cbe5e5c7854cab8ba93d4e929f62840c34586 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Thu, 21 Mar 2024 13:40:10 +0700 Subject: [PATCH 035/119] Move alertService observer code to AlertBannerController. Remove alert if data got removed. Show alert after restart if it was not dismissed by user. --- .../bisq/desktop/main/MainController.java | 39 +--------- .../main/alert/AlertBannerController.java | 77 +++++++++++++++++-- .../desktop/main/alert/AlertBannerModel.java | 11 +++ 3 files changed, 83 insertions(+), 44 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java index 997c686439..1deba25165 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java @@ -20,9 +20,6 @@ import bisq.application.ApplicationService; import bisq.bisq_easy.NavigationTarget; import bisq.bonded_roles.security_manager.alert.AlertService; -import bisq.bonded_roles.security_manager.alert.AlertType; -import bisq.bonded_roles.security_manager.alert.AuthorizedAlertData; -import bisq.common.observable.collection.CollectionObserver; import bisq.desktop.ServiceProvider; import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Controller; @@ -51,7 +48,6 @@ public class MainController extends NavigationController { private final MainView view; private final ServiceProvider serviceProvider; private final LeftNavController leftNavController; - private final AlertService alertService; private final SettingsService settingsService; private final UpdaterService updaterService; private final ApplicationService.Config config; @@ -62,14 +58,14 @@ public MainController(ServiceProvider serviceProvider) { this.serviceProvider = serviceProvider; settingsService = serviceProvider.getSettingsService(); - alertService = serviceProvider.getBondedRolesService().getAlertService(); + AlertService alertService = serviceProvider.getBondedRolesService().getAlertService(); updaterService = serviceProvider.getUpdaterService(); config = serviceProvider.getConfig(); leftNavController = new LeftNavController(serviceProvider); TopPanelController topPanelController = new TopPanelController(serviceProvider); NotificationPanelController notificationPanelController = new NotificationPanelController(serviceProvider); - alertBannerController = new AlertBannerController(); + alertBannerController = new AlertBannerController(settingsService, alertService); view = new MainView(model, this, leftNavController.getView().getRoot(), @@ -98,37 +94,6 @@ public void onActivate() { } } - alertService.getAuthorizedAlertDataSet().addObserver(new CollectionObserver<>() { - @Override - public void add(AuthorizedAlertData authorizedAlertData) { - if (authorizedAlertData == null) { - return; - } - UIThread.run(() -> { - if (settingsService.getConsumedAlertIds().contains(authorizedAlertData.getId())) { - return; - } - settingsService.getConsumedAlertIds().add(authorizedAlertData.getId()); - Optional optionalMessage = authorizedAlertData.getMessage(); - - if (optionalMessage.isPresent()) { - log.info("Showing alert with message {}", optionalMessage.get()); - alertBannerController.showAlert(optionalMessage.get(), authorizedAlertData.getAlertType()); - } else { - log.warn("optionalMessage not present"); - } - }); - } - - @Override - public void remove(Object element) { - } - - @Override - public void clear() { - } - }); - updaterService.getReleaseNotification().addObserver(releaseNotification -> { if (releaseNotification == null) { return; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java index 169f214abc..642fd2bf77 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java @@ -17,37 +17,100 @@ package bisq.desktop.main.alert; -import bisq.bonded_roles.security_manager.alert.AlertType; +import bisq.bonded_roles.security_manager.alert.AlertService; +import bisq.bonded_roles.security_manager.alert.AuthorizedAlertData; +import bisq.common.observable.Pin; +import bisq.common.observable.collection.CollectionObserver; +import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Controller; +import bisq.settings.SettingsService; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import java.util.Optional; + @Slf4j public class AlertBannerController implements Controller { private final AlertBannerModel model; @Getter private final AlertBannerView view; + private final SettingsService settingsService; + private final AlertService alertService; + private Pin authorizedAlertDataSetPin; - public AlertBannerController() { + public AlertBannerController(SettingsService settingsService, AlertService alertService) { + this.settingsService = settingsService; + this.alertService = alertService; model = new AlertBannerModel(); view = new AlertBannerView(model, this); } @Override public void onActivate() { + authorizedAlertDataSetPin = alertService.getAuthorizedAlertDataSet().addObserver(new CollectionObserver<>() { + @Override + public void add(AuthorizedAlertData authorizedAlertData) { + if (authorizedAlertData == null) { + return; + } + UIThread.run(() -> addAlert(authorizedAlertData)); + } + + @Override + public void remove(Object element) { + if (element instanceof AuthorizedAlertData) { + UIThread.run(() -> removeAlert((AuthorizedAlertData) element)); + } + } + + @Override + public void clear() { + UIThread.run(() -> removeAllAlerts()); + } + }); + + settingsService.getTradeRulesConfirmed().addObserver(e -> { + + }); } + @Override public void onDeactivate() { + authorizedAlertDataSetPin.unbind(); + } + + + private void addAlert(AuthorizedAlertData authorizedAlertData) { + if (settingsService.getConsumedAlertIds().contains(authorizedAlertData.getId())) { + model.getIsAlertVisible().set(false); + return; + } + + model.setDisplayedAuthorizedAlertData(authorizedAlertData); + Optional optionalMessage = authorizedAlertData.getMessage(); + + if (optionalMessage.isPresent()) { + log.info("Showing alert with message {}", optionalMessage.get()); + model.getMessage().set(authorizedAlertData.getMessage().orElseThrow()); + model.getAlertType().set(authorizedAlertData.getAlertType()); + model.getIsAlertVisible().set(true); + } else { + log.warn("optionalMessage not present"); + model.getIsAlertVisible().set(false); + } + } + + private void removeAlert(AuthorizedAlertData data) { + model.reset(); } - public void showAlert(String message, AlertType alertType) { - model.getMessage().set(message); - model.getAlertType().set(alertType); - model.getIsAlertVisible().set(true); + private void removeAllAlerts() { + model.reset(); } void onClose() { - model.getIsAlertVisible().set(false); + settingsService.getConsumedAlertIds().add(model.getDisplayedAuthorizedAlertData().getId()); + model.reset(); } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java index f01312828a..5a6ce07c03 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java @@ -18,16 +18,27 @@ package bisq.desktop.main.alert; import bisq.bonded_roles.security_manager.alert.AlertType; +import bisq.bonded_roles.security_manager.alert.AuthorizedAlertData; import bisq.desktop.common.view.Model; import javafx.beans.property.*; import lombok.Getter; +import lombok.Setter; @Getter public class AlertBannerModel implements Model { + @Setter + private AuthorizedAlertData displayedAuthorizedAlertData; private final BooleanProperty isAlertVisible = new SimpleBooleanProperty(); private final StringProperty message = new SimpleStringProperty(); private final ObjectProperty alertType = new SimpleObjectProperty<>(); public AlertBannerModel() { } + + void reset() { + displayedAuthorizedAlertData = null; + isAlertVisible.set(false); + message.set(null); + alertType.set(null); + } } From dd3826ab65f6bf6a7a552824d0a88fcb015d0f10 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Thu, 21 Mar 2024 15:43:58 +0700 Subject: [PATCH 036/119] Add headline to alerts Handle multiple alerts according to alert type. Higher priority alerts get preferred at display if multiple alerts are present. --- .../main/alert/AlertBannerController.java | 64 +++++++++++++------ .../desktop/main/alert/AlertBannerModel.java | 14 ++++ .../desktop/main/alert/AlertBannerView.java | 44 +++++++++---- .../SecurityManagerController.java | 5 +- .../SecurityManagerModel.java | 1 + .../security_manager/SecurityManagerView.java | 57 ++++++++++------- .../src/main/resources/css/application.css | 13 +++- .../alert/AuthorizedAlertData.java | 16 ++++- .../src/main/proto/bonded_roles.proto | 1 + .../main/resources/authorized_role.properties | 1 + .../SecurityManagerService.java | 2 + 11 files changed, 156 insertions(+), 62 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java index 642fd2bf77..6499d75edb 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java @@ -18,15 +18,18 @@ package bisq.desktop.main.alert; import bisq.bonded_roles.security_manager.alert.AlertService; +import bisq.bonded_roles.security_manager.alert.AlertType; import bisq.bonded_roles.security_manager.alert.AuthorizedAlertData; import bisq.common.observable.Pin; import bisq.common.observable.collection.CollectionObserver; import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Controller; import bisq.settings.SettingsService; +import javafx.collections.ListChangeListener; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import java.util.Comparator; import java.util.Optional; @Slf4j @@ -36,6 +39,7 @@ public class AlertBannerController implements Controller { private final AlertBannerView view; private final SettingsService settingsService; private final AlertService alertService; + private final ListChangeListener listChangeListener; private Pin authorizedAlertDataSetPin; public AlertBannerController(SettingsService settingsService, AlertService alertService) { @@ -43,55 +47,75 @@ public AlertBannerController(SettingsService settingsService, AlertService alert this.alertService = alertService; model = new AlertBannerModel(); view = new AlertBannerView(model, this); + + model.getSortedList().setComparator(Comparator.comparing(AuthorizedAlertData::getAlertType).reversed()); + + listChangeListener = change -> { + change.next(); + if (change.wasAdded()) { + AuthorizedAlertData newItem = model.getSortedList().get(0); + AuthorizedAlertData displayed = model.getDisplayedAuthorizedAlertData(); + if (displayed == null || newItem.getAlertType().ordinal() >= displayed.getAlertType().ordinal()) { + add(newItem); + } + } else if (change.wasRemoved()) { + change.getRemoved().stream() + .filter(e -> e.equals(model.getDisplayedAuthorizedAlertData())) + .findFirst() + .ifPresent(e -> handleRemove()); + } + }; } @Override public void onActivate() { + updatePredicate(); + authorizedAlertDataSetPin = alertService.getAuthorizedAlertDataSet().addObserver(new CollectionObserver<>() { @Override public void add(AuthorizedAlertData authorizedAlertData) { if (authorizedAlertData == null) { return; } - UIThread.run(() -> addAlert(authorizedAlertData)); + UIThread.run(() -> model.getObservableList().add(authorizedAlertData)); } @Override public void remove(Object element) { if (element instanceof AuthorizedAlertData) { - UIThread.run(() -> removeAlert((AuthorizedAlertData) element)); + UIThread.run(() -> model.getObservableList().remove((AuthorizedAlertData) element)); } } @Override public void clear() { - UIThread.run(() -> removeAllAlerts()); + UIThread.run(() -> model.getObservableList().clear()); } }); - settingsService.getTradeRulesConfirmed().addObserver(e -> { - - }); + model.getSortedList().addListener(listChangeListener); + model.getSortedList().stream().findFirst().ifPresent(this::add); } - @Override public void onDeactivate() { authorizedAlertDataSetPin.unbind(); + model.getSortedList().removeListener(listChangeListener); } + void onClose() { + settingsService.getConsumedAlertIds().add(model.getDisplayedAuthorizedAlertData().getId()); + updatePredicate(); + handleRemove(); + } - private void addAlert(AuthorizedAlertData authorizedAlertData) { - if (settingsService.getConsumedAlertIds().contains(authorizedAlertData.getId())) { - model.getIsAlertVisible().set(false); - return; - } - + private void add(AuthorizedAlertData authorizedAlertData) { model.setDisplayedAuthorizedAlertData(authorizedAlertData); Optional optionalMessage = authorizedAlertData.getMessage(); if (optionalMessage.isPresent()) { log.info("Showing alert with message {}", optionalMessage.get()); + model.getHeadline().set(authorizedAlertData.getHeadline().orElseThrow()); model.getMessage().set(authorizedAlertData.getMessage().orElseThrow()); model.getAlertType().set(authorizedAlertData.getAlertType()); model.getIsAlertVisible().set(true); @@ -101,16 +125,14 @@ private void addAlert(AuthorizedAlertData authorizedAlertData) { } } - private void removeAlert(AuthorizedAlertData data) { - model.reset(); - } - - private void removeAllAlerts() { + private void handleRemove() { model.reset(); + model.getSortedList().stream().findFirst().ifPresent(this::add); } - void onClose() { - settingsService.getConsumedAlertIds().add(model.getDisplayedAuthorizedAlertData().getId()); - model.reset(); + private void updatePredicate() { + model.getFilteredList().setPredicate(authorizedAlertData -> + !settingsService.getConsumedAlertIds().contains(authorizedAlertData.getId()) && + authorizedAlertData.getAlertType() != AlertType.BAN); } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java index 5a6ce07c03..f394904b87 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java @@ -21,14 +21,27 @@ import bisq.bonded_roles.security_manager.alert.AuthorizedAlertData; import bisq.desktop.common.view.Model; import javafx.beans.property.*; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import javafx.collections.transformation.SortedList; import lombok.Getter; import lombok.Setter; +import java.util.HashSet; +import java.util.Set; + @Getter public class AlertBannerModel implements Model { + private final ObservableList observableList = FXCollections.observableArrayList(); + private final FilteredList filteredList = new FilteredList<>(observableList); + private final SortedList sortedList = new SortedList<>(filteredList); + private final Set displayedAlerts = new HashSet<>(); + @Setter private AuthorizedAlertData displayedAuthorizedAlertData; private final BooleanProperty isAlertVisible = new SimpleBooleanProperty(); + private final StringProperty headline = new SimpleStringProperty(); private final StringProperty message = new SimpleStringProperty(); private final ObjectProperty alertType = new SimpleObjectProperty<>(); @@ -38,6 +51,7 @@ public AlertBannerModel() { void reset() { displayedAuthorizedAlertData = null; isAlertVisible.set(false); + headline.set(null); message.set(null); alertType.set(null); } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java index 9835a8055b..94bb1e65a0 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java @@ -28,7 +28,9 @@ import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.Label; -import javafx.scene.layout.*; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; import lombok.extern.slf4j.Slf4j; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; @@ -36,11 +38,13 @@ @Slf4j public class AlertBannerView extends View { public static final int DURATION = Transitions.DEFAULT_DURATION / 2; + private final VBox contentVBox; - Label content = new Label(); + Label headline = new Label(); + Label message = new Label(); private final Button closeButton; private final HBox banner; - private final ChangeListener contentHeightListener; + private final ChangeListener heightListener; private Timeline slideInRightTimeline, slideOutTopTimeline; private Subscription isVisiblePin, alertTypePin; @@ -50,15 +54,16 @@ public AlertBannerView(AlertBannerModel model, AlertBannerController controller) root.setManaged(false); root.setVisible(false); - content.setWrapText(true); + message.setWrapText(true); closeButton = BisqIconButton.createIconButton("close-white"); - banner = new HBox(content, Spacer.fillHBox(), closeButton); + contentVBox = new VBox(10, headline, message); + banner = new HBox(contentVBox, Spacer.fillHBox(), closeButton); banner.setAlignment(Pos.TOP_CENTER); banner.setPadding(new Insets(10, 10, 10, 15)); - contentHeightListener = ((observable, oldValue, newValue) -> banner.setMinHeight(newValue.doubleValue() + 25)); // padding = 25 + heightListener = ((observable, oldValue, newValue) -> banner.setMinHeight(contentVBox.getHeight() + 25)); // padding = 25 root.setCenter(banner); root.setPadding(new Insets(20, 40, 20, 40)); @@ -66,7 +71,8 @@ public AlertBannerView(AlertBannerModel model, AlertBannerController controller) @Override protected void onViewAttached() { - content.textProperty().bind(model.getMessage()); + headline.textProperty().bind(model.getHeadline()); + message.textProperty().bind(model.getMessage()); isVisiblePin = EasyBind.subscribe(model.getIsAlertVisible(), isVisible -> { if (slideInRightTimeline != null) { @@ -99,27 +105,37 @@ protected void onViewAttached() { } }); - alertTypePin = EasyBind.subscribe(model.getAlertType(), this::addAlertTypeStyleClass); + alertTypePin = EasyBind.subscribe(model.getAlertType(), alertType -> { + if (alertType != null) { + addAlertTypeStyleClass(alertType); + } + }); - content.heightProperty().addListener(contentHeightListener); + message.heightProperty().addListener(heightListener); closeButton.setOnAction(e -> controller.onClose()); } @Override protected void onViewDetached() { - content.textProperty().unbind(); + headline.textProperty().unbind(); + message.textProperty().unbind(); isVisiblePin.unsubscribe(); alertTypePin.unsubscribe(); - content.heightProperty().removeListener(contentHeightListener); + message.heightProperty().removeListener(heightListener); closeButton.setOnAction(null); } private void addAlertTypeStyleClass(AlertType alertType) { - banner.getStyleClass().clear(); + headline.getStyleClass().clear(); + headline.getStyleClass().add("alert-banner-headline"); + + message.getStyleClass().clear(); + message.getStyleClass().add("alert-banner-message"); + String alertTypeClass; if (alertType == AlertType.INFO) { alertTypeClass = "info-banner"; @@ -128,6 +144,8 @@ private void addAlertTypeStyleClass(AlertType alertType) { } else { alertTypeClass = "emergency-banner"; } - banner.getStyleClass().addAll("alert-banner", alertTypeClass); + + banner.getStyleClass().clear(); + banner.getStyleClass().add(alertTypeClass); } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerController.java index 5c20a5ce71..179300212b 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerController.java @@ -139,7 +139,8 @@ void onSendAlert() { Optional bannedRole = bondedRoleListItem == null ? Optional.empty() : Optional.ofNullable(bondedRoleListItem.getBondedRole().getAuthorizedBondedRole()); securityManagerService.publishAlert(model.getSelectedAlertType().get(), - StringUtils.toOptional(model.getMessage().get()), + StringUtils.toOptional(model.getHeadline().get()), + StringUtils.toOptional(message), model.getHaltTrading().get(), model.getRequireVersionForTrading().get(), StringUtils.toOptional(model.getMinVersion().get()), @@ -150,6 +151,7 @@ void onSendAlert() { new Popup().error(throwable).show(); } else { model.getSelectedAlertType().set(null); + model.getHeadline().set(null); model.getMessage().set(null); model.getHaltTrading().set(false); model.getRequireVersionForTrading().set(false); @@ -241,6 +243,7 @@ private void applySelectAlertType(AlertType alertType) { model.getHaltTrading().set(false); model.getRequireVersionForTrading().set(false); model.getMinVersion().set(null); + model.getHeadline().set(null); model.getMessage().set(null); break; } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerModel.java index 7d57e76e1d..b865de4334 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerModel.java @@ -42,6 +42,7 @@ public class SecurityManagerModel implements Model { private final ObservableList bondedRoleListItems = FXCollections.observableArrayList(); private final StringProperty actionButtonText = new SimpleStringProperty(); private final BooleanProperty actionButtonDisabled = new SimpleBooleanProperty(); + private final StringProperty headline = new SimpleStringProperty(); private final StringProperty message = new SimpleStringProperty(); private final StringProperty minVersion = new SimpleStringProperty(); private final BooleanProperty haltTrading = new SimpleBooleanProperty(); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerView.java index c5b9efae45..5c355acf8e 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerView.java @@ -63,7 +63,7 @@ public class SecurityManagerView extends View alertTypeSelection; private final AutoCompleteComboBox bondedRoleSelection; private final CheckBox haltTradingCheckBox, requireVersionForTradingCheckBox; @@ -78,24 +78,6 @@ public SecurityManagerView(SecurityManagerModel model, SecurityManagerController root.setPadding(new Insets(0, 40, 40, 40)); root.setAlignment(Pos.TOP_LEFT); - // difficultyAdjustment - Label difficultyAdjustmentHeadline = new Label(Res.get("authorizedRole.securityManager.difficultyAdjustment.headline")); - difficultyAdjustmentHeadline.getStyleClass().add("large-thin-headline"); - - difficultyAdjustmentFactor = new MaterialTextField(Res.get("authorizedRole.securityManager.difficultyAdjustment.description")); - difficultyAdjustmentFactor.setMaxWidth(400); - difficultyAdjustmentFactor.setValidators(DIFFICULTY_ADJUSTMENT_FACTOR_VALIDATOR); - - difficultyAdjustmentButton = new Button(Res.get("authorizedRole.securityManager.difficultyAdjustment.button")); - difficultyAdjustmentButton.setDefaultButton(true); - - Label difficultyAdjustmentTableHeadline = new Label(Res.get("authorizedRole.securityManager.difficultyAdjustment.table.headline")); - difficultyAdjustmentTableHeadline.getStyleClass().add("large-thin-headline"); - - difficultyAdjustmentTableView = new BisqTableView<>(model.getDifficultyAdjustmentListItems()); - difficultyAdjustmentTableView.setFixHeight(200); - difficultyAdjustmentTableView.getStyleClass().add("user-bonded-roles-table-view"); - configDifficultyAdjustmentTableView(); // alerts Label alertHeadline = new Label(Res.get("authorizedRole.securityManager.alert.headline")); @@ -129,6 +111,7 @@ public BondedRoleListItem fromString(String string) { } }); + headline = new MaterialTextField(Res.get("authorizedRole.securityManager.alert.message.headline")); message = new MaterialTextArea(Res.get("authorizedRole.securityManager.alert.message")); haltTradingCheckBox = new CheckBox(Res.get("authorizedRole.securityManager.emergency.haltTrading")); @@ -152,6 +135,27 @@ public BondedRoleListItem fromString(String string) { alertTableView.getStyleClass().add("user-bonded-roles-table-view"); configAlertTableView(); + + // difficultyAdjustment + Label difficultyAdjustmentHeadline = new Label(Res.get("authorizedRole.securityManager.difficultyAdjustment.headline")); + difficultyAdjustmentHeadline.getStyleClass().add("large-thin-headline"); + + difficultyAdjustmentFactor = new MaterialTextField(Res.get("authorizedRole.securityManager.difficultyAdjustment.description")); + difficultyAdjustmentFactor.setMaxWidth(400); + difficultyAdjustmentFactor.setValidators(DIFFICULTY_ADJUSTMENT_FACTOR_VALIDATOR); + + difficultyAdjustmentButton = new Button(Res.get("authorizedRole.securityManager.difficultyAdjustment.button")); + difficultyAdjustmentButton.setDefaultButton(true); + + Label difficultyAdjustmentTableHeadline = new Label(Res.get("authorizedRole.securityManager.difficultyAdjustment.table.headline")); + difficultyAdjustmentTableHeadline.getStyleClass().add("large-thin-headline"); + + difficultyAdjustmentTableView = new BisqTableView<>(model.getDifficultyAdjustmentListItems()); + difficultyAdjustmentTableView.setFixHeight(200); + difficultyAdjustmentTableView.getStyleClass().add("user-bonded-roles-table-view"); + configDifficultyAdjustmentTableView(); + + // Role info roleInfo.setPadding(new Insets(0)); VBox.setMargin(difficultyAdjustmentButton, new Insets(0, 0, 10, 0)); @@ -162,14 +166,16 @@ public BondedRoleListItem fromString(String string) { VBox.setVgrow(difficultyAdjustmentTableView, Priority.NEVER); VBox.setVgrow(alertTableView, Priority.NEVER); this.root.getChildren().addAll( - difficultyAdjustmentHeadline, difficultyAdjustmentFactor, difficultyAdjustmentButton, - difficultyAdjustmentTableHeadline, difficultyAdjustmentTableView, alertHeadline, - alertTypeSelection, message, + alertTypeSelection, headline, message, haltTradingCheckBox, requireVersionForTradingHBox, bondedRoleSelection, sendAlertButton, alertTableHeadline, alertTableView, + + difficultyAdjustmentHeadline, difficultyAdjustmentFactor, difficultyAdjustmentButton, + difficultyAdjustmentTableHeadline, difficultyAdjustmentTableView, + roleInfo); } @@ -188,6 +194,10 @@ protected void onViewAttached() { bondedRoleSelection.visibleProperty().bind(model.getSelectedAlertType().isEqualTo(AlertType.BAN)); bondedRoleSelection.managedProperty().bind(bondedRoleSelection.visibleProperty()); + headline.textProperty().bindBidirectional(model.getHeadline()); + headline.visibleProperty().bind(bondedRoleSelection.visibleProperty().not()); + headline.managedProperty().bind(headline.visibleProperty()); + message.textProperty().bindBidirectional(model.getMessage()); message.visibleProperty().bind(bondedRoleSelection.visibleProperty().not()); message.managedProperty().bind(message.visibleProperty()); @@ -236,6 +246,9 @@ protected void onViewDetached() { bondedRoleSelection.visibleProperty().unbind(); bondedRoleSelection.managedProperty().unbind(); + headline.textProperty().unbindBidirectional(model.getHeadline()); + headline.visibleProperty().unbind(); + headline.managedProperty().unbind(); message.textProperty().unbindBidirectional(model.getMessage()); message.visibleProperty().unbind(); diff --git a/apps/desktop/desktop/src/main/resources/css/application.css b/apps/desktop/desktop/src/main/resources/css/application.css index 06089c4dce..21163408b3 100644 --- a/apps/desktop/desktop/src/main/resources/css/application.css +++ b/apps/desktop/desktop/src/main/resources/css/application.css @@ -120,9 +120,16 @@ * AlertBanner * ******************************************************************************/ -.alert-banner { - -fx-fill: -bisq-dark-grey-50; - -fx-text-fill: -bisq-dark-grey-50; +.alert-banner-headline { + -fx-fill: -fx-light-text-color; + -fx-text-fill: -fx-light-text-color; + -fx-font-size: 2em; + -fx-font-family: "IBM Plex Sans Light"; +} + +.alert-banner-message { + -fx-fill: -fx-light-text-color; + -fx-text-fill: -fx-light-text-color; -fx-font-size: 1.15em; -fx-font-family: "IBM Plex Sans"; } diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AuthorizedAlertData.java b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AuthorizedAlertData.java index 8f93001419..d520e099eb 100644 --- a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AuthorizedAlertData.java +++ b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AuthorizedAlertData.java @@ -23,6 +23,7 @@ import bisq.common.proto.ProtoResolver; import bisq.common.proto.UnresolvableProtobufMessageException; import bisq.common.validation.NetworkDataValidation; +import bisq.i18n.Res; import bisq.network.p2p.services.data.storage.DistributedData; import bisq.network.p2p.services.data.storage.MetaData; import bisq.network.p2p.services.data.storage.auth.authorized.AuthorizedDistributedData; @@ -49,6 +50,7 @@ public final class AuthorizedAlertData implements AuthorizedDistributedData { private final String id; private final long date; private final AlertType alertType; + private final Optional headline; private final Optional message; private final boolean haltTrading; private final boolean requireVersionForTrading; @@ -60,6 +62,7 @@ public final class AuthorizedAlertData implements AuthorizedDistributedData { public AuthorizedAlertData(String id, long date, AlertType alertType, + Optional headline, Optional message, boolean haltTrading, boolean requireVersionForTrading, @@ -70,6 +73,7 @@ public AuthorizedAlertData(String id, this.id = id; this.date = date; this.alertType = alertType; + this.headline = headline; this.message = message; this.haltTrading = haltTrading; this.requireVersionForTrading = requireVersionForTrading; @@ -101,22 +105,30 @@ public bisq.bonded_roles.protobuf.AuthorizedAlertData toProto() { .setSecurityManagerProfileId(securityManagerProfileId) .setStaticPublicKeysProvided(staticPublicKeysProvided); message.ifPresent(builder::setMessage); + headline.ifPresent(builder::setHeadline); minVersion.ifPresent(builder::setMinVersion); bannedRole.ifPresent(authorizedBondedRole -> builder.setBannedRole(authorizedBondedRole.toProto())); return builder.build(); } public static AuthorizedAlertData fromProto(bisq.bonded_roles.protobuf.AuthorizedAlertData proto) { + AlertType alertType = AlertType.fromProto(proto.getAlertType()); return new AuthorizedAlertData(proto.getId(), proto.getDate(), - AlertType.fromProto(proto.getAlertType()), + alertType, + proto.hasHeadline() ? Optional.of(proto.getHeadline()) : getDefaultHeadline(alertType), proto.hasMessage() ? Optional.of(proto.getMessage()) : Optional.empty(), proto.getHaltTrading(), proto.getRequireVersionForTrading(), proto.hasMinVersion() ? Optional.of(proto.getMinVersion()) : Optional.empty(), proto.hasBannedRole() ? Optional.of(AuthorizedBondedRole.fromProto(proto.getBannedRole())) : Optional.empty(), proto.getSecurityManagerProfileId(), - proto.getStaticPublicKeysProvided()); + proto.getStaticPublicKeysProvided() + ); + } + + private static Optional getDefaultHeadline(AlertType alertType) { + return Optional.of(Res.get("authorizedRole.securityManager.alertType." + alertType.name())); } public static ProtoResolver getResolver() { diff --git a/bonded-roles/src/main/proto/bonded_roles.proto b/bonded-roles/src/main/proto/bonded_roles.proto index 68c8964b82..5be159eec2 100644 --- a/bonded-roles/src/main/proto/bonded_roles.proto +++ b/bonded-roles/src/main/proto/bonded_roles.proto @@ -69,6 +69,7 @@ message AuthorizedAlertData { optional AuthorizedBondedRole bannedRole = 8; string securityManagerProfileId = 9; bool staticPublicKeysProvided = 10; + optional string headline = 11; } message ReleaseNotification { diff --git a/i18n/src/main/resources/authorized_role.properties b/i18n/src/main/resources/authorized_role.properties index 290ad7b99a..5424252fd2 100644 --- a/i18n/src/main/resources/authorized_role.properties +++ b/i18n/src/main/resources/authorized_role.properties @@ -102,6 +102,7 @@ authorizedRole.securityManager.difficultyAdjustment.table.value=Value authorizedRole.securityManager.alert.headline=Management of alerts authorizedRole.securityManager.selectAlertType=Select alert type +authorizedRole.securityManager.alert.message.headline=Headline authorizedRole.securityManager.alert.message=Alert message (max 1000 characters) authorizedRole.securityManager.alert.message.tooLong=Alert message is longer than 1000 characters authorizedRole.securityManager.emergency.haltTrading=Halt trading diff --git a/support/src/main/java/bisq/support/security_manager/SecurityManagerService.java b/support/src/main/java/bisq/support/security_manager/SecurityManagerService.java index 4e33ec6c56..42439004fe 100644 --- a/support/src/main/java/bisq/support/security_manager/SecurityManagerService.java +++ b/support/src/main/java/bisq/support/security_manager/SecurityManagerService.java @@ -95,6 +95,7 @@ public CompletableFuture shutdown() { /////////////////////////////////////////////////////////////////////////////////////////////////// public CompletableFuture publishAlert(AlertType alertType, + Optional headline, Optional message, boolean haltTrading, boolean requireVersionForTrading, @@ -108,6 +109,7 @@ public CompletableFuture publishAlert(AlertType alertType, AuthorizedAlertData authorizedAlertData = new AuthorizedAlertData(StringUtils.createUid(), new Date().getTime(), alertType, + headline, message, haltTrading, requireVersionForTrading, From fbe9d6e7b33160e083958df3a7a809d16fa46be4 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Thu, 21 Mar 2024 17:21:22 +0700 Subject: [PATCH 037/119] Don't use the currency post fix when copying the base or quote amount in the trade process. --- .../bisq_easy/open_trades/trade_state/states/BaseState.java | 6 ++++++ .../open_trades/trade_state/states/BuyerState2a.java | 3 +++ .../open_trades/trade_state/states/SellerState3a.java | 3 +++ 3 files changed, 12 insertions(+) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/states/BaseState.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/states/BaseState.java index 410e0e784e..64ab55ba7c 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/states/BaseState.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/states/BaseState.java @@ -78,7 +78,9 @@ public void onActivate() { long baseSideAmount = model.getBisqEasyTrade().getContract().getBaseSideAmount(); long quoteSideAmount = model.getBisqEasyTrade().getContract().getQuoteSideAmount(); + model.setBaseAmount(AmountFormatter.formatAmount(Coin.asBtcFromValue(baseSideAmount), false)); model.setFormattedBaseAmount(AmountFormatter.formatAmountWithCode(Coin.asBtcFromValue(baseSideAmount), false)); + model.setQuoteAmount(AmountFormatter.formatAmount(Fiat.from(quoteSideAmount, bisqEasyOffer.getMarket().getQuoteCurrencyCode()))); model.setFormattedQuoteAmount(AmountFormatter.formatAmountWithCode(Fiat.from(quoteSideAmount, bisqEasyOffer.getMarket().getQuoteCurrencyCode()))); } @@ -106,8 +108,12 @@ protected static class Model implements bisq.desktop.common.view.Model { @Setter protected String quoteCode; @Setter + protected String baseAmount; + @Setter protected String formattedBaseAmount; @Setter + protected String quoteAmount; + @Setter protected String formattedQuoteAmount; protected Model(BisqEasyTrade bisqEasyTrade, BisqEasyOpenTradeChannel channel) { diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/states/BuyerState2a.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/states/BuyerState2a.java index f6d2fc05d8..fd9ec36f9d 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/states/BuyerState2a.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/states/BuyerState2a.java @@ -19,6 +19,7 @@ import bisq.chat.bisqeasy.open_trades.BisqEasyOpenTradeChannel; import bisq.desktop.ServiceProvider; +import bisq.desktop.common.utils.ClipboardUtil; import bisq.desktop.components.controls.MaterialTextArea; import bisq.desktop.components.controls.MaterialTextField; import bisq.desktop.components.controls.WrappingText; @@ -114,6 +115,7 @@ protected void onViewAttached() { headline.setText(Res.get("bisqEasy.tradeState.info.buyer.phase2a.headline", model.getFormattedQuoteAmount())); quoteAmount.setText(model.getFormattedQuoteAmount()); + quoteAmount.getIconButton().setOnAction(e -> ClipboardUtil.copyToClipboard(model.getQuoteAmount())); account.setText(model.getBisqEasyTrade().getPaymentAccountData().get()); confirmFiatSentButton.setText(Res.get("bisqEasy.tradeState.info.buyer.phase2a.confirmFiatSent", model.getFormattedQuoteAmount())); confirmFiatSentButton.setOnAction(e -> controller.onConfirmFiatSent()); @@ -124,6 +126,7 @@ protected void onViewDetached() { super.onViewDetached(); confirmFiatSentButton.setOnAction(null); + quoteAmount.getIconButton().setOnAction(null); } } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/states/SellerState3a.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/states/SellerState3a.java index 1c64c1419c..622c9c12a2 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/states/SellerState3a.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/states/SellerState3a.java @@ -20,6 +20,7 @@ import bisq.chat.bisqeasy.open_trades.BisqEasyOpenTradeChannel; import bisq.common.data.Pair; import bisq.desktop.ServiceProvider; +import bisq.desktop.common.utils.ClipboardUtil; import bisq.desktop.components.controls.MaterialTextField; import bisq.desktop.components.controls.WrappingText; import bisq.i18n.Res; @@ -136,6 +137,7 @@ protected void onViewAttached() { super.onViewAttached(); baseAmount.setText(model.getFormattedBaseAmount()); + baseAmount.getIconButton().setOnAction(e -> ClipboardUtil.copyToClipboard(model.getBaseAmount())); btcAddress.setText(model.getBtcAddress()); btcSentButton.setText(Res.get("bisqEasy.tradeState.info.seller.phase3a.btcSentButton", model.getFormattedBaseAmount())); txId.textProperty().bindBidirectional(model.getTxId()); @@ -151,6 +153,7 @@ protected void onViewDetached() { txId.textProperty().unbindBidirectional(model.getTxId()); btcSentButton.setOnAction(null); + baseAmount.getIconButton().setOnAction(null); btcSentButton.disableProperty().unbind(); } } From bd86990541b0ab648fc307230af86b982ef39688 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Thu, 21 Mar 2024 17:08:50 +0700 Subject: [PATCH 038/119] If video cannot be played we open the link to https://bisq.network/bisq-easy or if the user has previously decided to not open links in the browser we show a popup. Improve handling of hyperlinks: - Add tooltip - Wrap links in ' Improve Browser.open calls: - Show notification popup to give feedback that link got copied, if user has previously decided to not open links in the browser we show a popup. --- .../java/bisq/desktop/common/Browser.java | 19 +++++++++++++-- .../desktop/components/overlay/Overlay.java | 20 ++++++++++++++-- .../video/BisqEasyVideoController.java | 23 +++++++++++++++++++ .../onboarding/video/BisqEasyVideoView.java | 11 ++++----- .../java/bisq/common/util/StringUtils.java | 2 +- .../src/main/resources/application.properties | 10 ++++---- 6 files changed, 70 insertions(+), 15 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/common/Browser.java b/apps/desktop/desktop/src/main/java/bisq/desktop/common/Browser.java index 8e7fc0d334..c695b1d07a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/common/Browser.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/common/Browser.java @@ -18,6 +18,7 @@ package bisq.desktop.common; import bisq.common.util.OsUtils; +import bisq.desktop.common.threading.UIScheduler; import bisq.desktop.common.utils.ClipboardUtil; import bisq.desktop.components.overlay.Popup; import bisq.i18n.Res; @@ -33,6 +34,8 @@ @Slf4j public class Browser { + public static final String HYPERLINKS_OPEN_IN_BROWSER = "hyperlinks.openInBrowser"; + @Nullable private static HostServices hostServices; private static SettingsService settingsService; @@ -43,9 +46,10 @@ public static void initialize(HostServices hostServices, SettingsService setting } public static void open(String url) { - String id = "hyperlinks.openInBrowser"; + String id = HYPERLINKS_OPEN_IN_BROWSER; if (DontShowAgainService.showAgain(id)) { - new Popup().feedback(Res.get("hyperlinks.openInBrowser.attention", url)) + new Popup().headline(Res.get("hyperlinks.openInBrowser.attention.headline")) + .feedback(Res.get("hyperlinks.openInBrowser.attention", url)) .closeButtonText(Res.get("hyperlinks.openInBrowser.no")) .onClose(() -> { settingsService.setCookie(CookieKey.PERMIT_OPENING_BROWSER, false); @@ -62,9 +66,20 @@ public static void open(String url) { doOpen(url); } else { ClipboardUtil.copyToClipboard(url); + + // TODO create custom popup style and animation similar like Bisq1 notifications + // See https://github.com/bisq-network/bisq2/issues/1883 + Popup popup = new Popup().notify(Res.get("hyperlinks.copiedToClipboard")); + popup.show(); + UIScheduler.run(popup::hide).after(3000); } } + public static boolean hyperLinksGetCopiesWithoutPopup() { + return !DontShowAgainService.showAgain(Browser.HYPERLINKS_OPEN_IN_BROWSER) && + !settingsService.getCookie().asBoolean(CookieKey.PERMIT_OPENING_BROWSER).orElse(false); + } + private static void doOpen(String url) { checkNotNull(hostServices, "hostServices must be set before doOpen is called"); try { diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/overlay/Overlay.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/overlay/Overlay.java index 679c76228f..52c04371b8 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/overlay/Overlay.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/components/overlay/Overlay.java @@ -29,6 +29,7 @@ import bisq.desktop.common.utils.ClipboardUtil; import bisq.desktop.components.containers.BisqGridPane; import bisq.desktop.components.containers.Spacer; +import bisq.desktop.components.controls.BisqTooltip; import bisq.desktop.components.controls.BusyAnimation; import bisq.i18n.Res; import bisq.settings.DontShowAgainService; @@ -109,7 +110,9 @@ public enum AnimationType { public enum Type { UNDEFINED(AnimationType.ScaleFromCenter), - NOTIFICATION(AnimationType.SlideFromRightTop), + // NOTIFICATION(AnimationType.SlideFromRightTop), + // TODO https://github.com/bisq-network/bisq2/issues/1883 + NOTIFICATION(AnimationType.SlideDownFromCenterTop, Transitions.Type.LIGHT_BLUR_LIGHT), BACKGROUND_INFO(AnimationType.SlideDownFromCenterTop), FEEDBACK(AnimationType.SlideDownFromCenterTop), @@ -327,6 +330,12 @@ public T backgroundInfo(String message) { return cast(); } + public T notify(String message) { + type = Type.NOTIFICATION; + processMessage(message); + return cast(); + } + public T feedback(String message) { type = Type.FEEDBACK; if (headline == null) @@ -798,12 +807,15 @@ protected void applyStyles() { headlineIcon.setVisible(true); headlineLabel.getStyleClass().add("overlay-headline"); switch (type) { + case NOTIFICATION: + headlineIcon.setManaged(false); + headlineIcon.setVisible(false); + break; case INFORMATION: case BACKGROUND_INFO: case INSTRUCTION: case CONFIRMATION: case FEEDBACK: - case NOTIFICATION: case ATTENTION: Icons.getIconForLabel(AwesomeIcon.INFO_SIGN, headlineIcon, "1.8em"); headlineLabel.getStyleClass().add("overlay-headline-information"); @@ -899,6 +911,10 @@ protected void addFooter() { Hyperlink link = new Hyperlink(messageHyperlinks.get(i)); link.getStyleClass().add("overlay-message"); link.setOnAction(event -> Browser.open(link.getText())); + String tooltipText = Browser.hyperLinksGetCopiesWithoutPopup() + ? Res.get("popup.hyperlink.copy.tooltip", link.getText()) + : Res.get("popup.hyperlink.openInBrowser.tooltip", link.getText()); + link.setTooltip(new BisqTooltip(tooltipText)); HBox hBox = new HBox(5, enumeration, link); hBox.setAlignment(Pos.CENTER_LEFT); footerBox.getChildren().addAll(hBox); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/onboarding/video/BisqEasyVideoController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/onboarding/video/BisqEasyVideoController.java index 6b573396a0..20e2dbcc3e 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/onboarding/video/BisqEasyVideoController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/onboarding/video/BisqEasyVideoController.java @@ -18,8 +18,12 @@ package bisq.desktop.main.content.bisq_easy.onboarding.video; import bisq.desktop.ServiceProvider; +import bisq.desktop.common.Browser; +import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Controller; +import bisq.desktop.components.overlay.Popup; import bisq.desktop.overlay.OverlayController; +import bisq.i18n.Res; import bisq.settings.CookieKey; import bisq.settings.SettingsService; import lombok.Getter; @@ -53,4 +57,23 @@ void onClose() { void onCompleted() { settingsService.setCookie(CookieKey.BISQ_EASY_VIDEO_OPENED, true); } + + public void onHandleVideoPlayerError(Exception e) { + UIThread.runOnNextRenderFrame(this::onClose); + + // If OS does not support mp4 we get an exception + log.warn("mp4 not supported", e); + + String videoUrl = "https://bisq.network/bisq-easy"; + if (Browser.hyperLinksGetCopiesWithoutPopup()) { + // User has set don't show again flag for popup and set to not open browser. + // We would only copy the link but user might be confused that nothing visually happened, + // so we show a popup. + new Popup().headline(Res.get("video.mp4NotSupported.warning.headline")) + .warning(Res.get("video.mp4NotSupported.warning", videoUrl)) + .show(); + } else { + Browser.open(videoUrl); + } + } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/onboarding/video/BisqEasyVideoView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/onboarding/video/BisqEasyVideoView.java index 44286b7c2d..977813c60f 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/onboarding/video/BisqEasyVideoView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/onboarding/video/BisqEasyVideoView.java @@ -18,7 +18,6 @@ package bisq.desktop.main.content.bisq_easy.onboarding.video; import bisq.common.data.Pair; -import bisq.common.util.ExceptionUtil; import bisq.desktop.common.Transitions; import bisq.desktop.common.threading.UIScheduler; import bisq.desktop.common.utils.ImageUtil; @@ -26,7 +25,6 @@ import bisq.desktop.common.view.View; import bisq.desktop.components.containers.Spacer; import bisq.desktop.components.controls.BisqIconButton; -import bisq.desktop.components.overlay.Popup; import bisq.desktop.overlay.OverlayController; import bisq.i18n.Res; import bisq.presentation.formatters.TimeFormatter; @@ -55,6 +53,8 @@ import org.fxmisc.easybind.Subscription; import org.fxmisc.easybind.monadic.MonadicBinding; +import javax.annotation.Nullable; + /** * This view is not following strictly the MVC patters (doing too much in the View). Reason is that lot of the relevant data * comes from the MediaPlayer and animations. But could be considered to clean that all up... @@ -68,6 +68,7 @@ public class BisqEasyVideoView extends View hyperlinks) while (matcher.find()) { String link = matcher.group(1); hyperlinks.add(link); - message = message.replaceFirst(pattern.toString(), String.format("%s [%d]", link, hyperlinks.size())); + message = message.replaceFirst(pattern.toString(), String.format("'%s' [%d]", link, hyperlinks.size())); } return message; } diff --git a/i18n/src/main/resources/application.properties b/i18n/src/main/resources/application.properties index 6ae04d81d9..8ce81d82a6 100644 --- a/i18n/src/main/resources/application.properties +++ b/i18n/src/main/resources/application.properties @@ -236,19 +236,21 @@ popup.startup.error=An error occurred at initializing Bisq: {0}. popup.shutdown=Shut down is in process.\n\n\ It might take up to {0} seconds until shut down is completed. popup.shutdown.error=An error occurred at shut down: {0}. +popup.hyperlink.openInBrowser.tooltip=Open link in browser: {0}. +popup.hyperlink.copy.tooltip=Copy link: {0}. #################################################################### # Messages #################################################################### - +hyperlinks.openInBrowser.attention.headline=Open web link hyperlinks.openInBrowser.attention=Do you want to open the link to `{0}` in your default web browser? hyperlinks.openInBrowser.no=No, copy link +hyperlinks.copiedToClipboard=Link was copied to clipboard #################################################################### # Video #################################################################### - -video.mp4NotSupported.warning=Your operating system does not support mp4 video format.\n\n\ - Reported error: {0} +video.mp4NotSupported.warning.headline=Embedded video cannot be played +video.mp4NotSupported.warning=You can watch the video in your browser at: [HYPERLINK:{0}] From 6546d638dac316ab565ed3e1c69e02f75d274607 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Thu, 21 Mar 2024 22:57:24 +0100 Subject: [PATCH 039/119] Introduce Bisq broadcast icon --- .../bisq/desktop/main/alert/AlertBannerView.java | 8 ++++++++ .../src/main/resources/css/application.css | 4 ++-- .../desktop/src/main/resources/css/images.css | 4 ++++ .../images/icons/bisq-broadcast-white.png | Bin 0 -> 1283 bytes 4 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 apps/desktop/desktop/src/main/resources/images/icons/bisq-broadcast-white.png diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java index 94bb1e65a0..751f928ded 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java @@ -19,6 +19,7 @@ import bisq.bonded_roles.security_manager.alert.AlertType; import bisq.desktop.common.Transitions; +import bisq.desktop.common.utils.ImageUtil; import bisq.desktop.common.view.View; import bisq.desktop.components.containers.Spacer; import bisq.desktop.components.controls.BisqIconButton; @@ -28,6 +29,7 @@ import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.Label; +import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; @@ -58,6 +60,12 @@ public AlertBannerView(AlertBannerModel model, AlertBannerController controller) closeButton = BisqIconButton.createIconButton("close-white"); + ImageView broadcastIcon = ImageUtil.getImageViewById("bisq-broadcast-white"); + headline.setGraphic(broadcastIcon); + headline.setGraphicTextGap(20); + + message.setPadding(new Insets(0, 0, 0, 5)); + contentVBox = new VBox(10, headline, message); banner = new HBox(contentVBox, Spacer.fillHBox(), closeButton); banner.setAlignment(Pos.TOP_CENTER); diff --git a/apps/desktop/desktop/src/main/resources/css/application.css b/apps/desktop/desktop/src/main/resources/css/application.css index 21163408b3..e85574dc8a 100644 --- a/apps/desktop/desktop/src/main/resources/css/application.css +++ b/apps/desktop/desktop/src/main/resources/css/application.css @@ -124,7 +124,7 @@ -fx-fill: -fx-light-text-color; -fx-text-fill: -fx-light-text-color; -fx-font-size: 2em; - -fx-font-family: "IBM Plex Sans Light"; + -fx-font-family: "IBM Plex Sans Medium"; } .alert-banner-message { @@ -146,7 +146,7 @@ } .warn-banner { - -fx-background-color: -bisq2-yellow-dim-50; + -fx-background-color: -bisq2-yellow-dim-20; } .emergency-banner { diff --git a/apps/desktop/desktop/src/main/resources/css/images.css b/apps/desktop/desktop/src/main/resources/css/images.css index 55bb5bb3a4..efc7f7ef25 100644 --- a/apps/desktop/desktop/src/main/resources/css/images.css +++ b/apps/desktop/desktop/src/main/resources/css/images.css @@ -210,6 +210,10 @@ -fx-image: url("/images/icons/check-grey.png"); } +#bisq-broadcast-white { + -fx-image: url("/images/icons/bisq-broadcast-white.png"); +} + /* ------------------------------------------------------------------------------------ */ /* Navigation */ diff --git a/apps/desktop/desktop/src/main/resources/images/icons/bisq-broadcast-white.png b/apps/desktop/desktop/src/main/resources/images/icons/bisq-broadcast-white.png new file mode 100644 index 0000000000000000000000000000000000000000..82001064073fe110f91cd3d6f7f5c80412951c21 GIT binary patch literal 1283 zcmV+e1^oJnP);2v-fxCh(=-e)gGWpt%{r;0ocn9!E)E$ke;;^q+fiL7kb?t%w0QR}`l!2{4UhNrJ*P`4r<0dg2QardaS5iw<3kV}tQiMA$Ywz}LcAP<)WrJ_K(PvVq<(oSxS;b%D-yuD=3MvWZ;;~O-hjk$DKHMIh<%6kBIQ^?HO zgp?7~h}G`7ae&uIc5&(4;8GcI?_1~Zg~zQmvGPNp0nU{NOP~9sFlj(3r>=hwKpbAt z-xCAi&ql*U$OwlXwhe^`S<=D^)ZGy%@Hav^S*570P?ipNwD9j;EzDgu1WNE@m;f{o zE4p_L8u+PDI*uBYzp90~n_V>MMy`c;EI^xA&!_4#Go$O6z(%ZXbmePOQ^`i#km!;tPXeeG2*^GV$pz7kYs|EN>VeIBo zDJ?xT=tk!3g^>sJIelKMxKaYj!(j=kw|RqZ?%P|=v@DJM5V-hSc+V1$*A0koPJE`B z7J5OV6a*UpK)c5(2&`|;hVFTpmo=G7`wqUPx%8Q_Y4FKbAu`Rnuk?`S(uj~29;Kwy zx;D<5EOOU45s+Qa3+pvM zpg`%ujU(WZNgET77(6H#DBqZQLm3J6w>m&*GUAy4OQ#(H&VX*F%1&cxX?MH+uH{a8 tyh=+SMf&ec_kerAJ>VYjKFc2g1_0nIs|3A-EkytT002ovPDHLkV1jzfQS1N! literal 0 HcmV?d00001 From daab2e2c604148ee6114bf3d8c9b2620e80371ba Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Fri, 22 Mar 2024 21:25:27 +0700 Subject: [PATCH 040/119] Add authorized data for min. required rep. score which can be published by security manager. By default users use that value. If they toggle the switch in settings their custom value is used. --- .../java/bisq/application/ResolverConfig.java | 2 + .../SecurityManagerController.java | 64 +++++++++- .../SecurityManagerModel.java | 4 + .../security_manager/SecurityManagerView.java | 116 +++++++++++++++-- .../bisq_easy/BisqEasyServiceUtil.java | 6 +- .../amount/TradeWizardAmountController.java | 5 +- .../review/TradeWizardReviewController.java | 5 +- .../TradeWizardSelectOfferController.java | 7 +- .../chatMessages/ChatMessagesListView.java | 7 +- .../preferences/PreferencesController.java | 53 ++++++-- .../preferences/PreferencesModel.java | 3 + .../settings/preferences/PreferencesView.java | 24 +++- .../java/bisq/bisq_easy/BisqEasyService.java | 26 +++- .../bisq/bonded_roles/BondedRolesService.java | 6 +- .../AuthorizedDifficultyAdjustmentData.java | 2 - .../DifficultyAdjustmentService.java | 3 - ...horizedMinRequiredReputationScoreData.java | 118 ++++++++++++++++++ .../MinRequiredReputationScoreService.java | 104 +++++++++++++++ .../src/main/proto/bonded_roles.proto | 7 ++ .../main/resources/authorized_role.properties | 8 +- i18n/src/main/resources/settings.properties | 7 +- .../java/bisq/settings/SettingsService.java | 9 +- .../java/bisq/settings/SettingsStore.java | 16 ++- settings/src/main/proto/settings.proto | 1 + .../SecurityManagerService.java | 24 ++++ .../bisq/user/reputation/ReputationScore.java | 1 + 26 files changed, 574 insertions(+), 54 deletions(-) create mode 100644 bonded-roles/src/main/java/bisq/bonded_roles/security_manager/min_reputation_score/AuthorizedMinRequiredReputationScoreData.java create mode 100644 bonded-roles/src/main/java/bisq/bonded_roles/security_manager/min_reputation_score/MinRequiredReputationScoreService.java diff --git a/application/src/main/java/bisq/application/ResolverConfig.java b/application/src/main/java/bisq/application/ResolverConfig.java index 13ae9b5bd7..a2b017821b 100644 --- a/application/src/main/java/bisq/application/ResolverConfig.java +++ b/application/src/main/java/bisq/application/ResolverConfig.java @@ -24,6 +24,7 @@ import bisq.bonded_roles.release.ReleaseNotification; import bisq.bonded_roles.security_manager.alert.AuthorizedAlertData; import bisq.bonded_roles.security_manager.difficulty_adjustment.AuthorizedDifficultyAdjustmentData; +import bisq.bonded_roles.security_manager.min_reputation_score.AuthorizedMinRequiredReputationScoreData; import bisq.chat.ChatMessage; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; import bisq.chat.bisqeasy.open_trades.BisqEasyOpenTradeMessage; @@ -57,6 +58,7 @@ public static void config() { DistributedDataResolver.addResolver("bonded_roles.AuthorizedBondedRole", AuthorizedBondedRole.getResolver()); DistributedDataResolver.addResolver("bonded_roles.AuthorizedAlertData", AuthorizedAlertData.getResolver()); DistributedDataResolver.addResolver("bonded_roles.AuthorizedDifficultyAdjustmentData", AuthorizedDifficultyAdjustmentData.getResolver()); + DistributedDataResolver.addResolver("bonded_roles.AuthorizedMinRequiredReputationScoreData", AuthorizedMinRequiredReputationScoreData.getResolver()); DistributedDataResolver.addResolver("bonded_roles.ReleaseNotification", ReleaseNotification.getResolver()); DistributedDataResolver.addResolver("bonded_roles.AuthorizedMarketPriceData", AuthorizedMarketPriceData.getResolver()); DistributedDataResolver.addResolver("user.AuthorizedProofOfBurnData", AuthorizedProofOfBurnData.getResolver()); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerController.java index 179300212b..01219fb5af 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerController.java @@ -25,6 +25,8 @@ import bisq.bonded_roles.security_manager.alert.AuthorizedAlertData; import bisq.bonded_roles.security_manager.difficulty_adjustment.AuthorizedDifficultyAdjustmentData; import bisq.bonded_roles.security_manager.difficulty_adjustment.DifficultyAdjustmentService; +import bisq.bonded_roles.security_manager.min_reputation_score.AuthorizedMinRequiredReputationScoreData; +import bisq.bonded_roles.security_manager.min_reputation_score.MinRequiredReputationScoreService; import bisq.common.observable.Pin; import bisq.common.util.StringUtils; import bisq.desktop.ServiceProvider; @@ -40,6 +42,7 @@ import bisq.user.identity.UserIdentityService; import bisq.user.profile.UserProfile; import bisq.user.profile.UserProfileService; +import bisq.user.reputation.ReputationScore; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.fxmisc.easybind.EasyBind; @@ -60,8 +63,11 @@ public class SecurityManagerController implements Controller { private final UserProfileService userProfileService; private final AuthorizedBondedRolesService authorizedBondedRolesService; private final DifficultyAdjustmentService difficultyAdjustmentService; - private Pin userIdentityPin, alertsPin, bondedRoleSetPin, difficultyAdjustmentListItemsPin; - private Subscription messagePin, requireVersionForTradingPin, minVersionPin, selectedBondedRolePin, difficultyAdjustmentPin; + private final MinRequiredReputationScoreService minRequiredReputationScoreService; + private Pin userIdentityPin, alertsPin, bondedRoleSetPin, difficultyAdjustmentListItemsPin, + minRequiredReputationScoreListItemsPin; + private Subscription messagePin, requireVersionForTradingPin, minVersionPin, selectedBondedRolePin, + difficultyAdjustmentPin, minRequiredReputationScorePin; public SecurityManagerController(ServiceProvider serviceProvider) { securityManagerService = serviceProvider.getSupportService().getSecurityManagerService(); @@ -69,6 +75,7 @@ public SecurityManagerController(ServiceProvider serviceProvider) { userProfileService = serviceProvider.getUserService().getUserProfileService(); alertService = serviceProvider.getBondedRolesService().getAlertService(); difficultyAdjustmentService = serviceProvider.getBondedRolesService().getDifficultyAdjustmentService(); + minRequiredReputationScoreService = serviceProvider.getBondedRolesService().getMinRequiredReputationScoreService(); authorizedBondedRolesService = serviceProvider.getBondedRolesService().getAuthorizedBondedRolesService(); RoleInfo roleInfo = new RoleInfo(serviceProvider); model = new SecurityManagerModel(); @@ -95,12 +102,22 @@ public void onActivate() { .to(authorizedBondedRolesService.getBondedRoles()); difficultyAdjustmentListItemsPin = FxBindings.bind(model.getDifficultyAdjustmentListItems()) - .map(authorizedBondedRole -> new SecurityManagerView.DifficultyAdjustmentListItem(authorizedBondedRole, this)) + .map(SecurityManagerView.DifficultyAdjustmentListItem::new) .to(difficultyAdjustmentService.getAuthorizedDifficultyAdjustmentDataSet()); + model.getDifficultyAdjustmentFactor().set(difficultyAdjustmentService.getMostRecentValueOrDefault().get()); difficultyAdjustmentPin = EasyBind.subscribe(model.getDifficultyAdjustmentFactor(), difficultyAdjustmentFactor -> model.getDifficultyAdjustmentFactorButtonDisabled().set(difficultyAdjustmentFactor == null || !isValidDifficultyAdjustmentFactor(difficultyAdjustmentFactor.doubleValue()))); + + minRequiredReputationScoreListItemsPin = FxBindings.bind(model.getMinRequiredReputationScoreListItems()) + .map(SecurityManagerView.MinRequiredReputationScoreListItem::new) + .to(minRequiredReputationScoreService.getAuthorizedMinRequiredReputationScoreDataSet()); + + model.getMinRequiredReputationScore().set(minRequiredReputationScoreService.getMostRecentValueOrDefault().get()); + minRequiredReputationScorePin = EasyBind.subscribe(model.getMinRequiredReputationScore(), minRequiredReputationScore -> + model.getMinRequiredReputationScoreButtonDisabled().set(minRequiredReputationScore == null || + !isValidMinRequiredRequiredReputationScore(minRequiredReputationScore.doubleValue()))); } @Override @@ -109,11 +126,13 @@ public void onDeactivate() { bondedRoleSetPin.unbind(); alertsPin.unbind(); difficultyAdjustmentListItemsPin.unbind(); + minRequiredReputationScoreListItemsPin.unbind(); messagePin.unsubscribe(); requireVersionForTradingPin.unsubscribe(); minVersionPin.unsubscribe(); selectedBondedRolePin.unsubscribe(); difficultyAdjustmentPin.unsubscribe(); + minRequiredReputationScorePin.unsubscribe(); } void onSelectAlertType(AlertType alertType) { @@ -162,7 +181,7 @@ void onSendAlert() { }); } - boolean isRemoveButtonVisible(AuthorizedAlertData authorizedAlertData) { + boolean isRemoveDifficultyAdjustmentButtonVisible(AuthorizedAlertData authorizedAlertData) { if (userIdentityService.getSelectedUserIdentity() == null) { return false; } @@ -209,23 +228,56 @@ void onPublishDifficultyAdjustmentFactor() { } } + void onPublishMinRequiredReputationScore() { + long minRequiredReputationScore = model.getMinRequiredReputationScore().get(); + if (isValidMinRequiredRequiredReputationScore(minRequiredReputationScore)) { + securityManagerService.publishMinRequiredReputationScore(minRequiredReputationScore) + .whenComplete((result, throwable) -> { + UIThread.run(() -> { + if (throwable != null) { + new Popup().error(throwable).show(); + } else { + model.getMinRequiredReputationScore().set(minRequiredReputationScoreService.getMostRecentValueOrDefault().get()); + } + }); + }); + } + } + private static boolean isValidDifficultyAdjustmentFactor(double difficultyAdjustmentFactor) { return difficultyAdjustmentFactor >= 0 && difficultyAdjustmentFactor <= NetworkLoad.MAX_DIFFICULTY_ADJUSTMENT; } - boolean isRemoveButtonVisible(AuthorizedDifficultyAdjustmentData data) { + private static boolean isValidMinRequiredRequiredReputationScore(double minRequiredReputationScore) { + return minRequiredReputationScore >= 0 && minRequiredReputationScore <= ReputationScore.MAX_VALUE; + } + + boolean isRemoveDifficultyAdjustmentButtonVisible(AuthorizedDifficultyAdjustmentData data) { + if (userIdentityService.getSelectedUserIdentity() == null) { + return false; + } + return userIdentityService.getSelectedUserIdentity().getId().equals(data.getSecurityManagerProfileId()); + } + + boolean isRemoveMinRequiredReputationScoreButtonVisible(AuthorizedMinRequiredReputationScoreData data) { if (userIdentityService.getSelectedUserIdentity() == null) { return false; } return userIdentityService.getSelectedUserIdentity().getId().equals(data.getSecurityManagerProfileId()); } - void onRemoveDifficultyAdjustmentData(SecurityManagerView.DifficultyAdjustmentListItem item) { + void onRemoveDifficultyAdjustmentListItem(SecurityManagerView.DifficultyAdjustmentListItem item) { UserIdentity userIdentity = checkNotNull(userIdentityService.getSelectedUserIdentity()); securityManagerService.removeDifficultyAdjustment(item.getData(), userIdentity.getNetworkIdWithKeyPair().getKeyPair()); model.getDifficultyAdjustmentFactor().set(difficultyAdjustmentService.getMostRecentValueOrDefault().get()); } + void onRemoveMinRequiredReputationScoreListItem(SecurityManagerView.MinRequiredReputationScoreListItem item) { + UserIdentity userIdentity = checkNotNull(userIdentityService.getSelectedUserIdentity()); + securityManagerService.removeMinRequiredReputationScore(item.getData(), userIdentity.getNetworkIdWithKeyPair().getKeyPair()); + model.getMinRequiredReputationScore().set(minRequiredReputationScoreService.getMostRecentValueOrDefault().get()); + } + private void applySelectAlertType(AlertType alertType) { model.getSelectedAlertType().set(alertType); switch (alertType) { diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerModel.java index b865de4334..17d318c3f3 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerModel.java @@ -22,6 +22,7 @@ import bisq.desktop.main.content.authorized_role.security_manager.SecurityManagerView.AlertListItem; import bisq.desktop.main.content.authorized_role.security_manager.SecurityManagerView.BondedRoleListItem; import bisq.desktop.main.content.authorized_role.security_manager.SecurityManagerView.DifficultyAdjustmentListItem; +import bisq.desktop.main.content.authorized_role.security_manager.SecurityManagerView.MinRequiredReputationScoreListItem; import javafx.beans.property.*; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -33,8 +34,11 @@ @Getter public class SecurityManagerModel implements Model { private final DoubleProperty difficultyAdjustmentFactor = new SimpleDoubleProperty(); + private final LongProperty minRequiredReputationScore = new SimpleLongProperty(); private final BooleanProperty difficultyAdjustmentFactorButtonDisabled = new SimpleBooleanProperty(); + private final BooleanProperty minRequiredReputationScoreButtonDisabled = new SimpleBooleanProperty(); private final ObservableList difficultyAdjustmentListItems = FXCollections.observableArrayList(); + private final ObservableList minRequiredReputationScoreListItems = FXCollections.observableArrayList(); private final ObjectProperty selectedAlertType = new SimpleObjectProperty<>(); private final ObservableList alertTypes = FXCollections.observableArrayList(); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerView.java index 5c355acf8e..595c86b155 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/security_manager/SecurityManagerView.java @@ -21,6 +21,7 @@ import bisq.bonded_roles.security_manager.alert.AlertType; import bisq.bonded_roles.security_manager.alert.AuthorizedAlertData; import bisq.bonded_roles.security_manager.difficulty_adjustment.AuthorizedDifficultyAdjustmentData; +import bisq.bonded_roles.security_manager.min_reputation_score.AuthorizedMinRequiredReputationScoreData; import bisq.desktop.common.view.View; import bisq.desktop.components.controls.AutoCompleteComboBox; import bisq.desktop.components.controls.MaterialTextArea; @@ -35,6 +36,7 @@ import bisq.network.p2p.node.network_load.NetworkLoad; import bisq.presentation.formatters.BooleanFormatter; import bisq.presentation.formatters.DateFormatter; +import bisq.user.reputation.ReputationScore; import javafx.beans.binding.Bindings; import javafx.geometry.Insets; import javafx.geometry.Pos; @@ -60,16 +62,21 @@ public class SecurityManagerView extends View alertTypeSelection; private final AutoCompleteComboBox bondedRoleSelection; private final CheckBox haltTradingCheckBox, requireVersionForTradingCheckBox; private final HBox requireVersionForTradingHBox; private final BisqTableView alertTableView; private final BisqTableView difficultyAdjustmentTableView; + private final BisqTableView minRequiredReputationScoreTableView; + private Subscription selectedAlertTypePin, selectedBondedRolListItemPin; public SecurityManagerView(SecurityManagerModel model, SecurityManagerController controller, Pane roleInfo) { @@ -155,13 +162,38 @@ public BondedRoleListItem fromString(String string) { difficultyAdjustmentTableView.getStyleClass().add("user-bonded-roles-table-view"); configDifficultyAdjustmentTableView(); + + // minRequiredReputationScore + Label minRequiredReputationScoreHeadline = new Label(Res.get("authorizedRole.securityManager.minRequiredReputationScore.headline")); + minRequiredReputationScoreHeadline.getStyleClass().add("large-thin-headline"); + + minRequiredReputationScore = new MaterialTextField(Res.get("authorizedRole.securityManager.minRequiredReputationScore.description")); + minRequiredReputationScore.setMaxWidth(400); + minRequiredReputationScore.setValidators(MIN_REPUTATION_SCORE_VALIDATOR); + + minRequiredReputationScoreButton = new Button(Res.get("authorizedRole.securityManager.minRequiredReputationScore.button")); + minRequiredReputationScoreButton.setDefaultButton(true); + + Label minRequiredReputationScoreTableHeadline = new Label(Res.get("authorizedRole.securityManager.minRequiredReputationScore.table.headline")); + minRequiredReputationScoreTableHeadline.getStyleClass().add("large-thin-headline"); + + minRequiredReputationScoreTableView = new BisqTableView<>(model.getMinRequiredReputationScoreListItems()); + minRequiredReputationScoreTableView.setFixHeight(200); + minRequiredReputationScoreTableView.getStyleClass().add("user-bonded-roles-table-view"); + configMinRequiredReputationScoreTableView(); + + // Role info roleInfo.setPadding(new Insets(0)); VBox.setMargin(difficultyAdjustmentButton, new Insets(0, 0, 10, 0)); VBox.setMargin(sendAlertButton, new Insets(10, 0, 0, 0)); VBox.setMargin(haltTradingCheckBox, new Insets(10, 0, 0, 0)); - VBox.setMargin(alertTableHeadline, new Insets(30, 0, -5, 0)); + VBox.setMargin(alertTableHeadline, new Insets(10, 0, 0, 0)); + VBox.setMargin(minRequiredReputationScoreHeadline, new Insets(20, 0, 0, 0)); + VBox.setMargin(minRequiredReputationScoreTableHeadline, new Insets(10, 0, 0, 0)); + VBox.setMargin(difficultyAdjustmentHeadline, new Insets(20, 0, 0, 0)); + VBox.setMargin(difficultyAdjustmentTableHeadline, new Insets(10, 0, 0, 0)); VBox.setMargin(roleInfo, new Insets(20, 0, 0, 0)); VBox.setVgrow(difficultyAdjustmentTableView, Priority.NEVER); VBox.setVgrow(alertTableView, Priority.NEVER); @@ -173,6 +205,9 @@ public BondedRoleListItem fromString(String string) { sendAlertButton, alertTableHeadline, alertTableView, + minRequiredReputationScoreHeadline, minRequiredReputationScore, minRequiredReputationScoreButton, + minRequiredReputationScoreTableHeadline, minRequiredReputationScoreTableView, + difficultyAdjustmentHeadline, difficultyAdjustmentFactor, difficultyAdjustmentButton, difficultyAdjustmentTableHeadline, difficultyAdjustmentTableView, @@ -182,6 +217,7 @@ public BondedRoleListItem fromString(String string) { @Override protected void onViewAttached() { Bindings.bindBidirectional(difficultyAdjustmentFactor.textProperty(), model.getDifficultyAdjustmentFactor(), new NumberStringConverter()); + Bindings.bindBidirectional(minRequiredReputationScore.textProperty(), model.getMinRequiredReputationScore(), new NumberStringConverter()); haltTradingCheckBox.visibleProperty().bind(model.getSelectedAlertType().isEqualTo(AlertType.EMERGENCY)); haltTradingCheckBox.managedProperty().bind(haltTradingCheckBox.visibleProperty()); @@ -190,7 +226,7 @@ protected void onViewAttached() { minVersion.textProperty().bindBidirectional(model.getMinVersion()); minVersion.disableProperty().bind(requireVersionForTradingCheckBox.selectedProperty().not()); difficultyAdjustmentButton.disableProperty().bind(model.getDifficultyAdjustmentFactorButtonDisabled()); - + minRequiredReputationScoreButton.disableProperty().bind(model.getMinRequiredReputationScoreButtonDisabled()); bondedRoleSelection.visibleProperty().bind(model.getSelectedAlertType().isEqualTo(AlertType.BAN)); bondedRoleSelection.managedProperty().bind(bondedRoleSelection.visibleProperty()); @@ -221,6 +257,7 @@ protected void onViewAttached() { }); difficultyAdjustmentButton.setOnAction(e -> controller.onPublishDifficultyAdjustmentFactor()); + minRequiredReputationScoreButton.setOnAction(e -> controller.onPublishMinRequiredReputationScore()); sendAlertButton.setOnAction(e -> controller.onSendAlert()); haltTradingCheckBox.selectedProperty().bindBidirectional(model.getHaltTrading()); requireVersionForTradingCheckBox.selectedProperty().bindBidirectional(model.getRequireVersionForTrading()); @@ -234,6 +271,7 @@ protected void onViewAttached() { @Override protected void onViewDetached() { Bindings.unbindBidirectional(difficultyAdjustmentFactor.textProperty(), model.getDifficultyAdjustmentFactor()); + Bindings.unbindBidirectional(minRequiredReputationScore.textProperty(), model.getMinRequiredReputationScore()); haltTradingCheckBox.visibleProperty().unbind(); haltTradingCheckBox.managedProperty().unbind(); @@ -242,6 +280,7 @@ protected void onViewDetached() { minVersion.textProperty().unbindBidirectional(model.getMinVersion()); minVersion.disableProperty().unbind(); difficultyAdjustmentButton.disableProperty().unbind(); + minRequiredReputationScoreButton.disableProperty().unbind(); bondedRoleSelection.visibleProperty().unbind(); bondedRoleSelection.managedProperty().unbind(); @@ -259,6 +298,8 @@ protected void onViewDetached() { haltTradingCheckBox.selectedProperty().unbindBidirectional(model.getHaltTrading()); requireVersionForTradingCheckBox.selectedProperty().unbindBidirectional(model.getRequireVersionForTrading()); + difficultyAdjustmentButton.setOnAction(null); + minRequiredReputationScoreButton.setOnAction(null); alertTypeSelection.setOnChangeConfirmed(null); bondedRoleSelection.setOnChangeConfirmed(null); @@ -284,6 +325,23 @@ private void configDifficultyAdjustmentTableView() { .build()); } + private void configMinRequiredReputationScoreTableView() { + minRequiredReputationScoreTableView.getColumns().add(BisqTableColumns.getDateColumn(minRequiredReputationScoreTableView.getSortOrder())); + minRequiredReputationScoreTableView.getColumns().add(new BisqTableColumn.Builder() + .title(Res.get("authorizedRole.securityManager.minRequiredReputationScore.table.value")) + .minWidth(150) + .comparator(Comparator.comparing(MinRequiredReputationScoreListItem::getMinRequiredReputationScore)) + .valueSupplier(MinRequiredReputationScoreListItem::getMinRequiredReputationScoreString) + .build()); + minRequiredReputationScoreTableView.getColumns().add(new BisqTableColumn.Builder() + .isSortable(false) + .minWidth(200) + .right() + .setCellFactory(getMinRequiredReputationScoreCellFactory()) + .build()); + } + + private void configAlertTableView() { alertTableView.getColumns().add(BisqTableColumns.getDateColumn(alertTableView.getSortOrder())); @@ -339,7 +397,7 @@ private Callback, TableCell controller.onRemoveAlert(item.getAuthorizedAlertData())); setGraphic(button); } else { @@ -350,7 +408,8 @@ public void updateItem(final AlertListItem item, boolean empty) { }; } - private Callback, TableCell> getRemoveDifficultyAdjustmentCellFactory() { + private Callback, + TableCell> getRemoveDifficultyAdjustmentCellFactory() { return column -> new TableCell<>() { private final Button button = new Button(Res.get("data.remove")); @@ -358,8 +417,28 @@ private Callback controller.onRemoveDifficultyAdjustmentData(item)); + if (item != null && !empty && controller.isRemoveDifficultyAdjustmentButtonVisible(item.getData())) { + button.setOnAction(e -> controller.onRemoveDifficultyAdjustmentListItem(item)); + setGraphic(button); + } else { + button.setOnAction(null); + setGraphic(null); + } + } + }; + } + + private Callback, + TableCell> getMinRequiredReputationScoreCellFactory() { + return column -> new TableCell<>() { + private final Button button = new Button(Res.get("data.remove")); + + @Override + public void updateItem(final MinRequiredReputationScoreListItem item, boolean empty) { + super.updateItem(item, empty); + + if (item != null && !empty && controller.isRemoveMinRequiredReputationScoreButtonVisible(item.getData())) { + button.setOnAction(e -> controller.onRemoveMinRequiredReputationScoreListItem(item)); setGraphic(button); } else { button.setOnAction(null); @@ -378,7 +457,7 @@ public static class DifficultyAdjustmentListItem implements DateTableItem { private final String dateString, timeString, difficultyAdjustmentFactorString; private final double difficultyAdjustmentFactor; - public DifficultyAdjustmentListItem(AuthorizedDifficultyAdjustmentData data, SecurityManagerController controller) { + public DifficultyAdjustmentListItem(AuthorizedDifficultyAdjustmentData data) { this.data = data; date = data.getDate(); dateString = DateFormatter.formatDate(date); @@ -388,6 +467,25 @@ public DifficultyAdjustmentListItem(AuthorizedDifficultyAdjustmentData data, Sec } } + @EqualsAndHashCode + @Getter + @ToString + public static class MinRequiredReputationScoreListItem implements DateTableItem { + private final AuthorizedMinRequiredReputationScoreData data; + private final long date; + private final String dateString, timeString, minRequiredReputationScoreString; + private final double minRequiredReputationScore; + + public MinRequiredReputationScoreListItem(AuthorizedMinRequiredReputationScoreData data) { + this.data = data; + date = data.getDate(); + dateString = DateFormatter.formatDate(date); + timeString = DateFormatter.formatTime(date); + minRequiredReputationScore = data.getMinRequiredReputationScore(); + minRequiredReputationScoreString = String.valueOf(minRequiredReputationScore); + } + } + @EqualsAndHashCode @Getter @ToString diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/BisqEasyServiceUtil.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/BisqEasyServiceUtil.java index adb9f12da0..9eb426bf97 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/BisqEasyServiceUtil.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/BisqEasyServiceUtil.java @@ -17,12 +17,12 @@ package bisq.desktop.main.content.bisq_easy; +import bisq.bisq_easy.BisqEasyService; import bisq.chat.bisqeasy.open_trades.BisqEasyOpenTradeChannel; import bisq.desktop.ServiceProvider; import bisq.network.identity.NetworkId; import bisq.offer.bisq_easy.BisqEasyOffer; import bisq.offer.options.OfferOptionUtil; -import bisq.settings.SettingsService; import bisq.trade.Trade; import bisq.trade.bisq_easy.BisqEasyTrade; import bisq.user.identity.UserIdentity; @@ -51,7 +51,7 @@ public static Optional findTradeFromChannel(ServiceProvider servi } public static boolean offerMatchesMinRequiredReputationScore(ReputationService reputationService, - SettingsService settingsService, + BisqEasyService bisqEasyService, UserIdentityService userIdentityService, UserProfileService userProfileService, BisqEasyOffer peersOffer) { @@ -61,7 +61,7 @@ public static boolean offerMatchesMinRequiredReputationScore(ReputationService r return false; } long makerAsSellersScore = reputationService.getReputationScore(optionalMakersUserProfile.get()).getTotalScore(); - long myMinRequiredScore = settingsService.getMinRequiredReputationScore().get(); + long myMinRequiredScore = bisqEasyService.getMinRequiredReputationScore().get(); // Maker as seller's score must be > than my required score (as buyer) return makerAsSellersScore >= myMinRequiredScore; } else { diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/amount/TradeWizardAmountController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/amount/TradeWizardAmountController.java index 451d7115aa..bee2a9771a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/amount/TradeWizardAmountController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/amount/TradeWizardAmountController.java @@ -18,6 +18,7 @@ package bisq.desktop.main.content.bisq_easy.trade_wizard.amount; import bisq.account.payment_method.FiatPaymentMethod; +import bisq.bisq_easy.BisqEasyService; import bisq.bonded_roles.market_price.MarketPriceService; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannel; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannelService; @@ -75,11 +76,13 @@ public class TradeWizardAmountController implements Controller { private final UserProfileService userProfileService; private final ReputationService reputationService; private final UserIdentityService userIdentityService; + private final BisqEasyService bisqEasyService; private Subscription isMinAmountEnabledPin, maxOrFixAmountCompBaseSideAmountPin, minAmountCompBaseSideAmountPin, maxAmountCompQuoteSideAmountPin, minAmountCompQuoteSideAmountPin; public TradeWizardAmountController(ServiceProvider serviceProvider) { settingsService = serviceProvider.getSettingsService(); + bisqEasyService = serviceProvider.getBisqEasyService(); marketPriceService = serviceProvider.getBondedRolesService().getMarketPriceService(); userProfileService = serviceProvider.getUserService().getUserProfileService(); userIdentityService = serviceProvider.getUserService().getUserIdentityService(); @@ -372,7 +375,7 @@ private boolean filterOffers(BisqEasyOffer peersOffer) { } if (!BisqEasyServiceUtil.offerMatchesMinRequiredReputationScore(reputationService, - settingsService, + bisqEasyService, userIdentityService, userProfileService, peersOffer)) { diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/review/TradeWizardReviewController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/review/TradeWizardReviewController.java index 6c865da3c0..d8f91c7f02 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/review/TradeWizardReviewController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/review/TradeWizardReviewController.java @@ -18,6 +18,7 @@ package bisq.desktop.main.content.bisq_easy.trade_wizard.review; import bisq.account.payment_method.FiatPaymentMethod; +import bisq.bisq_easy.BisqEasyService; import bisq.bisq_easy.NavigationTarget; import bisq.bonded_roles.market_price.MarketPrice; import bisq.bonded_roles.market_price.MarketPriceService; @@ -96,6 +97,7 @@ public class TradeWizardReviewController implements Controller { private final SettingsService settingsService; private final ReviewDataDisplay reviewDataDisplay; private final MediationRequestService mediationRequestService; + private final BisqEasyService bisqEasyService; private Pin errorMessagePin, peersErrorMessagePin; public TradeWizardReviewController(ServiceProvider serviceProvider, @@ -112,6 +114,7 @@ public TradeWizardReviewController(ServiceProvider serviceProvider, bisqEasyTradeService = serviceProvider.getTradeService().getBisqEasyTradeService(); bannedUserService = serviceProvider.getUserService().getBannedUserService(); settingsService = serviceProvider.getSettingsService(); + bisqEasyService = serviceProvider.getBisqEasyService(); mediationRequestService = serviceProvider.getSupportService().getMediationRequestService(); priceInput = new PriceInput(serviceProvider.getBondedRolesService().getMarketPriceService()); @@ -176,7 +179,7 @@ public void setDataForCreateOffer(Direction direction, priceSpec, new ArrayList<>(fiatPaymentMethods), userIdentity.getUserProfile().getTerms(), - settingsService.getMinRequiredReputationScore().get(), + bisqEasyService.getMinRequiredReputationScore().get(), new ArrayList<>(settingsService.getSupportedLanguageCodes())); model.setBisqEasyOffer(bisqEasyOffer); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferController.java index 8b47c58fa6..12ad8fab42 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferController.java @@ -18,6 +18,7 @@ package bisq.desktop.main.content.bisq_easy.trade_wizard.select_offer; import bisq.account.payment_method.FiatPaymentMethod; +import bisq.bisq_easy.BisqEasyService; import bisq.bisq_easy.NavigationTarget; import bisq.bonded_roles.market_price.MarketPriceService; import bisq.chat.ChatService; @@ -82,6 +83,7 @@ public class TradeWizardSelectOfferController implements Controller { private final MarketPriceService marketPriceService; private final BannedUserService bannedUserService; private final BisqEasyTradeService bisqEasyTradeService; + private final BisqEasyService bisqEasyService; public TradeWizardSelectOfferController(ServiceProvider serviceProvider, Runnable onBackHandler, @@ -94,6 +96,7 @@ public TradeWizardSelectOfferController(ServiceProvider serviceProvider, bisqEasyOfferbookChannelService = chatService.getBisqEasyOfferbookChannelService(); reputationService = serviceProvider.getUserService().getReputationService(); settingsService = serviceProvider.getSettingsService(); + bisqEasyService = serviceProvider.getBisqEasyService(); userIdentityService = serviceProvider.getUserService().getUserIdentityService(); userProfileService = serviceProvider.getUserService().getUserProfileService(); marketPriceService = serviceProvider.getBondedRolesService().getMarketPriceService(); @@ -202,7 +205,7 @@ public void onActivate() { priceSpec, new ArrayList<>(model.getFiatPaymentMethods()), userIdentity.getUserProfile().getTerms(), - settingsService.getMinRequiredReputationScore().get(), + bisqEasyService.getMinRequiredReputationScore().get(), new ArrayList<>(settingsService.getSupportedLanguageCodes())); model.setBisqEasyOffer(bisqEasyOffer); @@ -360,7 +363,7 @@ private Predicate getPredicate() { } if (!BisqEasyServiceUtil.offerMatchesMinRequiredReputationScore(reputationService, - settingsService, + bisqEasyService, userIdentityService, userProfileService, peersOffer)) { diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesListView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesListView.java index c7bab04871..4f8ca9b73f 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesListView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesListView.java @@ -17,6 +17,7 @@ package bisq.desktop.main.content.components.chatMessages; +import bisq.bisq_easy.BisqEasyService; import bisq.bisq_easy.NavigationTarget; import bisq.chat.*; import bisq.chat.bisqeasy.BisqEasyOfferMessage; @@ -161,6 +162,7 @@ public static class Controller implements bisq.desktop.common.view.Controller { private final BannedUserService bannedUserService; private final NetworkService networkService; private final Optional resendMessageService; + private final BisqEasyService bisqEasyService; private Pin selectedChannelPin, chatMessagesPin, offerOnlySettingsPin; private Subscription selectedChannelSubscription, focusSubscription, scrollValuePin, scrollBarVisiblePin; @@ -175,6 +177,7 @@ private Controller(ServiceProvider serviceProvider, userProfileService = serviceProvider.getUserService().getUserProfileService(); reputationService = serviceProvider.getUserService().getReputationService(); settingsService = serviceProvider.getSettingsService(); + bisqEasyService = serviceProvider.getBisqEasyService(); bisqEasyTradeService = serviceProvider.getTradeService().getBisqEasyTradeService(); bannedUserService = serviceProvider.getUserService().getBannedUserService(); networkService = serviceProvider.getNetworkService(); @@ -355,7 +358,7 @@ public void onTakeOffer(BisqEasyOfferbookMessage bisqEasyOfferbookMessage) { } if (!BisqEasyServiceUtil.offerMatchesMinRequiredReputationScore(reputationService, - settingsService, + bisqEasyService, userIdentityService, userProfileService, bisqEasyOffer)) { @@ -364,7 +367,7 @@ public void onTakeOffer(BisqEasyOfferbookMessage bisqEasyOfferbookMessage) { .map(reputationService::getReputationScore) .map(ReputationScore::getTotalScore) .orElse(0L); - long myMinRequiredScore = settingsService.getMinRequiredReputationScore().get(); + long myMinRequiredScore = bisqEasyService.getMinRequiredReputationScore().get(); new Popup().information(Res.get("chat.message.takeOffer.makersReputationScoreTooLow.warn", myMinRequiredScore, makerAsSellersScore)).show(); } else { diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/settings/preferences/PreferencesController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/settings/preferences/PreferencesController.java index 901f3a2b83..c5510d6d2b 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/settings/preferences/PreferencesController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/settings/preferences/PreferencesController.java @@ -18,6 +18,7 @@ package bisq.desktop.main.content.settings.preferences; import bisq.bonded_roles.security_manager.difficulty_adjustment.DifficultyAdjustmentService; +import bisq.bonded_roles.security_manager.min_reputation_score.MinRequiredReputationScoreService; import bisq.chat.notifications.ChatNotificationService; import bisq.common.locale.LanguageRepository; import bisq.common.observable.Pin; @@ -45,17 +46,20 @@ public class PreferencesController implements Controller { private final SettingsService settingsService; private final ChatNotificationService chatNotificationService; private final DifficultyAdjustmentService difficultyAdjustmentService; + private final MinRequiredReputationScoreService minRequiredReputationScoreService; private Pin chatNotificationTypePin, useAnimationsPin, preventStandbyModePin, offerOnlyPin, closeMyOfferWhenTakenPin, supportedLanguageCodesPin, minRequiredReputationScorePin, ignoreDiffAdjustmentFromSecManagerPin, - mostRecentValueOrDefaultPin, difficultyAdjustmentFactorPin; + mostRecentDifficultyAdjustmentFactorOrDefaultPin, difficultyAdjustmentFactorPin, ignoreMinRequiredReputationScoreFromSecManagerPin, + mostRecentMinRequiredReputationScoreOrDefaultPin; private Subscription notifyForPreReleasePin, useTransientNotificationsPin, - difficultyAdjustmentFactorDescriptionTextPin; + difficultyAdjustmentFactorDescriptionTextPin, minRequiredReputationScoreDescriptionTextPin; public PreferencesController(ServiceProvider serviceProvider) { settingsService = serviceProvider.getSettingsService(); chatNotificationService = serviceProvider.getChatService().getChatNotificationService(); difficultyAdjustmentService = serviceProvider.getBondedRolesService().getDifficultyAdjustmentService(); + minRequiredReputationScoreService = serviceProvider.getBondedRolesService().getMinRequiredReputationScoreService(); model = new PreferencesModel(); view = new PreferencesView(model, this); } @@ -72,24 +76,49 @@ public void onActivate() { .to(settingsService.getUseAnimations()); preventStandbyModePin = FxBindings.bindBiDir(model.getPreventStandbyMode()) .to(settingsService.getPreventStandbyMode()); + minRequiredReputationScorePin = FxBindings.bindBiDir(model.getMinRequiredReputationScore()) .to(settingsService.getMinRequiredReputationScore()); + ignoreMinRequiredReputationScoreFromSecManagerPin = FxBindings.bindBiDir(model.getIgnoreMinRequiredReputationScoreFromSecManager()) + .to(settingsService.getIgnoreMinRequiredReputationScoreFromSecManager()); + model.getMinRequiredReputationScoreEditable().bind(model.getIgnoreMinRequiredReputationScoreFromSecManager()); + minRequiredReputationScoreDescriptionTextPin = EasyBind.subscribe(model.getIgnoreMinRequiredReputationScoreFromSecManager(), + value -> { + if (value) { + model.getMinRequiredReputationScoreDescriptionText().set(Res.get("settings.preferences.network.minReputationScore.description.self")); + if (mostRecentMinRequiredReputationScoreOrDefaultPin != null) { + mostRecentMinRequiredReputationScoreOrDefaultPin.unbind(); + } + minRequiredReputationScorePin = FxBindings.bindBiDir(model.getMinRequiredReputationScore()) + .to(settingsService.getMinRequiredReputationScore()); + } else { + model.getMinRequiredReputationScoreDescriptionText().set(Res.get("settings.preferences.network.minReputationScore.description.fromSecManager")); + + if (minRequiredReputationScorePin != null) { + minRequiredReputationScorePin.unbind(); + } + mostRecentMinRequiredReputationScoreOrDefaultPin = minRequiredReputationScoreService.getMostRecentValueOrDefault() + .addObserver(mostRecentValueOrDefault -> + UIThread.run(() -> model.getMinRequiredReputationScore().set(mostRecentValueOrDefault))); + } + }); + offerOnlyPin = FxBindings.bindBiDir(model.getOfferOnly()) .to(settingsService.getOffersOnly()); closeMyOfferWhenTakenPin = FxBindings.bindBiDir(model.getCloseMyOfferWhenTaken()) .to(settingsService.getCloseMyOfferWhenTaken()); supportedLanguageCodesPin = FxBindings.bind(model.getSelectedSupportedLanguageCodes()) .to(settingsService.getSupportedLanguageCodes()); + ignoreDiffAdjustmentFromSecManagerPin = FxBindings.bindBiDir(model.getIgnoreDiffAdjustmentFromSecManager()) .to(settingsService.getIgnoreDiffAdjustmentFromSecManager()); model.getDifficultyAdjustmentFactorEditable().bind(model.getIgnoreDiffAdjustmentFromSecManager()); - difficultyAdjustmentFactorDescriptionTextPin = EasyBind.subscribe(model.getIgnoreDiffAdjustmentFromSecManager(), value -> { if (value) { model.getDifficultyAdjustmentFactorDescriptionText().set(Res.get("settings.preferences.network.difficultyAdjustmentFactor.description.self")); - if (mostRecentValueOrDefaultPin != null) { - mostRecentValueOrDefaultPin.unbind(); + if (mostRecentDifficultyAdjustmentFactorOrDefaultPin != null) { + mostRecentDifficultyAdjustmentFactorOrDefaultPin.unbind(); } difficultyAdjustmentFactorPin = FxBindings.bindBiDir(model.getDifficultyAdjustmentFactor()) .to(settingsService.getDifficultyAdjustmentFactor()); @@ -99,7 +128,7 @@ public void onActivate() { if (difficultyAdjustmentFactorPin != null) { difficultyAdjustmentFactorPin.unbind(); } - mostRecentValueOrDefaultPin = difficultyAdjustmentService.getMostRecentValueOrDefault() + mostRecentDifficultyAdjustmentFactorOrDefaultPin = difficultyAdjustmentService.getMostRecentValueOrDefault() .addObserver(mostRecentValueOrDefault -> UIThread.run(() -> model.getDifficultyAdjustmentFactor().set(mostRecentValueOrDefault))); } @@ -130,17 +159,25 @@ public void onDeactivate() { preventStandbyModePin.unbind(); supportedLanguageCodesPin.unbind(); ignoreDiffAdjustmentFromSecManagerPin.unbind(); + ignoreMinRequiredReputationScoreFromSecManagerPin.unbind(); model.getDifficultyAdjustmentFactorEditable().unbind(); notifyForPreReleasePin.unsubscribe(); difficultyAdjustmentFactorDescriptionTextPin.unsubscribe(); + minRequiredReputationScoreDescriptionTextPin.unsubscribe(); if (useTransientNotificationsPin != null) { useTransientNotificationsPin.unsubscribe(); } if (difficultyAdjustmentFactorPin != null) { difficultyAdjustmentFactorPin.unbind(); } - if (mostRecentValueOrDefaultPin != null) { - mostRecentValueOrDefaultPin.unbind(); + if (minRequiredReputationScorePin != null) { + minRequiredReputationScorePin.unbind(); + } + if (mostRecentDifficultyAdjustmentFactorOrDefaultPin != null) { + mostRecentDifficultyAdjustmentFactorOrDefaultPin.unbind(); + } + if (mostRecentMinRequiredReputationScoreOrDefaultPin != null) { + mostRecentMinRequiredReputationScoreOrDefaultPin.unbind(); } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/settings/preferences/PreferencesModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/settings/preferences/PreferencesModel.java index b0a5a5973b..a89ffb2c10 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/settings/preferences/PreferencesModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/settings/preferences/PreferencesModel.java @@ -38,6 +38,9 @@ public class PreferencesModel implements Model { private final BooleanProperty useAnimations = new SimpleBooleanProperty(); private final BooleanProperty preventStandbyMode = new SimpleBooleanProperty(); private final LongProperty minRequiredReputationScore = new SimpleLongProperty(); + private final BooleanProperty minRequiredReputationScoreEditable = new SimpleBooleanProperty(); + private final StringProperty minRequiredReputationScoreDescriptionText = new SimpleStringProperty(); + private final BooleanProperty ignoreMinRequiredReputationScoreFromSecManager = new SimpleBooleanProperty(); private final BooleanProperty offerOnly = new SimpleBooleanProperty(); private final BooleanProperty closeMyOfferWhenTaken = new SimpleBooleanProperty(); private final DoubleProperty difficultyAdjustmentFactor = new SimpleDoubleProperty(); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/settings/preferences/PreferencesView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/settings/preferences/PreferencesView.java index faf484e889..94c96c1e2c 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/settings/preferences/PreferencesView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/settings/preferences/PreferencesView.java @@ -29,6 +29,7 @@ import bisq.i18n.Res; import bisq.network.p2p.node.network_load.NetworkLoad; import bisq.settings.ChatNotificationType; +import bisq.user.reputation.ReputationScore; import javafx.beans.binding.Bindings; import javafx.beans.value.ChangeListener; import javafx.geometry.Insets; @@ -46,14 +47,16 @@ @Slf4j public class PreferencesView extends View { private static final ValidatorBase REPUTATION_SCORE_VALIDATOR = - new NumberValidator(Res.get("settings.preferences.trade.requiredTotalReputationScore.invalid")); + new NumberValidator(Res.get("settings.preferences.trade.minReputationScore.invalid", ReputationScore.MAX_VALUE), + 0, ReputationScore.MAX_VALUE); private static final ValidatorBase DIFFICULTY_ADJUSTMENT_FACTOR_VALIDATOR = new NumberValidator(Res.get("settings.preferences.network.difficultyAdjustmentFactor.invalid", NetworkLoad.MAX_DIFFICULTY_ADJUSTMENT), 0, NetworkLoad.MAX_DIFFICULTY_ADJUSTMENT); private final Button resetDontShowAgain, clearNotifications, addLanguageButton; private final Switch useAnimations, preventStandbyMode, offersOnlySwitch, closeMyOfferWhenTaken, notifyForPreRelease, - useTransientNotifications, ignoreDiffAdjustFromSecManagerSwitch; + useTransientNotifications, ignoreDiffAdjustFromSecManagerSwitch, + ignoreMinRequiredReputationScoreFromSecManagerSwitch; private final ToggleGroup notificationsToggleGroup = new ToggleGroup(); private final RadioButton all, mention, off; private final ChangeListener notificationsToggleListener; @@ -193,8 +196,13 @@ public String fromString(String string) { null, Res.get("settings.preferences.trade.requiredTotalReputationScore.help")); minRequiredReputationScore.setMaxWidth(400); minRequiredReputationScore.setValidators(REPUTATION_SCORE_VALIDATOR); + ignoreMinRequiredReputationScoreFromSecManagerSwitch = new Switch(Res.get("settings.preferences.network.minReputationScore.ignoreValueFromSecManager")); - VBox tradeVBox = new VBox(10, minRequiredReputationScore, offersOnlySwitch, closeMyOfferWhenTaken); + VBox tradeVBox = new VBox(10, + closeMyOfferWhenTaken, + offersOnlySwitch, + minRequiredReputationScore, ignoreMinRequiredReputationScoreFromSecManagerSwitch + ); // Network @@ -204,7 +212,7 @@ public String fromString(String string) { difficultyAdjustmentFactor = new MaterialTextField(); difficultyAdjustmentFactor.setMaxWidth(400); difficultyAdjustmentFactor.setValidators(DIFFICULTY_ADJUSTMENT_FACTOR_VALIDATOR); - ignoreDiffAdjustFromSecManagerSwitch = new Switch(Res.get("settings.preferences.network.ignoreDifficultyAdjustmentFactorFromSecManager")); + ignoreDiffAdjustFromSecManagerSwitch = new Switch(Res.get("settings.preferences.network.difficultyAdjustmentFactor.ignoreValueFromSecManager")); VBox networkVBox = new VBox(10, difficultyAdjustmentFactor, ignoreDiffAdjustFromSecManagerSwitch); @@ -238,9 +246,13 @@ protected void onViewAttached() { preventStandbyMode.selectedProperty().bindBidirectional(model.getPreventStandbyMode()); offersOnlySwitch.selectedProperty().bindBidirectional(model.getOfferOnly()); ignoreDiffAdjustFromSecManagerSwitch.selectedProperty().bindBidirectional(model.getIgnoreDiffAdjustmentFromSecManager()); + ignoreMinRequiredReputationScoreFromSecManagerSwitch.selectedProperty().bindBidirectional(model.getIgnoreMinRequiredReputationScoreFromSecManager()); closeMyOfferWhenTaken.selectedProperty().bindBidirectional(model.getCloseMyOfferWhenTaken()); Bindings.bindBidirectional(minRequiredReputationScore.textProperty(), model.getMinRequiredReputationScore(), new NumberStringConverter()); + minRequiredReputationScore.descriptionProperty().bind(model.getMinRequiredReputationScoreDescriptionText()); + minRequiredReputationScore.getTextInputControl().editableProperty().bind(model.getMinRequiredReputationScoreEditable()); + Bindings.bindBidirectional(difficultyAdjustmentFactor.textProperty(), model.getDifficultyAdjustmentFactor(), new NumberStringConverter()); difficultyAdjustmentFactor.descriptionProperty().bind(model.getDifficultyAdjustmentFactorDescriptionText()); difficultyAdjustmentFactor.getTextInputControl().editableProperty().bind(model.getDifficultyAdjustmentFactorEditable()); @@ -282,9 +294,13 @@ protected void onViewDetached() { preventStandbyMode.selectedProperty().unbindBidirectional(model.getPreventStandbyMode()); offersOnlySwitch.selectedProperty().unbindBidirectional(model.getOfferOnly()); ignoreDiffAdjustFromSecManagerSwitch.selectedProperty().unbindBidirectional(model.getIgnoreDiffAdjustmentFromSecManager()); + ignoreMinRequiredReputationScoreFromSecManagerSwitch.selectedProperty().unbindBidirectional(model.getIgnoreMinRequiredReputationScoreFromSecManager()); closeMyOfferWhenTaken.selectedProperty().unbindBidirectional(model.getCloseMyOfferWhenTaken()); Bindings.unbindBidirectional(minRequiredReputationScore.textProperty(), model.getMinRequiredReputationScore()); + minRequiredReputationScore.getTextInputControl().editableProperty().unbind(); + minRequiredReputationScore.descriptionProperty().unbind(); + Bindings.unbindBidirectional(difficultyAdjustmentFactor.textProperty(), model.getDifficultyAdjustmentFactor()); difficultyAdjustmentFactor.getTextInputControl().editableProperty().unbind(); difficultyAdjustmentFactor.descriptionProperty().unbind(); diff --git a/bisq-easy/src/main/java/bisq/bisq_easy/BisqEasyService.java b/bisq-easy/src/main/java/bisq/bisq_easy/BisqEasyService.java index 6f43254d14..38fca50c9f 100644 --- a/bisq-easy/src/main/java/bisq/bisq_easy/BisqEasyService.java +++ b/bisq-easy/src/main/java/bisq/bisq_easy/BisqEasyService.java @@ -21,6 +21,7 @@ import bisq.bonded_roles.BondedRolesService; import bisq.chat.ChatService; import bisq.common.application.Service; +import bisq.common.observable.Observable; import bisq.common.observable.Pin; import bisq.contract.ContractService; import bisq.identity.IdentityService; @@ -63,7 +64,10 @@ public class BisqEasyService implements Service { private final TradeService tradeService; private final UserIdentityService userIdentityService; private final BisqEasyNotificationsService bisqEasyNotificationsService; - private Pin difficultyAdjustmentFactorPin, ignoreDiffAdjustmentFromSecManagerPin, mostRecentValueOrDefaultPin; + private final Observable minRequiredReputationScore = new Observable<>(); + private Pin difficultyAdjustmentFactorPin, ignoreDiffAdjustmentFromSecManagerPin, + mostRecentDiffAdjustmentValueOrDefaultPin, minRequiredReputationScorePin, + ignoreMinRequiredReputationScoreFromSecManagerPin, mostRecentMinRequiredReputationScoreOrDefaultPin; public BisqEasyService(PersistenceService persistenceService, SecurityService securityService, @@ -110,7 +114,12 @@ public CompletableFuture initialize() { log.info("initialize"); difficultyAdjustmentFactorPin = settingsService.getDifficultyAdjustmentFactor().addObserver(e -> applyDifficultyAdjustmentFactor()); ignoreDiffAdjustmentFromSecManagerPin = settingsService.getIgnoreDiffAdjustmentFromSecManager().addObserver(e -> applyDifficultyAdjustmentFactor()); - mostRecentValueOrDefaultPin = bondedRolesService.getDifficultyAdjustmentService().getMostRecentValueOrDefault().addObserver(e -> applyDifficultyAdjustmentFactor()); + mostRecentDiffAdjustmentValueOrDefaultPin = bondedRolesService.getDifficultyAdjustmentService().getMostRecentValueOrDefault().addObserver(e -> applyDifficultyAdjustmentFactor()); + + minRequiredReputationScorePin = settingsService.getMinRequiredReputationScore().addObserver(e -> applyMinRequiredReputationScore()); + ignoreMinRequiredReputationScoreFromSecManagerPin = settingsService.getIgnoreMinRequiredReputationScoreFromSecManager().addObserver(e -> applyMinRequiredReputationScore()); + mostRecentMinRequiredReputationScoreOrDefaultPin = bondedRolesService.getMinRequiredReputationScoreService().getMostRecentValueOrDefault().addObserver(e -> applyMinRequiredReputationScore()); + return bisqEasyNotificationsService.initialize(); } @@ -119,7 +128,10 @@ public CompletableFuture shutdown() { if (difficultyAdjustmentFactorPin != null) { difficultyAdjustmentFactorPin.unbind(); ignoreDiffAdjustmentFromSecManagerPin.unbind(); - mostRecentValueOrDefaultPin.unbind(); + mostRecentDiffAdjustmentValueOrDefaultPin.unbind(); + minRequiredReputationScorePin.unbind(); + ignoreMinRequiredReputationScoreFromSecManagerPin.unbind(); + mostRecentMinRequiredReputationScoreOrDefaultPin.unbind(); } return bisqEasyNotificationsService.shutdown(); } @@ -145,4 +157,12 @@ private void applyDifficultyAdjustmentFactor() { } }); } + + private void applyMinRequiredReputationScore() { + if (settingsService.getIgnoreMinRequiredReputationScoreFromSecManager().get()) { + minRequiredReputationScore.set(settingsService.getMinRequiredReputationScore().get()); + } else { + minRequiredReputationScore.set(bondedRolesService.getMinRequiredReputationScoreService().getMostRecentValueOrDefault().get()); + } + } } diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/BondedRolesService.java b/bonded-roles/src/main/java/bisq/bonded_roles/BondedRolesService.java index ac7a2c1158..69cc8b49cb 100644 --- a/bonded-roles/src/main/java/bisq/bonded_roles/BondedRolesService.java +++ b/bonded-roles/src/main/java/bisq/bonded_roles/BondedRolesService.java @@ -24,6 +24,7 @@ import bisq.bonded_roles.release.ReleaseNotificationsService; import bisq.bonded_roles.security_manager.alert.AlertService; import bisq.bonded_roles.security_manager.difficulty_adjustment.DifficultyAdjustmentService; +import bisq.bonded_roles.security_manager.min_reputation_score.MinRequiredReputationScoreService; import bisq.common.application.Service; import bisq.common.util.Version; import bisq.network.NetworkService; @@ -63,6 +64,7 @@ public static Config from(com.typesafe.config.Config config) { private final ExplorerService explorerService; private final AlertService alertService; private final DifficultyAdjustmentService difficultyAdjustmentService; + private final MinRequiredReputationScoreService minRequiredReputationScoreService; private final ReleaseNotificationsService releaseNotificationsService; public BondedRolesService(Config config, Version version, PersistenceService persistenceService, NetworkService networkService) { @@ -74,10 +76,10 @@ public BondedRolesService(Config config, Version version, PersistenceService per version); alertService = new AlertService(authorizedBondedRolesService); difficultyAdjustmentService = new DifficultyAdjustmentService(authorizedBondedRolesService); + minRequiredReputationScoreService = new MinRequiredReputationScoreService(authorizedBondedRolesService); releaseNotificationsService = new ReleaseNotificationsService(authorizedBondedRolesService); } - /////////////////////////////////////////////////////////////////////////////////////////////////// // Service /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -85,6 +87,7 @@ public BondedRolesService(Config config, Version version, PersistenceService per public CompletableFuture initialize() { log.info("initialize"); return difficultyAdjustmentService.initialize() + .thenCompose(result -> minRequiredReputationScoreService.initialize()) .thenCompose(result -> alertService.initialize()) .thenCompose(result -> bondedRoleRegistrationService.initialize()) .thenCompose(result -> marketPriceService.initialize()) @@ -96,6 +99,7 @@ public CompletableFuture initialize() { public CompletableFuture shutdown() { return authorizedBondedRolesService.shutdown() .thenCompose(result -> difficultyAdjustmentService.shutdown()) + .thenCompose(result -> minRequiredReputationScoreService.shutdown()) .thenCompose(result -> alertService.shutdown()) .thenCompose(result -> bondedRoleRegistrationService.shutdown()) .thenCompose(result -> marketPriceService.shutdown()) diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/difficulty_adjustment/AuthorizedDifficultyAdjustmentData.java b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/difficulty_adjustment/AuthorizedDifficultyAdjustmentData.java index 15bbf5c033..9856df6ae7 100644 --- a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/difficulty_adjustment/AuthorizedDifficultyAdjustmentData.java +++ b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/difficulty_adjustment/AuthorizedDifficultyAdjustmentData.java @@ -41,8 +41,6 @@ @EqualsAndHashCode @Getter public final class AuthorizedDifficultyAdjustmentData implements AuthorizedDistributedData { - public final static int MAX_MESSAGE_LENGTH = 1000; - private final MetaData metaData = new MetaData(TTL_100_DAYS, HIGH_PRIORITY, getClass().getSimpleName()); private final long date; private final double difficultyAdjustmentFactor; diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/difficulty_adjustment/DifficultyAdjustmentService.java b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/difficulty_adjustment/DifficultyAdjustmentService.java index cdc78a909c..7bbe276b4c 100644 --- a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/difficulty_adjustment/DifficultyAdjustmentService.java +++ b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/difficulty_adjustment/DifficultyAdjustmentService.java @@ -37,9 +37,6 @@ */ @Slf4j public class DifficultyAdjustmentService implements Service, AuthorizedBondedRolesService.Listener { - - @Getter - private final Observable hasNotificationSenderIdentity = new Observable<>(); @Getter private final Observable mostRecentValueOrDefault = new Observable<>(); private final AuthorizedBondedRolesService authorizedBondedRolesService; diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/min_reputation_score/AuthorizedMinRequiredReputationScoreData.java b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/min_reputation_score/AuthorizedMinRequiredReputationScoreData.java new file mode 100644 index 0000000000..04796f8dd4 --- /dev/null +++ b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/min_reputation_score/AuthorizedMinRequiredReputationScoreData.java @@ -0,0 +1,118 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.bonded_roles.security_manager.min_reputation_score; + +import bisq.bonded_roles.AuthorizedPubKeys; +import bisq.common.application.DevMode; +import bisq.common.proto.ProtoResolver; +import bisq.common.proto.UnresolvableProtobufMessageException; +import bisq.common.validation.NetworkDataValidation; +import bisq.network.p2p.services.data.storage.DistributedData; +import bisq.network.p2p.services.data.storage.MetaData; +import bisq.network.p2p.services.data.storage.auth.authorized.AuthorizedDistributedData; +import com.google.protobuf.InvalidProtocolBufferException; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + +import java.util.Set; + +import static bisq.network.p2p.services.data.storage.MetaData.HIGH_PRIORITY; +import static bisq.network.p2p.services.data.storage.MetaData.TTL_100_DAYS; + +@Slf4j +@ToString +@EqualsAndHashCode +@Getter +public final class AuthorizedMinRequiredReputationScoreData implements AuthorizedDistributedData { + private final MetaData metaData = new MetaData(TTL_100_DAYS, HIGH_PRIORITY, getClass().getSimpleName()); + private final long date; + private final long minRequiredReputationScore; + private final String securityManagerProfileId; + private final boolean staticPublicKeysProvided; + + public AuthorizedMinRequiredReputationScoreData(long date, + long minRequiredReputationScore, + String securityManagerProfileId, + boolean staticPublicKeysProvided) { + this.date = date; + this.minRequiredReputationScore = minRequiredReputationScore; + this.securityManagerProfileId = securityManagerProfileId; + this.staticPublicKeysProvided = staticPublicKeysProvided; + + verify(); + } + + @Override + public void verify() { + NetworkDataValidation.validateDate(date); + NetworkDataValidation.validateProfileId(securityManagerProfileId); + } + + @Override + public bisq.bonded_roles.protobuf.AuthorizedMinRequiredReputationScoreData toProto() { + bisq.bonded_roles.protobuf.AuthorizedMinRequiredReputationScoreData.Builder builder = bisq.bonded_roles.protobuf.AuthorizedMinRequiredReputationScoreData.newBuilder() + .setDate(date) + .setMinRequiredReputationScore(minRequiredReputationScore) + .setSecurityManagerProfileId(securityManagerProfileId) + .setStaticPublicKeysProvided(staticPublicKeysProvided); + return builder.build(); + } + + public static AuthorizedMinRequiredReputationScoreData fromProto(bisq.bonded_roles.protobuf.AuthorizedMinRequiredReputationScoreData proto) { + return new AuthorizedMinRequiredReputationScoreData(proto.getDate(), + proto.getMinRequiredReputationScore(), + proto.getSecurityManagerProfileId(), + proto.getStaticPublicKeysProvided()); + } + + public static ProtoResolver getResolver() { + return any -> { + try { + return fromProto(any.unpack(bisq.bonded_roles.protobuf.AuthorizedMinRequiredReputationScoreData.class)); + } catch (InvalidProtocolBufferException e) { + throw new UnresolvableProtobufMessageException(e); + } + }; + } + + @Override + public double getCostFactor() { + return 0.5; + } + + @Override + public Set getAuthorizedPublicKeys() { + if (DevMode.isDevMode()) { + return AuthorizedPubKeys.DEV_PUB_KEYS; + } else { + return AuthorizedPubKeys.SECURITY_MANAGER_PUB_KEYS; + } + } + + @Override + public boolean staticPublicKeysProvided() { + return staticPublicKeysProvided; + } + + @Override + public boolean isDataInvalid(byte[] pubKeyHash) { + return false; + } +} \ No newline at end of file diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/min_reputation_score/MinRequiredReputationScoreService.java b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/min_reputation_score/MinRequiredReputationScoreService.java new file mode 100644 index 0000000000..b580c2701d --- /dev/null +++ b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/min_reputation_score/MinRequiredReputationScoreService.java @@ -0,0 +1,104 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.bonded_roles.security_manager.min_reputation_score; + +import bisq.bonded_roles.BondedRoleType; +import bisq.bonded_roles.bonded_role.AuthorizedBondedRolesService; +import bisq.common.application.Service; +import bisq.common.observable.Observable; +import bisq.common.observable.collection.ObservableSet; +import bisq.network.p2p.services.data.storage.auth.authorized.AuthorizedData; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.util.Comparator; +import java.util.concurrent.CompletableFuture; + +@Slf4j +public class MinRequiredReputationScoreService implements Service, AuthorizedBondedRolesService.Listener { + public final static long DEFAULT_MIN_REPUTATION_SCORE = 10_000; + @Getter + private final Observable hasNotificationSenderIdentity = new Observable<>(); + @Getter + private final Observable mostRecentValueOrDefault = new Observable<>(); + private final AuthorizedBondedRolesService authorizedBondedRolesService; + @Getter + private final ObservableSet authorizedMinRequiredReputationScoreDataSet = new ObservableSet<>(); + + public MinRequiredReputationScoreService(AuthorizedBondedRolesService authorizedBondedRolesService) { + this.authorizedBondedRolesService = authorizedBondedRolesService; + } + + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // Service + /////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public CompletableFuture initialize() { + authorizedBondedRolesService.addListener(this); + updateMostRecentValueOrDefault(); + return CompletableFuture.completedFuture(true); + } + + @Override + public CompletableFuture shutdown() { + authorizedBondedRolesService.removeListener(this); + return CompletableFuture.completedFuture(true); + } + + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // AuthorizedBondedRolesService.Listener + /////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void onAuthorizedDataAdded(AuthorizedData authorizedData) { + if (authorizedData.getAuthorizedDistributedData() instanceof AuthorizedMinRequiredReputationScoreData) { + if (isAuthorized(authorizedData)) { + AuthorizedMinRequiredReputationScoreData authorizedMinRequiredReputationScoreData = (AuthorizedMinRequiredReputationScoreData) authorizedData.getAuthorizedDistributedData(); + authorizedMinRequiredReputationScoreDataSet.add(authorizedMinRequiredReputationScoreData); + updateMostRecentValueOrDefault(); + } + } + } + + @Override + public void onAuthorizedDataRemoved(AuthorizedData authorizedData) { + if (authorizedData.getAuthorizedDistributedData() instanceof AuthorizedMinRequiredReputationScoreData) { + if (isAuthorized(authorizedData)) { + AuthorizedMinRequiredReputationScoreData authorizedMinRequiredReputationScoreData = (AuthorizedMinRequiredReputationScoreData) authorizedData.getAuthorizedDistributedData(); + authorizedMinRequiredReputationScoreDataSet.remove(authorizedMinRequiredReputationScoreData); + updateMostRecentValueOrDefault(); + } + } + } + + private boolean isAuthorized(AuthorizedData authorizedData) { + return authorizedBondedRolesService.hasAuthorizedPubKey(authorizedData, BondedRoleType.SECURITY_MANAGER); + } + + private void updateMostRecentValueOrDefault() { + long value = authorizedMinRequiredReputationScoreDataSet.stream() + .sorted(Comparator.comparingLong(AuthorizedMinRequiredReputationScoreData::getDate).reversed()) + .map(AuthorizedMinRequiredReputationScoreData::getMinRequiredReputationScore) + .findFirst() + .orElse(DEFAULT_MIN_REPUTATION_SCORE); + mostRecentValueOrDefault.set(value); + } +} \ No newline at end of file diff --git a/bonded-roles/src/main/proto/bonded_roles.proto b/bonded-roles/src/main/proto/bonded_roles.proto index 5be159eec2..6d09f72623 100644 --- a/bonded-roles/src/main/proto/bonded_roles.proto +++ b/bonded-roles/src/main/proto/bonded_roles.proto @@ -90,6 +90,13 @@ message AuthorizedDifficultyAdjustmentData { bool staticPublicKeysProvided = 4; } +message AuthorizedMinRequiredReputationScoreData { + sint64 date = 1; + sint64 minRequiredReputationScore = 2; + string securityManagerProfileId = 3; + bool staticPublicKeysProvided = 4; +} + enum MarketPriceProvider { MARKETPRICEPROVIDER_UNSPECIFIED = 0; MARKETPRICEPROVIDER_BISQAGGREGATE = 1; diff --git a/i18n/src/main/resources/authorized_role.properties b/i18n/src/main/resources/authorized_role.properties index 5424252fd2..5a95493d24 100644 --- a/i18n/src/main/resources/authorized_role.properties +++ b/i18n/src/main/resources/authorized_role.properties @@ -97,8 +97,14 @@ authorizedRole.securityManager.difficultyAdjustment.headline=Pow difficulty adju authorizedRole.securityManager.difficultyAdjustment.description=Difficulty adjustment factor authorizedRole.securityManager.difficultyAdjustment.invalid=Must be a number between 0 and {0} authorizedRole.securityManager.difficultyAdjustment.button=Publish difficulty adjustment factor -authorizedRole.securityManager.difficultyAdjustment.table.headline=Difficulty adjustment factors +authorizedRole.securityManager.difficultyAdjustment.table.headline=Published difficulty adjustment data authorizedRole.securityManager.difficultyAdjustment.table.value=Value +authorizedRole.securityManager.minRequiredReputationScore.headline=Min. required reputation score +authorizedRole.securityManager.minRequiredReputationScore.description=Min. required reputation score +authorizedRole.securityManager.minRequiredReputationScore.invalid=Must be a number between 0 and {0} +authorizedRole.securityManager.minRequiredReputationScore.button=Publish score +authorizedRole.securityManager.minRequiredReputationScore.table.headline=Published reputation score data +authorizedRole.securityManager.minRequiredReputationScore.table.value=Value authorizedRole.securityManager.alert.headline=Management of alerts authorizedRole.securityManager.selectAlertType=Select alert type diff --git a/i18n/src/main/resources/settings.properties b/i18n/src/main/resources/settings.properties index 8d696bb07f..0a127c2b5e 100644 --- a/i18n/src/main/resources/settings.properties +++ b/i18n/src/main/resources/settings.properties @@ -37,14 +37,17 @@ settings.preferences.display.resetDontShowAgain=Reset all 'Don't show again' fla settings.preferences.trade.headline=Offer and trade settings settings.preferences.trade.requiredTotalReputationScore=Min. required reputation score -settings.preferences.trade.requiredTotalReputationScore.invalid=Must be a number settings.preferences.trade.requiredTotalReputationScore.help=Restrict trading to trader with a min. reputation score +settings.preferences.network.minReputationScore.description.self=Custom min. required reputation score +settings.preferences.network.minReputationScore.description.fromSecManager=Min. required reputation score by Bisq Security Manager +settings.preferences.trade.minReputationScore.invalid=Must be a number between 0 and {0} +settings.preferences.network.minReputationScore.ignoreValueFromSecManager=Ignore value provided by Bisq Security Manager settings.preferences.trade.closeMyOfferWhenTaken=Close my offer when taken settings.preferences.network.headline=Network settings settings.preferences.network.difficultyAdjustmentFactor.description.self=Custom PoW difficulty adjustment factor settings.preferences.network.difficultyAdjustmentFactor.description.fromSecManager=PoW difficulty adjustment factor by Bisq Security Manager settings.preferences.network.difficultyAdjustmentFactor.invalid=Must be a number between 0 and {0} -settings.preferences.network.ignoreDifficultyAdjustmentFactorFromSecManager=Ignore value provided by Bisq Security Manager +settings.preferences.network.difficultyAdjustmentFactor.ignoreValueFromSecManager=Ignore value provided by Bisq Security Manager #################################################################### # Settings / Network diff --git a/settings/src/main/java/bisq/settings/SettingsService.java b/settings/src/main/java/bisq/settings/SettingsService.java index 84292cfb6e..6df8621f30 100644 --- a/settings/src/main/java/bisq/settings/SettingsService.java +++ b/settings/src/main/java/bisq/settings/SettingsService.java @@ -78,10 +78,11 @@ public CompletableFuture initialize() { getDifficultyAdjustmentFactor().addObserver(value -> persist()); getIgnoreDiffAdjustmentFromSecManager().addObserver(value -> persist()); getFavouriteMarkets().addObserver(this::persist); + getIgnoreMinRequiredReputationScoreFromSecManager().addObserver(value -> persist()); isInitialized = true; - if (DevMode.isDevMode() && - getMinRequiredReputationScore().get() == DEFAULT_MIN_REQUIRED_REPUTATION_SCORE) { + if (DevMode.isDevMode() && getMinRequiredReputationScore().get() == DEFAULT_MIN_REQUIRED_REPUTATION_SCORE) { + getIgnoreMinRequiredReputationScoreFromSecManager().set(true); getMinRequiredReputationScore().set(0L); log.info("In dev mode we set getMinRequiredReputationScore to 0 if it was the default value of {}", DEFAULT_MIN_REQUIRED_REPUTATION_SCORE); @@ -148,6 +149,10 @@ public Observable getIgnoreDiffAdjustmentFromSecManager() { return persistableStore.ignoreDiffAdjustmentFromSecManager; } + public Observable getIgnoreMinRequiredReputationScoreFromSecManager() { + return persistableStore.ignoreMinRequiredReputationScoreFromSecManager; + } + public Observable getDifficultyAdjustmentFactor() { return persistableStore.difficultyAdjustmentFactor; } diff --git a/settings/src/main/java/bisq/settings/SettingsStore.java b/settings/src/main/java/bisq/settings/SettingsStore.java index da9bf23a41..7460cf97ac 100644 --- a/settings/src/main/java/bisq/settings/SettingsStore.java +++ b/settings/src/main/java/bisq/settings/SettingsStore.java @@ -52,6 +52,7 @@ public final class SettingsStore implements PersistableStore { final Observable difficultyAdjustmentFactor = new Observable<>(); final Observable ignoreDiffAdjustmentFromSecManager = new Observable<>(); final ObservableSet favouriteMarkets = new ObservableSet<>(); + final Observable ignoreMinRequiredReputationScoreFromSecManager = new Observable<>(); public SettingsStore() { this(new Cookie(), @@ -70,7 +71,8 @@ public SettingsStore() { Set.of(LanguageRepository.getDefaultLanguage()), NetworkLoad.DEFAULT_DIFFICULTY_ADJUSTMENT, false, - new HashSet<>()); + new HashSet<>(), + false); } public SettingsStore(Cookie cookie, @@ -89,7 +91,8 @@ public SettingsStore(Cookie cookie, Set supportedLanguageCodes, double difficultyAdjustmentFactor, boolean ignoreDiffAdjustmentFromSecManager, - Set favouriteMarkets) { + Set favouriteMarkets, + boolean ignoreMinRequiredReputationScoreFromSecManager) { this.cookie = cookie; this.dontShowAgainMap.putAll(dontShowAgainMap); this.useAnimations.set(useAnimations); @@ -107,6 +110,7 @@ public SettingsStore(Cookie cookie, this.difficultyAdjustmentFactor.set(difficultyAdjustmentFactor); this.ignoreDiffAdjustmentFromSecManager.set(ignoreDiffAdjustmentFromSecManager); this.favouriteMarkets.setAll(favouriteMarkets); + this.ignoreMinRequiredReputationScoreFromSecManager.set(ignoreMinRequiredReputationScoreFromSecManager); } @Override @@ -129,6 +133,7 @@ public bisq.settings.protobuf.SettingsStore toProto() { .setDifficultyAdjustmentFactor(difficultyAdjustmentFactor.get()) .setIgnoreDiffAdjustmentFromSecManager(ignoreDiffAdjustmentFromSecManager.get()) .addAllFavouriteMarkets(favouriteMarkets.stream().map(Market::toProto).collect(Collectors.toList())) + .setIgnoreMinRequiredReputationScoreFromSecManager(ignoreMinRequiredReputationScoreFromSecManager.get()) .build(); } @@ -151,7 +156,8 @@ public static SettingsStore fromProto(bisq.settings.protobuf.SettingsStore proto proto.getDifficultyAdjustmentFactor(), proto.getIgnoreDiffAdjustmentFromSecManager(), new HashSet<>(proto.getFavouriteMarketsList().stream() - .map(Market::fromProto).collect(Collectors.toSet()))); + .map(Market::fromProto).collect(Collectors.toSet())), + proto.getIgnoreMinRequiredReputationScoreFromSecManager()); } @Override @@ -183,7 +189,8 @@ public SettingsStore getClone() { new HashSet<>(supportedLanguageCodes), difficultyAdjustmentFactor.get(), ignoreDiffAdjustmentFromSecManager.get(), - new HashSet<>(favouriteMarkets)); + new HashSet<>(favouriteMarkets), + ignoreMinRequiredReputationScoreFromSecManager.get()); } @Override @@ -206,6 +213,7 @@ public void applyPersisted(SettingsStore persisted) { difficultyAdjustmentFactor.set(persisted.difficultyAdjustmentFactor.get()); ignoreDiffAdjustmentFromSecManager.set(persisted.ignoreDiffAdjustmentFromSecManager.get()); favouriteMarkets.setAll(persisted.favouriteMarkets); + ignoreMinRequiredReputationScoreFromSecManager.set(persisted.ignoreMinRequiredReputationScoreFromSecManager.get()); } catch (Exception e) { log.error("Exception at applyPersisted", e); } diff --git a/settings/src/main/proto/settings.proto b/settings/src/main/proto/settings.proto index 9de7a18422..c9880c64fc 100644 --- a/settings/src/main/proto/settings.proto +++ b/settings/src/main/proto/settings.proto @@ -55,4 +55,5 @@ message SettingsStore { double difficultyAdjustmentFactor = 15; bool ignoreDiffAdjustmentFromSecManager = 16; repeated common.Market favouriteMarkets = 17; + bool ignoreMinRequiredReputationScoreFromSecManager = 18; } diff --git a/support/src/main/java/bisq/support/security_manager/SecurityManagerService.java b/support/src/main/java/bisq/support/security_manager/SecurityManagerService.java index 42439004fe..4d7476d258 100644 --- a/support/src/main/java/bisq/support/security_manager/SecurityManagerService.java +++ b/support/src/main/java/bisq/support/security_manager/SecurityManagerService.java @@ -23,6 +23,7 @@ import bisq.bonded_roles.security_manager.alert.AlertType; import bisq.bonded_roles.security_manager.alert.AuthorizedAlertData; import bisq.bonded_roles.security_manager.difficulty_adjustment.AuthorizedDifficultyAdjustmentData; +import bisq.bonded_roles.security_manager.min_reputation_score.AuthorizedMinRequiredReputationScoreData; import bisq.common.application.Service; import bisq.common.observable.Observable; import bisq.common.util.StringUtils; @@ -146,8 +147,31 @@ public CompletableFuture publishDifficultyAdjustment(double difficultyA .thenApply(broadCastDataResult -> true); } + public CompletableFuture publishMinRequiredReputationScore(long minRequiredReputationScore) { + UserIdentity userIdentity = checkNotNull(userIdentityService.getSelectedUserIdentity()); + String profileId = userIdentity.getId(); + KeyPair keyPair = userIdentity.getIdentity().getKeyBundle().getKeyPair(); + PublicKey authorizedPublicKey = keyPair.getPublic(); + PrivateKey authorizedPrivateKey = keyPair.getPrivate(); + AuthorizedMinRequiredReputationScoreData data = new AuthorizedMinRequiredReputationScoreData(new Date().getTime(), + minRequiredReputationScore, + profileId, + staticPublicKeysProvided); + return networkService.publishAuthorizedData(data, + keyPair, + authorizedPrivateKey, + authorizedPublicKey) + .thenApply(broadCastDataResult -> true); + } + public CompletableFuture removeDifficultyAdjustment(AuthorizedDifficultyAdjustmentData data, KeyPair ownerKeyPair) { return networkService.removeAuthorizedData(data, ownerKeyPair, ownerKeyPair.getPublic()) .thenApply(broadCastDataResult -> true); } + + public CompletableFuture removeMinRequiredReputationScore(AuthorizedMinRequiredReputationScoreData data, + KeyPair ownerKeyPair) { + return networkService.removeAuthorizedData(data, ownerKeyPair, ownerKeyPair.getPublic()) + .thenApply(broadCastDataResult -> true); + } } \ No newline at end of file diff --git a/user/src/main/java/bisq/user/reputation/ReputationScore.java b/user/src/main/java/bisq/user/reputation/ReputationScore.java index 48e9672e73..c1f377e07c 100644 --- a/user/src/main/java/bisq/user/reputation/ReputationScore.java +++ b/user/src/main/java/bisq/user/reputation/ReputationScore.java @@ -29,6 +29,7 @@ @EqualsAndHashCode @Getter public class ReputationScore implements Comparable { + public final static double MAX_VALUE = 10_000_000; public static final ReputationScore NONE = new ReputationScore(0, 0, 0, 0); private final long totalScore; From 796ad5903edd670fa029f8e0b15de6f3a086edb0 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Fri, 22 Mar 2024 22:04:00 +0700 Subject: [PATCH 041/119] Increase limit for btc addresses and btx txs for more flexibility when used for non-mainnet-bitcoin --- .../protocol/messages/BisqEasyBtcAddressMessageHandler.java | 2 +- .../protocol/messages/BisqEasyConfirmBtcSentMessageHandler.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/trade/src/main/java/bisq/trade/bisq_easy/protocol/messages/BisqEasyBtcAddressMessageHandler.java b/trade/src/main/java/bisq/trade/bisq_easy/protocol/messages/BisqEasyBtcAddressMessageHandler.java index 05203940c0..f377dea340 100644 --- a/trade/src/main/java/bisq/trade/bisq_easy/protocol/messages/BisqEasyBtcAddressMessageHandler.java +++ b/trade/src/main/java/bisq/trade/bisq_easy/protocol/messages/BisqEasyBtcAddressMessageHandler.java @@ -48,7 +48,7 @@ protected void verifyMessage(BisqEasyBtcAddressMessage message) { checkArgument(StringUtils.isNotEmpty(message.getBtcAddress())); // We leave it flexible so that users can use other than a BTC address data as btcAddress - checkArgument(message.getBtcAddress().length() <= 100); + checkArgument(message.getBtcAddress().length() <= 1000); checkNotNull(message.getBisqEasyOffer()); } diff --git a/trade/src/main/java/bisq/trade/bisq_easy/protocol/messages/BisqEasyConfirmBtcSentMessageHandler.java b/trade/src/main/java/bisq/trade/bisq_easy/protocol/messages/BisqEasyConfirmBtcSentMessageHandler.java index 267cfe71a4..71ec72e9f2 100644 --- a/trade/src/main/java/bisq/trade/bisq_easy/protocol/messages/BisqEasyConfirmBtcSentMessageHandler.java +++ b/trade/src/main/java/bisq/trade/bisq_easy/protocol/messages/BisqEasyConfirmBtcSentMessageHandler.java @@ -47,7 +47,7 @@ protected void verifyMessage(BisqEasyConfirmBtcSentMessage message) { checkArgument(StringUtils.isNotEmpty(message.getTxId())); // We leave it flexible so that users can use other than BTC mainnet data as txId - checkArgument(message.getTxId().length() <= 100); + checkArgument(message.getTxId().length() <= 1000); } private void commitToModel(String txId) { From 83fd5a4d4738c5ff259d698cae85870d4f611c80 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Fri, 22 Mar 2024 22:15:25 +0700 Subject: [PATCH 042/119] Increase price tolerance --- .../protocol/messages/BisqEasyTakeOfferRequestHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trade/src/main/java/bisq/trade/bisq_easy/protocol/messages/BisqEasyTakeOfferRequestHandler.java b/trade/src/main/java/bisq/trade/bisq_easy/protocol/messages/BisqEasyTakeOfferRequestHandler.java index 9c9d370df5..00dd4339b0 100644 --- a/trade/src/main/java/bisq/trade/bisq_easy/protocol/messages/BisqEasyTakeOfferRequestHandler.java +++ b/trade/src/main/java/bisq/trade/bisq_easy/protocol/messages/BisqEasyTakeOfferRequestHandler.java @@ -130,7 +130,7 @@ private void validateAmount(BisqEasyOffer takersOffer, BisqEasyContract takersCo Optional amount = getAmount(takersOffer, takersContract); checkArgument(amount.isPresent(), "No market price available for validation."); - double tolerancePercentage = 0.01; + double tolerancePercentage = 0.03; long tolerance = (long) (amount.get().getValue() * tolerancePercentage); long minAmountWithTolerance = amount.get().getValue() - tolerance; long maxAmountWithTolerance = amount.get().getValue() + tolerance; From 52e7db305b4c347b8a6c80f0c2704389cbd1c7bd Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Fri, 22 Mar 2024 22:36:35 +0700 Subject: [PATCH 043/119] Change text of Active users and add tooltip --- .../bisq/desktop/main/content/dashboard/DashboardView.java | 5 +++++ apps/desktop/desktop/src/main/resources/css/controls.css | 7 +++++++ i18n/src/main/resources/application.properties | 3 ++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/dashboard/DashboardView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/dashboard/DashboardView.java index 248d0ecdb8..76ecfe5667 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/dashboard/DashboardView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/dashboard/DashboardView.java @@ -22,6 +22,7 @@ import bisq.desktop.common.threading.UIScheduler; import bisq.desktop.common.utils.GridPaneUtil; import bisq.desktop.common.view.View; +import bisq.desktop.components.controls.BisqTooltip; import bisq.desktop.main.notification.NotificationPanelView; import bisq.i18n.Res; import javafx.geometry.Insets; @@ -29,6 +30,7 @@ import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; +import javafx.scene.control.Tooltip; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; @@ -72,6 +74,9 @@ public DashboardView(DashboardModel model, DashboardController controller) { Pair usersPair = getValueBox(Res.get("dashboard.activeUsers")); VBox activeUsers = usersPair.getFirst(); activeUsersLabel = usersPair.getSecond(); + BisqTooltip tooltip = new BisqTooltip(Res.get("dashboard.activeUsers.tooltip")); + tooltip.setId("large-tooltip"); + Tooltip.install(activeUsers, tooltip); HBox.setMargin(marketPrice, new Insets(0, -100, 0, -30)); HBox hBox = new HBox(16, marketPrice, offersOnline, activeUsers); diff --git a/apps/desktop/desktop/src/main/resources/css/controls.css b/apps/desktop/desktop/src/main/resources/css/controls.css index 9ba51780aa..7b37a1cdce 100644 --- a/apps/desktop/desktop/src/main/resources/css/controls.css +++ b/apps/desktop/desktop/src/main/resources/css/controls.css @@ -744,6 +744,13 @@ -fx-font-size: 0.85em; } +#large-tooltip { + -fx-font-size: 1.2em; + -fx-font-family: "IBM Plex Sans Light"; + -fx-text-fill: -fx-dark-text-color; + -fx-padding: 0.3em; +} + /******************************************************************************* * * diff --git a/i18n/src/main/resources/application.properties b/i18n/src/main/resources/application.properties index 6ae04d81d9..4a6959b49a 100644 --- a/i18n/src/main/resources/application.properties +++ b/i18n/src/main/resources/application.properties @@ -198,7 +198,8 @@ topPanel.wallet.balance=Balance dashboard.marketPrice=Latest market price dashboard.offersOnline=Offers online -dashboard.activeUsers=Active users +dashboard.activeUsers=Active user profiles +dashboard.activeUsers.tooltip=Number of active user profiles in last 15 days dashboard.main.headline=Get your first BTC dashboard.main.content1=Start trading or browse open offers in the offerbook dashboard.main.content2=Chat based and guided user interface for trading From d4c41bfbc72046d96997520eb21067f1fe42340d Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sat, 23 Mar 2024 00:34:08 +0700 Subject: [PATCH 044/119] Add copy button --- .../main/content/user/bonded_roles/nodes/NodesView.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/bonded_roles/nodes/NodesView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/bonded_roles/nodes/NodesView.java index 47d5819a69..36e905345c 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/bonded_roles/nodes/NodesView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/bonded_roles/nodes/NodesView.java @@ -17,6 +17,7 @@ package bisq.desktop.main.content.user.bonded_roles.nodes; +import bisq.desktop.common.utils.ClipboardUtil; import bisq.desktop.components.controls.BisqIconButton; import bisq.desktop.components.controls.BisqTooltip; import bisq.desktop.components.overlay.Popup; @@ -133,6 +134,8 @@ public void updateItem(final BondedRolesListItem item, boolean empty) { icon.setOnAction(e -> new Popup() .headline(Res.get("user.bondedRoles.table.columns.node.address.popup.headline")) .message(item.getAddressInfoJson()) + .onAction(() -> ClipboardUtil.copyToClipboard(item.getAddressInfoJson())) + .actionButtonText(Res.get("action.copyToClipboard")) .show()); setGraphic(hBox); } else { From 678f45f8f5d18264327e7d8e827bc1a9dd252548 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sat, 23 Mar 2024 00:38:24 +0700 Subject: [PATCH 045/119] Add seed and market provider addresses --- apps/desktop/desktop-app/src/main/resources/desktop.conf | 8 +++++++- .../oracle-node-app/src/main/resources/oracle_node.conf | 8 +++++++- apps/rest-api-app/src/main/resources/rest_api.conf | 8 +++++++- apps/seed-node-app/src/main/resources/seed_node.conf | 8 +++++++- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/apps/desktop/desktop-app/src/main/resources/desktop.conf b/apps/desktop/desktop-app/src/main/resources/desktop.conf index f30201f52c..814bb78b1d 100644 --- a/apps/desktop/desktop-app/src/main/resources/desktop.conf +++ b/apps/desktop/desktop-app/src/main/resources/desktop.conf @@ -33,6 +33,11 @@ application { // Production node, bonded role url = "http://runbtcpn7gmbj5rgqeyfyvepqokrijem6rbw7o5wgqbguimuoxrmcdyd.onion/" operator = "runbtc", + }, + { + // Production node, bonded role + url = "http://pn4o3iocd6wck4wrpj3j53hsknn5q4pqjmoircj4jzggmfuiszonxuid.onion/" + operator = "403State", } ] } @@ -117,7 +122,8 @@ application { ] "tor" : [ "hj2kj43oyq4mhd5gx4vokalnci3vlbwzclv7usocfwuj5f5iks3eivqd.onion:1000", - "plur5t7zhcf45bltdhtb4o43p726dqhibc6xo2lhjfxctjcoinlclaid.onion:1000" + "plur5t7zhcf45bltdhtb4o43p726dqhibc6xo2lhjfxctjcoinlclaid.onion:1000", + "b5jjgzuekdu7ys7yg2q6e27hybtufxfvdsxxvle7xpvqifyagdag5fid.onion:1000" ] "i2p" : [ "kglZCQYj~nyK3YlXCD5FjxOY2ggH8yosII0rqc7oqFhFfjKWy-89WYw-~mtTUqzCaN6LGd17XzheKG44XJnKrM-WvP732V8lbJcoMBIKeeHPlcfwpsTNbMJyWeXIlJByYNlw1HPVRMpBtzfJ9IznyQdwQWDkzA72pLreqpzJrgIoVYzP9OTXVLdROXnTP9RdmnzZ0h1B8XhQM-8LjHB7cE9o9VT9IXIFScICM8VZ8I1sp02rn26McTM~~XO5Zs1Df3IMV0eqteAe6TvH~Rc-6Hh3YhPrjEcv-YvV6RUlsoj605mmSO0Sj5oeacH3Cec73BlNJEGfQkmbTrXVNLqt2S4smqmkAhMq~sdCJCRKP8CFeBk6r-qVREucTeW3AmwXuGS~-8s7pAm99SlpTSepp75a2WNTIsWw~rWiHlM6faTJrkjcO5wJM7~G0tdYgVGk4zrt4VJ02AakUdh8wG1Y5sAX-daTUum~0YTk-fIAVBJSEiNc93XgZkwuTcc4J2BqAAAA:5000", diff --git a/apps/oracle-node/oracle-node-app/src/main/resources/oracle_node.conf b/apps/oracle-node/oracle-node-app/src/main/resources/oracle_node.conf index f96df5a6f1..535bd0c7f7 100644 --- a/apps/oracle-node/oracle-node-app/src/main/resources/oracle_node.conf +++ b/apps/oracle-node/oracle-node-app/src/main/resources/oracle_node.conf @@ -43,6 +43,11 @@ application { // Production node, bonded role url = "http://runbtcpn7gmbj5rgqeyfyvepqokrijem6rbw7o5wgqbguimuoxrmcdyd.onion/" operator = "runbtc", + }, + { + // Production node, bonded role + url = "http://pn4o3iocd6wck4wrpj3j53hsknn5q4pqjmoircj4jzggmfuiszonxuid.onion/" + operator = "403State", } ] } @@ -121,7 +126,8 @@ application { ] "tor" : [ "hj2kj43oyq4mhd5gx4vokalnci3vlbwzclv7usocfwuj5f5iks3eivqd.onion:1000", - "plur5t7zhcf45bltdhtb4o43p726dqhibc6xo2lhjfxctjcoinlclaid.onion:1000" + "plur5t7zhcf45bltdhtb4o43p726dqhibc6xo2lhjfxctjcoinlclaid.onion:1000", + "b5jjgzuekdu7ys7yg2q6e27hybtufxfvdsxxvle7xpvqifyagdag5fid.onion:1000" ] "i2p" : [ "kglZCQYj~nyK3YlXCD5FjxOY2ggH8yosII0rqc7oqFhFfjKWy-89WYw-~mtTUqzCaN6LGd17XzheKG44XJnKrM-WvP732V8lbJcoMBIKeeHPlcfwpsTNbMJyWeXIlJByYNlw1HPVRMpBtzfJ9IznyQdwQWDkzA72pLreqpzJrgIoVYzP9OTXVLdROXnTP9RdmnzZ0h1B8XhQM-8LjHB7cE9o9VT9IXIFScICM8VZ8I1sp02rn26McTM~~XO5Zs1Df3IMV0eqteAe6TvH~Rc-6Hh3YhPrjEcv-YvV6RUlsoj605mmSO0Sj5oeacH3Cec73BlNJEGfQkmbTrXVNLqt2S4smqmkAhMq~sdCJCRKP8CFeBk6r-qVREucTeW3AmwXuGS~-8s7pAm99SlpTSepp75a2WNTIsWw~rWiHlM6faTJrkjcO5wJM7~G0tdYgVGk4zrt4VJ02AakUdh8wG1Y5sAX-daTUum~0YTk-fIAVBJSEiNc93XgZkwuTcc4J2BqAAAA:5000", diff --git a/apps/rest-api-app/src/main/resources/rest_api.conf b/apps/rest-api-app/src/main/resources/rest_api.conf index d16f172314..4ef4082be2 100644 --- a/apps/rest-api-app/src/main/resources/rest_api.conf +++ b/apps/rest-api-app/src/main/resources/rest_api.conf @@ -33,6 +33,11 @@ application { // Production node, bonded role url = "http://runbtcpn7gmbj5rgqeyfyvepqokrijem6rbw7o5wgqbguimuoxrmcdyd.onion/" operator = "runbtc", + }, + { + // Production node, bonded role + url = "http://pn4o3iocd6wck4wrpj3j53hsknn5q4pqjmoircj4jzggmfuiszonxuid.onion/" + operator = "403State", } ] } @@ -117,7 +122,8 @@ application { ] "tor" : [ "hj2kj43oyq4mhd5gx4vokalnci3vlbwzclv7usocfwuj5f5iks3eivqd.onion:1000", - "plur5t7zhcf45bltdhtb4o43p726dqhibc6xo2lhjfxctjcoinlclaid.onion:1000" + "plur5t7zhcf45bltdhtb4o43p726dqhibc6xo2lhjfxctjcoinlclaid.onion:1000", + "b5jjgzuekdu7ys7yg2q6e27hybtufxfvdsxxvle7xpvqifyagdag5fid.onion:1000",b5jjgzuekdu7ys7yg2q6e27hybtufxfvdsxxvle7xpvqifyagdag5fid.onion:1000" ] "i2p" : [ "kglZCQYj~nyK3YlXCD5FjxOY2ggH8yosII0rqc7oqFhFfjKWy-89WYw-~mtTUqzCaN6LGd17XzheKG44XJnKrM-WvP732V8lbJcoMBIKeeHPlcfwpsTNbMJyWeXIlJByYNlw1HPVRMpBtzfJ9IznyQdwQWDkzA72pLreqpzJrgIoVYzP9OTXVLdROXnTP9RdmnzZ0h1B8XhQM-8LjHB7cE9o9VT9IXIFScICM8VZ8I1sp02rn26McTM~~XO5Zs1Df3IMV0eqteAe6TvH~Rc-6Hh3YhPrjEcv-YvV6RUlsoj605mmSO0Sj5oeacH3Cec73BlNJEGfQkmbTrXVNLqt2S4smqmkAhMq~sdCJCRKP8CFeBk6r-qVREucTeW3AmwXuGS~-8s7pAm99SlpTSepp75a2WNTIsWw~rWiHlM6faTJrkjcO5wJM7~G0tdYgVGk4zrt4VJ02AakUdh8wG1Y5sAX-daTUum~0YTk-fIAVBJSEiNc93XgZkwuTcc4J2BqAAAA:5000", diff --git a/apps/seed-node-app/src/main/resources/seed_node.conf b/apps/seed-node-app/src/main/resources/seed_node.conf index d887feb6d8..9469f2327b 100644 --- a/apps/seed-node-app/src/main/resources/seed_node.conf +++ b/apps/seed-node-app/src/main/resources/seed_node.conf @@ -27,6 +27,11 @@ application { // Production node, bonded role url = "http://runbtcpn7gmbj5rgqeyfyvepqokrijem6rbw7o5wgqbguimuoxrmcdyd.onion/" operator = "runbtc", + }, + { + // Production node, bonded role + url = "http://pn4o3iocd6wck4wrpj3j53hsknn5q4pqjmoircj4jzggmfuiszonxuid.onion/" + operator = "403State", } ] } @@ -99,7 +104,8 @@ application { ] "tor" : [ "hj2kj43oyq4mhd5gx4vokalnci3vlbwzclv7usocfwuj5f5iks3eivqd.onion:1000", - "plur5t7zhcf45bltdhtb4o43p726dqhibc6xo2lhjfxctjcoinlclaid.onion:1000" + "plur5t7zhcf45bltdhtb4o43p726dqhibc6xo2lhjfxctjcoinlclaid.onion:1000", + "b5jjgzuekdu7ys7yg2q6e27hybtufxfvdsxxvle7xpvqifyagdag5fid.onion:1000" ] "i2p" : [ "kglZCQYj~nyK3YlXCD5FjxOY2ggH8yosII0rqc7oqFhFfjKWy-89WYw-~mtTUqzCaN6LGd17XzheKG44XJnKrM-WvP732V8lbJcoMBIKeeHPlcfwpsTNbMJyWeXIlJByYNlw1HPVRMpBtzfJ9IznyQdwQWDkzA72pLreqpzJrgIoVYzP9OTXVLdROXnTP9RdmnzZ0h1B8XhQM-8LjHB7cE9o9VT9IXIFScICM8VZ8I1sp02rn26McTM~~XO5Zs1Df3IMV0eqteAe6TvH~Rc-6Hh3YhPrjEcv-YvV6RUlsoj605mmSO0Sj5oeacH3Cec73BlNJEGfQkmbTrXVNLqt2S4smqmkAhMq~sdCJCRKP8CFeBk6r-qVREucTeW3AmwXuGS~-8s7pAm99SlpTSepp75a2WNTIsWw~rWiHlM6faTJrkjcO5wJM7~G0tdYgVGk4zrt4VJ02AakUdh8wG1Y5sAX-daTUum~0YTk-fIAVBJSEiNc93XgZkwuTcc4J2BqAAAA:5000", From 7ec91a5dd525d370b0041fdbcb3c635495012441 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Sat, 23 Mar 2024 09:18:07 +0100 Subject: [PATCH 046/119] Improve strings for active users indicator --- i18n/src/main/resources/application.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/src/main/resources/application.properties b/i18n/src/main/resources/application.properties index 4a6959b49a..ec7e6a72ac 100644 --- a/i18n/src/main/resources/application.properties +++ b/i18n/src/main/resources/application.properties @@ -198,8 +198,8 @@ topPanel.wallet.balance=Balance dashboard.marketPrice=Latest market price dashboard.offersOnline=Offers online -dashboard.activeUsers=Active user profiles -dashboard.activeUsers.tooltip=Number of active user profiles in last 15 days +dashboard.activeUsers=Published user profiles +dashboard.activeUsers.tooltip=Profiles stay published on the network if the user was online in the last 15 days. dashboard.main.headline=Get your first BTC dashboard.main.content1=Start trading or browse open offers in the offerbook dashboard.main.content2=Chat based and guided user interface for trading From 5e7eafc7a647eacff52fd3ee1608d7666b245628 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Sat, 23 Mar 2024 13:02:37 +0100 Subject: [PATCH 047/119] Use tooltip icon to show specs about active user info --- .../main/content/dashboard/DashboardView.java | 27 +++++++++++++++---- .../src/main/resources/css/controls.css | 7 ----- .../src/main/resources/application.properties | 2 +- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/dashboard/DashboardView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/dashboard/DashboardView.java index 76ecfe5667..2ab6c5aa7a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/dashboard/DashboardView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/dashboard/DashboardView.java @@ -21,6 +21,7 @@ import bisq.common.data.Triple; import bisq.desktop.common.threading.UIScheduler; import bisq.desktop.common.utils.GridPaneUtil; +import bisq.desktop.common.utils.ImageUtil; import bisq.desktop.common.view.View; import bisq.desktop.components.controls.BisqTooltip; import bisq.desktop.main.notification.NotificationPanelView; @@ -31,6 +32,7 @@ import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.control.Tooltip; +import javafx.scene.image.ImageView; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; @@ -40,6 +42,8 @@ import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; +import java.util.Optional; + @Slf4j public class DashboardView extends View { private static final int PADDING = 20; @@ -71,12 +75,10 @@ public DashboardView(DashboardModel model, DashboardController controller) { VBox offersOnline = offersPair.getFirst(); offersOnlineLabel = offersPair.getSecond(); - Pair usersPair = getValueBox(Res.get("dashboard.activeUsers")); + Pair usersPair = getValueBox(Res.get("dashboard.activeUsers"), + Optional.of(Res.get("dashboard.activeUsers.tooltip"))); VBox activeUsers = usersPair.getFirst(); activeUsersLabel = usersPair.getSecond(); - BisqTooltip tooltip = new BisqTooltip(Res.get("dashboard.activeUsers.tooltip")); - tooltip.setId("large-tooltip"); - Tooltip.install(activeUsers, tooltip); HBox.setMargin(marketPrice, new Insets(0, -100, 0, -30)); HBox hBox = new HBox(16, marketPrice, offersOnline, activeUsers); @@ -199,13 +201,28 @@ private Triple getPriceBox(String title) { } private Pair getValueBox(String title) { + return getValueBox(title, Optional.empty()); + } + + private Pair getValueBox(String title, Optional tooltipText) { Label titleLabel = new Label(title); titleLabel.getStyleClass().addAll("bisq-text-7", "bisq-text-grey-9"); + HBox titleHBox = new HBox(titleLabel); + titleHBox.setAlignment(Pos.CENTER); + + if (tooltipText.isPresent()) { + ImageView tooltipIcon = ImageUtil.getImageViewById("info"); + tooltipIcon.setOpacity(0.6); + BisqTooltip tooltip = new BisqTooltip(tooltipText.get()); + Tooltip.install(tooltipIcon, tooltip); + titleHBox.getChildren().add(tooltipIcon); + titleHBox.setSpacing(5); + } Label valueLabel = new Label(); valueLabel.getStyleClass().add("bisq-text-headline-3"); - VBox box = new VBox(titleLabel, valueLabel); + VBox box = new VBox(titleHBox, valueLabel); box.setAlignment(Pos.TOP_CENTER); HBox.setHgrow(box, Priority.ALWAYS); return new Pair<>(box, valueLabel); diff --git a/apps/desktop/desktop/src/main/resources/css/controls.css b/apps/desktop/desktop/src/main/resources/css/controls.css index 7b37a1cdce..9ba51780aa 100644 --- a/apps/desktop/desktop/src/main/resources/css/controls.css +++ b/apps/desktop/desktop/src/main/resources/css/controls.css @@ -744,13 +744,6 @@ -fx-font-size: 0.85em; } -#large-tooltip { - -fx-font-size: 1.2em; - -fx-font-family: "IBM Plex Sans Light"; - -fx-text-fill: -fx-dark-text-color; - -fx-padding: 0.3em; -} - /******************************************************************************* * * diff --git a/i18n/src/main/resources/application.properties b/i18n/src/main/resources/application.properties index ec7e6a72ac..dec58bb993 100644 --- a/i18n/src/main/resources/application.properties +++ b/i18n/src/main/resources/application.properties @@ -199,7 +199,7 @@ topPanel.wallet.balance=Balance dashboard.marketPrice=Latest market price dashboard.offersOnline=Offers online dashboard.activeUsers=Published user profiles -dashboard.activeUsers.tooltip=Profiles stay published on the network if the user was online in the last 15 days. +dashboard.activeUsers.tooltip=Profiles stay published on the network\nif the user was online in the last 15 days. dashboard.main.headline=Get your first BTC dashboard.main.content1=Start trading or browse open offers in the offerbook dashboard.main.content2=Chat based and guided user interface for trading From 930995a008a1864165985215654d3f01e4905c81 Mon Sep 17 00:00:00 2001 From: Alva Swanson Date: Sat, 23 Mar 2024 14:01:48 +0100 Subject: [PATCH 048/119] Test Tor Installation and Startup Race Condition Ref: #1798 --- common/build.gradle.kts | 1 + .../tor/InstallerAndRunRaceConditionTest.java | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 network/tor/tor/src/integrationTest/java/bisq/tor/InstallerAndRunRaceConditionTest.java diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 8177af9270..0ba381870e 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("bisq.java-conventions") + id("bisq.java-integration-tests") id("bisq.protobuf") } diff --git a/network/tor/tor/src/integrationTest/java/bisq/tor/InstallerAndRunRaceConditionTest.java b/network/tor/tor/src/integrationTest/java/bisq/tor/InstallerAndRunRaceConditionTest.java new file mode 100644 index 0000000000..6b34af5a11 --- /dev/null +++ b/network/tor/tor/src/integrationTest/java/bisq/tor/InstallerAndRunRaceConditionTest.java @@ -0,0 +1,55 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.tor; + +import bisq.tor.installer.TorInstallationFiles; +import bisq.tor.installer.TorInstaller; +import bisq.tor.process.LdPreload; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; + +public class InstallerAndRunRaceConditionTest { + @Test + void test(@TempDir Path tempDir) throws IOException, InterruptedException { + var torInstallationFiles = new TorInstallationFiles(tempDir); + var torInstaller = new TorInstaller(torInstallationFiles); + torInstaller.installIfNotUpToDate(); + + Path torBinaryPath = tempDir.resolve("tor"); + var processBuilder = new ProcessBuilder( + torBinaryPath.toAbsolutePath().toString(), + "--version" + ); + + Map environment = processBuilder.environment(); + environment.put("LD_PRELOAD", LdPreload.computeLdPreloadVariable(tempDir)); + + Process process = processBuilder.start(); + boolean isSuccess = process.waitFor(30, TimeUnit.SECONDS); + + assertThat(isSuccess).isTrue(); + assertThat(process.exitValue()).isEqualTo(0); + } +} From 8ae03fe9e115441ee89d766c2387cff950c22e70 Mon Sep 17 00:00:00 2001 From: Alva Swanson Date: Sat, 23 Mar 2024 14:08:37 +0100 Subject: [PATCH 049/119] Implement DirectoryEventSubscription (Flow.Subscription) Ref: #1798 --- .../DirectoryEventSubscription.java | 83 +++++++++++++++++++ .../NoDirectoryChangesTimeoutException.java | 24 ++++++ 2 files changed, 107 insertions(+) create mode 100644 common/src/main/java/bisq/common/io_watcher/DirectoryEventSubscription.java create mode 100644 common/src/main/java/bisq/common/io_watcher/NoDirectoryChangesTimeoutException.java diff --git a/common/src/main/java/bisq/common/io_watcher/DirectoryEventSubscription.java b/common/src/main/java/bisq/common/io_watcher/DirectoryEventSubscription.java new file mode 100644 index 0000000000..2c7fc283a4 --- /dev/null +++ b/common/src/main/java/bisq/common/io_watcher/DirectoryEventSubscription.java @@ -0,0 +1,83 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.common.io_watcher; + +import java.nio.file.Path; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Flow; +import java.util.concurrent.TimeUnit; + +public class DirectoryEventSubscription implements Flow.Subscription { + private final ExecutorService executorService = Executors.newSingleThreadExecutor(); + private final Flow.Subscriber subscriber; + private final WatchService watchService; + private final Path directoryPath; + private final Set> watchEventKinds; + + public DirectoryEventSubscription(Flow.Subscriber subscriber, + WatchService watchService, + Path directoryPath, + Set> watchEventKinds) { + this.subscriber = subscriber; + this.watchService = watchService; + this.directoryPath = directoryPath; + this.watchEventKinds = watchEventKinds; + } + + @Override + public void request(long l) { + executorService.submit(this::watchDirectoryForChanges); + } + + @Override + public void cancel() { + throw new UnsupportedOperationException(); + } + + private void watchDirectoryForChanges() { + try { + WatchKey key = watchService.poll(1, TimeUnit.MINUTES); + if (key == null) { + var error = new NoDirectoryChangesTimeoutException("No changes in directory for the last minute."); + subscriber.onError(error); + return; + } + + for (WatchEvent event : key.pollEvents()) { + if (watchEventKinds.contains(event.kind())) { + @SuppressWarnings("unchecked") + WatchEvent castedWatchEvent = (WatchEvent) event; + Path filename = castedWatchEvent.context(); + + Path filePath = directoryPath.resolve(filename); + subscriber.onNext(filePath); + } + } + } catch (InterruptedException e) { + subscriber.onError(e); + return; + } + var error = new IllegalStateException(); + subscriber.onError(error); + } +} diff --git a/common/src/main/java/bisq/common/io_watcher/NoDirectoryChangesTimeoutException.java b/common/src/main/java/bisq/common/io_watcher/NoDirectoryChangesTimeoutException.java new file mode 100644 index 0000000000..2c9fdf7cb8 --- /dev/null +++ b/common/src/main/java/bisq/common/io_watcher/NoDirectoryChangesTimeoutException.java @@ -0,0 +1,24 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.common.io_watcher; + +public class NoDirectoryChangesTimeoutException extends RuntimeException { + public NoDirectoryChangesTimeoutException(String message) { + super(message); + } +} From 5fed35ddfeca21697d59f49dd6a0794662cc59cf Mon Sep 17 00:00:00 2001 From: Alva Swanson Date: Sat, 23 Mar 2024 14:12:44 +0100 Subject: [PATCH 050/119] Implement DirectoryEvent Flow.Publisher and Flow.Subscriber Consumers can asynchronously subscribe to directory events with the Flow components. Ref: #1798 --- .../io_watcher/DirectoryEventPublisher.java | 42 +++++++++++++++ .../io_watcher/DirectoryEventSubscriber.java | 53 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 common/src/main/java/bisq/common/io_watcher/DirectoryEventPublisher.java create mode 100644 common/src/main/java/bisq/common/io_watcher/DirectoryEventSubscriber.java diff --git a/common/src/main/java/bisq/common/io_watcher/DirectoryEventPublisher.java b/common/src/main/java/bisq/common/io_watcher/DirectoryEventPublisher.java new file mode 100644 index 0000000000..356361a128 --- /dev/null +++ b/common/src/main/java/bisq/common/io_watcher/DirectoryEventPublisher.java @@ -0,0 +1,42 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.common.io_watcher; + +import java.nio.file.Path; +import java.nio.file.WatchEvent; +import java.nio.file.WatchService; +import java.util.Set; +import java.util.concurrent.Flow; + +public class DirectoryEventPublisher implements Flow.Publisher { + private final WatchService watchService; + private final Path directoryPath; + private final Set> watchEventKinds; + + public DirectoryEventPublisher(WatchService watchService, Path directoryPath, Set> watchEventKinds) { + this.watchService = watchService; + this.directoryPath = directoryPath; + this.watchEventKinds = watchEventKinds; + } + + @Override + public void subscribe(Flow.Subscriber subscriber) { + var subscription = new DirectoryEventSubscription(subscriber, watchService, directoryPath, watchEventKinds); + subscriber.onSubscribe(subscription); + } +} diff --git a/common/src/main/java/bisq/common/io_watcher/DirectoryEventSubscriber.java b/common/src/main/java/bisq/common/io_watcher/DirectoryEventSubscriber.java new file mode 100644 index 0000000000..a7845e6c86 --- /dev/null +++ b/common/src/main/java/bisq/common/io_watcher/DirectoryEventSubscriber.java @@ -0,0 +1,53 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.common.io_watcher; + +import java.nio.file.Path; +import java.util.concurrent.Flow; +import java.util.function.Consumer; + +public class DirectoryEventSubscriber implements Flow.Subscriber { + private final Consumer consumer; + private Flow.Subscription subscription; + + public DirectoryEventSubscriber(Consumer consumer) { + this.consumer = consumer; + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + this.subscription = subscription; + subscription.request(1); + } + + @Override + public void onNext(Path path) { + consumer.accept(path); + subscription.request(1); + } + + @Override + public void onError(Throwable throwable) { + throw new RuntimeException(throwable); + } + + @Override + public void onComplete() { + throw new UnsupportedOperationException(); + } +} From 67453299775e4b8250be612bdfe6f8404a2449f6 Mon Sep 17 00:00:00 2001 From: Alva Swanson Date: Sat, 23 Mar 2024 14:20:48 +0100 Subject: [PATCH 051/119] Implement Flow-component-based DirectoryWatcher Ref: #1798 --- .../io_watcher/DirectoryWatcherTests.java | 65 +++++++++++++++++++ ...otInitializeDirectoryWatcherException.java | 28 ++++++++ .../common/io_watcher/DirectoryWatcher.java | 64 ++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 common/src/integrationTest/java/bisq/common/io_watcher/DirectoryWatcherTests.java create mode 100644 common/src/main/java/bisq/common/io_watcher/CouldNotInitializeDirectoryWatcherException.java create mode 100644 common/src/main/java/bisq/common/io_watcher/DirectoryWatcher.java diff --git a/common/src/integrationTest/java/bisq/common/io_watcher/DirectoryWatcherTests.java b/common/src/integrationTest/java/bisq/common/io_watcher/DirectoryWatcherTests.java new file mode 100644 index 0000000000..508456e6d6 --- /dev/null +++ b/common/src/integrationTest/java/bisq/common/io_watcher/DirectoryWatcherTests.java @@ -0,0 +1,65 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.common.io_watcher; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardWatchEventKinds; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DirectoryWatcherTests { + @Test + void detectFileCreation(@TempDir Path tempDir) throws IOException, ExecutionException, InterruptedException, TimeoutException { + try (var watcher = new DirectoryWatcher(tempDir, Set.of(StandardWatchEventKinds.ENTRY_CREATE))) { + var completableFuture = new CompletableFuture(); + watcher.initialize(completableFuture::complete); + + Path newFilePath = tempDir.resolve("newFile"); + Files.writeString(newFilePath, "Hello!"); + + Path path = completableFuture.get(30, TimeUnit.SECONDS); + assertThat(path).isEqualTo(newFilePath); + } + } + + @Test + void detectFileWrite(@TempDir Path tempDir) throws IOException, ExecutionException, InterruptedException, TimeoutException { + Path newFilePath = tempDir.resolve("newFile"); + Files.writeString(newFilePath, "Hello!"); + + try (var watcher = new DirectoryWatcher(tempDir, Set.of(StandardWatchEventKinds.ENTRY_MODIFY))) { + var completableFuture = new CompletableFuture(); + watcher.initialize(completableFuture::complete); + + Files.writeString(newFilePath, "World!"); + + Path path = completableFuture.get(30, TimeUnit.SECONDS); + assertThat(path).isEqualTo(newFilePath); + } + } +} diff --git a/common/src/main/java/bisq/common/io_watcher/CouldNotInitializeDirectoryWatcherException.java b/common/src/main/java/bisq/common/io_watcher/CouldNotInitializeDirectoryWatcherException.java new file mode 100644 index 0000000000..e9f7d084c2 --- /dev/null +++ b/common/src/main/java/bisq/common/io_watcher/CouldNotInitializeDirectoryWatcherException.java @@ -0,0 +1,28 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.common.io_watcher; + +public class CouldNotInitializeDirectoryWatcherException extends RuntimeException { + public CouldNotInitializeDirectoryWatcherException(String message) { + super(message); + } + + public CouldNotInitializeDirectoryWatcherException(Throwable cause) { + super(cause); + } +} diff --git a/common/src/main/java/bisq/common/io_watcher/DirectoryWatcher.java b/common/src/main/java/bisq/common/io_watcher/DirectoryWatcher.java new file mode 100644 index 0000000000..1177770dbe --- /dev/null +++ b/common/src/main/java/bisq/common/io_watcher/DirectoryWatcher.java @@ -0,0 +1,64 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.common.io_watcher; + +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.WatchEvent; +import java.nio.file.WatchService; +import java.util.Set; +import java.util.function.Consumer; + +public class DirectoryWatcher implements AutoCloseable { + private final Path directoryPath; + private final Set> watchEventKinds; + + private WatchService watchService; + + public DirectoryWatcher(Path directoryPath, Set> watchEventKinds) { + this.directoryPath = directoryPath; + this.watchEventKinds = watchEventKinds; + } + + public void initialize(Consumer eventConsumer) { + try { + watchService = FileSystems.getDefault().newWatchService(); + + WatchEvent.Kind[] eventKinds = new WatchEvent.Kind[watchEventKinds.size()]; + watchEventKinds.toArray(eventKinds); + directoryPath.register(watchService, eventKinds); + + subscribeToChangesAsync(eventConsumer); + + } catch (IOException e) { + throw new CouldNotInitializeDirectoryWatcherException(e); + } + } + + private void subscribeToChangesAsync(Consumer consumer) { + var directoryEventPublisher = new DirectoryEventPublisher(watchService, directoryPath, watchEventKinds); + var directoryEventSubscriber = new DirectoryEventSubscriber(consumer); + directoryEventPublisher.subscribe(directoryEventSubscriber); + } + + @Override + public void close() throws IOException { + watchService.close(); + } +} From 2fd083962cba1dbf206e024d4c1c58558c18f0d9 Mon Sep 17 00:00:00 2001 From: Alva Swanson Date: Sat, 23 Mar 2024 14:26:28 +0100 Subject: [PATCH 052/119] Extract LdPreload computation out of TorProcess --- .../main/java/bisq/tor/process/LdPreload.java | 36 +++++++++++++++++++ .../bisq/tor/process/NativeTorProcess.java | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 network/tor/tor/src/main/java/bisq/tor/process/LdPreload.java diff --git a/network/tor/tor/src/main/java/bisq/tor/process/LdPreload.java b/network/tor/tor/src/main/java/bisq/tor/process/LdPreload.java new file mode 100644 index 0000000000..3ea0def3ec --- /dev/null +++ b/network/tor/tor/src/main/java/bisq/tor/process/LdPreload.java @@ -0,0 +1,36 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.tor.process; + +import java.io.File; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.Collectors; + +public class LdPreload { + public static String computeLdPreloadVariable(Path dirPath) { + File[] sharedLibraries = dirPath.toFile() + .listFiles((file, fileName) -> fileName.contains(".so.")); + Objects.requireNonNull(sharedLibraries); + + return Arrays.stream(sharedLibraries) + .map(File::getAbsolutePath) + .collect(Collectors.joining(":")); + } +} diff --git a/network/tor/tor/src/main/java/bisq/tor/process/NativeTorProcess.java b/network/tor/tor/src/main/java/bisq/tor/process/NativeTorProcess.java index 86d2646381..ba74468ef2 100644 --- a/network/tor/tor/src/main/java/bisq/tor/process/NativeTorProcess.java +++ b/network/tor/tor/src/main/java/bisq/tor/process/NativeTorProcess.java @@ -58,7 +58,7 @@ public void start() { ); Map environment = processBuilder.environment(); - environment.put("LD_PRELOAD", computeLdPreloadVariable()); + environment.put("LD_PRELOAD", LdPreload.computeLdPreloadVariable(torDataDirPath)); processBuilder.redirectError(ProcessBuilder.Redirect.DISCARD); processBuilder.redirectOutput(ProcessBuilder.Redirect.DISCARD); From cbe521acb59e196194f8148f2e3c7c3af9b30637 Mon Sep 17 00:00:00 2001 From: Alva Swanson Date: Sat, 23 Mar 2024 14:58:41 +0100 Subject: [PATCH 053/119] tor: Implement ControlPortFileParser Presently, we parse Tor's log file to detect when the control port is ready. This is unstable and could break with any Tor update. A better approach is to use Tor's 'ControlPortWriteToFile' config option and parse the control port from that file. Ref: #1798 --- .../ControlPortFileParseFailureException.java | 28 ++++++++ .../tor/process/ControlPortFileParser.java | 66 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 network/tor/tor/src/main/java/bisq/tor/process/ControlPortFileParseFailureException.java create mode 100644 network/tor/tor/src/main/java/bisq/tor/process/ControlPortFileParser.java diff --git a/network/tor/tor/src/main/java/bisq/tor/process/ControlPortFileParseFailureException.java b/network/tor/tor/src/main/java/bisq/tor/process/ControlPortFileParseFailureException.java new file mode 100644 index 0000000000..f386e8f6a0 --- /dev/null +++ b/network/tor/tor/src/main/java/bisq/tor/process/ControlPortFileParseFailureException.java @@ -0,0 +1,28 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.tor.process; + +public class ControlPortFileParseFailureException extends RuntimeException { + public ControlPortFileParseFailureException(String message) { + super(message); + } + + public ControlPortFileParseFailureException(Throwable cause) { + super(cause); + } +} diff --git a/network/tor/tor/src/main/java/bisq/tor/process/ControlPortFileParser.java b/network/tor/tor/src/main/java/bisq/tor/process/ControlPortFileParser.java new file mode 100644 index 0000000000..c3949f83d8 --- /dev/null +++ b/network/tor/tor/src/main/java/bisq/tor/process/ControlPortFileParser.java @@ -0,0 +1,66 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.tor.process; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public class ControlPortFileParser { + private static final String PORT_MARKER = "PORT="; + + public static int parse(Path controlPortFilePath) { + try { + String fileContent = Files.readString(controlPortFilePath); + + if (isControlPortFileReady(fileContent)) { + for (String line : fileContent.split("\n")) { + if (isPortLine(line)) { + return parsePort(line); + } + } + } + + throw new ControlPortFileParseFailureException("Parsed file is invalid or incomplete: " + fileContent); + + } catch (IOException e) { + throw new ControlPortFileParseFailureException(e); + } + } + + private static boolean isControlPortFileReady(String fileContent) { + return fileContent.contains("\n") && fileContent.contains(PORT_MARKER); + } + + private static boolean isPortLine(String line) { + return line.startsWith("PORT="); + } + + private static int parsePort(String line) { + // PORT=127.0.0.1:37611 + String lineWithoutPrefix = line.replace("PORT=", ""); + String[] ipAndPort = lineWithoutPrefix.split(":"); + + if (ipAndPort.length == 2) { + String port = ipAndPort[1]; + return Integer.parseInt(port); + } + + throw new ControlPortFileParseFailureException("Couldn't parse control port line: " + line); + } +} From 9d8eb9889f51b5096fef789c0132e5e129b78db7 Mon Sep 17 00:00:00 2001 From: Alva Swanson Date: Sat, 23 Mar 2024 15:03:09 +0100 Subject: [PATCH 054/119] tor: Implement ControlPortReadyWaiter Use the flow-component-based DirectoryWatcher to wait for the control port file and parse the control port file with the ControlPortFileParser afterward. Ref: #1798 --- .../tor/process/ControlPortReadyWaiter.java | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 network/tor/tor/src/main/java/bisq/tor/process/ControlPortReadyWaiter.java diff --git a/network/tor/tor/src/main/java/bisq/tor/process/ControlPortReadyWaiter.java b/network/tor/tor/src/main/java/bisq/tor/process/ControlPortReadyWaiter.java new file mode 100644 index 0000000000..4afa0d09d7 --- /dev/null +++ b/network/tor/tor/src/main/java/bisq/tor/process/ControlPortReadyWaiter.java @@ -0,0 +1,91 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.tor.process; + +import bisq.common.io_watcher.CouldNotInitializeDirectoryWatcherException; +import bisq.common.io_watcher.DirectoryWatcher; +import lombok.Getter; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchEvent; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +public class ControlPortReadyWaiter { + @Getter + private final CompletableFuture portCompletableFuture = new CompletableFuture<>(); + private final DirectoryWatcher directoryWatcher; + private final Path controlDirPath; + private final Path controlPortFilePath; + + public ControlPortReadyWaiter(Path controlDirPath) { + this.controlDirPath = controlDirPath; + Set> watchEventKinds = Set.of( + StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY); + directoryWatcher = new DirectoryWatcher(controlDirPath, watchEventKinds); + controlPortFilePath = controlDirPath.resolve("control"); + } + + public void initialize() { + createTorControlDirectory(); + deleteControlPortFileFromPreviousRun(); + + directoryWatcher.initialize(path -> { + if (path.equals(controlPortFilePath)) { + try { + int controlPort = ControlPortFileParser.parse(controlPortFilePath); + portCompletableFuture.complete(controlPort); + close(); + } catch (IOException | ControlPortFileParseFailureException e) { + portCompletableFuture.completeExceptionally(e); + } + } + }); + } + + public void close() throws IOException { + directoryWatcher.close(); + } + + private void createTorControlDirectory() { + File controlDirFile = controlDirPath.toFile(); + if (controlDirFile.exists()) { + return; + } + + boolean isSuccess = controlDirFile.mkdirs(); + if (!isSuccess) { + throw new CouldNotInitializeDirectoryWatcherException("Couldn't create Tor control directory: " + controlDirPath); + } + } + + private void deleteControlPortFileFromPreviousRun() { + File controlPortFile = controlPortFilePath.toFile(); + if (!controlPortFile.exists()) { + return; + } + + boolean isSuccess = controlPortFile.delete(); + if (!isSuccess) { + throw new CouldNotInitializeDirectoryWatcherException("Couldn't delete Tor control port file from previous run: " + controlPortFilePath); + } + } +} From 433c53f4b84b6e462e8c0d72aaab42dcd412b37c Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Sat, 23 Mar 2024 21:30:37 +0100 Subject: [PATCH 055/119] Add new content --- .../tab3/AccountAgeTab3Controller.java | 28 +++--- .../accountAge/tab3/AccountAgeTab3Model.java | 7 +- .../accountAge/tab3/AccountAgeTab3View.java | 94 ++++++++++++++----- i18n/src/main/resources/user.properties | 21 +++-- 4 files changed, 99 insertions(+), 51 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3Controller.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3Controller.java index 38897c7d81..f1446e0adc 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3Controller.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3Controller.java @@ -72,11 +72,14 @@ public void onActivate() { }); } ); + + model.getRequestCertificateButtonDisabled().bind(model.getSignedMessage().isEmpty()); } @Override public void onDeactivate() { selectedUserProfilePin.unbind(); + model.getRequestCertificateButtonDisabled().unbind(); } void onBack() { @@ -96,27 +99,20 @@ void onClose() { } public void onRequestAuthorization() { - ClipboardUtil.getClipboardString().ifPresent(clipboard -> { - if (clipboard.startsWith(PREFIX)) { - String json = clipboard.replace(PREFIX, ""); - boolean success = accountAgeService.requestAuthorization(json); - if (success) { - new Popup().information(Res.get("user.reputation.request.success")) - .animationType(Overlay.AnimationType.SlideDownFromCenterTop) - .transitionsType(Transitions.Type.LIGHT_BLUR_LIGHT) - .owner(parentView.getRoot()) - .onClose(this::onClose) - .show(); - return; - } - } + String signedMessage = String.valueOf(model.getSignedMessage()); + // Question: do we still need the prefix? + if (signedMessage.startsWith(PREFIX)) { + signedMessage = signedMessage.replace(PREFIX, ""); + } - new Popup().warning(Res.get("user.reputation.request.error", StringUtils.truncate(clipboard))) + boolean success = accountAgeService.requestAuthorization(signedMessage); + if (success) { + new Popup().information(Res.get("user.reputation.request.success")) .animationType(Overlay.AnimationType.SlideDownFromCenterTop) .transitionsType(Transitions.Type.LIGHT_BLUR_LIGHT) .owner(parentView.getRoot()) .onClose(this::onClose) .show(); - }); + } } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3Model.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3Model.java index 1405d1630b..9defb0586a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3Model.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3Model.java @@ -19,10 +19,7 @@ import bisq.desktop.common.view.Model; import bisq.user.identity.UserIdentity; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; +import javafx.beans.property.*; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -31,4 +28,6 @@ public class AccountAgeTab3Model implements Model { private final ObjectProperty selectedChatUserIdentity = new SimpleObjectProperty<>(); private final StringProperty pubKeyHash = new SimpleStringProperty(); + private final StringProperty signedMessage = new SimpleStringProperty(); + private final BooleanProperty requestCertificateButtonDisabled = new SimpleBooleanProperty(); } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3View.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3View.java index 08b1bd52b9..ea3543fa4f 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3View.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3View.java @@ -17,27 +17,26 @@ package bisq.desktop.main.content.user.reputation.accountAge.tab3; +import bisq.desktop.common.Layout; +import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.View; import bisq.desktop.components.containers.Spacer; import bisq.desktop.components.controls.MaterialTextField; -import bisq.desktop.components.controls.OrderedList; import bisq.i18n.Res; -import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; -import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; import lombok.extern.slf4j.Slf4j; @Slf4j public class AccountAgeTab3View extends View { - private final MaterialTextField pubKeyHash; private final Button closeButton, backButton, requestCertificateButton; private final Hyperlink learnMore; + private MaterialTextField pubKeyHash, signedMessage; public AccountAgeTab3View(AccountAgeTab3Model model, AccountAgeTab3Controller controller, @@ -46,19 +45,12 @@ public AccountAgeTab3View(AccountAgeTab3Model model, root.setSpacing(20); root.setAlignment(Pos.TOP_LEFT); + root.getStyleClass().add("account-age"); - Label headline = new Label(Res.get("user.reputation.accountAge.howToHeadline")); - headline.getStyleClass().add("bisq-text-headline-2"); - - OrderedList info = new OrderedList(Res.get("user.reputation.accountAge.howTo"), "bisq-text-13"); - - Label userProfileSelectLabel = new Label(Res.get("user.bondedRoles.userProfile.select").toUpperCase()); - userProfileSelectLabel.getStyleClass().add("bisq-text-4"); - userProfileSelectLabel.setAlignment(Pos.TOP_LEFT); - - pubKeyHash = new MaterialTextField(Res.get("user.reputation.pubKeyHash"), ""); - pubKeyHash.setEditable(false); - pubKeyHash.showCopyIcon(); + VBox stepOne = createAndGetStepOne(userProfileSelection); + VBox stepTwo = createAndGetStepTwo(); + VBox stepThree = createAndGetStepThree(); + VBox stepFour = createAndGetStepFour(); requestCertificateButton = new Button(Res.get("user.reputation.request")); requestCertificateButton.getStyleClass().add("outlined-button"); @@ -73,32 +65,88 @@ public AccountAgeTab3View(AccountAgeTab3Model model, HBox buttons = new HBox(20, backButton, closeButton, Spacer.fillHBox(), learnMore); buttons.setAlignment(Pos.BOTTOM_RIGHT); - VBox.setVgrow(info, Priority.ALWAYS); - VBox.setMargin(headline, new Insets(10, 0, 0, 0)); - VBox.setMargin(userProfileSelectLabel, new Insets(10, 0, -20, 0)); - VBox.setMargin(userProfileSelection, new Insets(0, 0, -30, 0)); - VBox.setMargin(buttons, new Insets(10, 0, 0, 0)); - root.getChildren().addAll(headline, info, userProfileSelectLabel, userProfileSelection, pubKeyHash, requestCertificateButton, buttons); + root.getChildren().addAll(stepOne, stepTwo, stepThree, stepFour, requestCertificateButton, buttons); } @Override protected void onViewAttached() { pubKeyHash.textProperty().bind(model.getPubKeyHash()); pubKeyHash.getIconButton().setOnAction(e -> controller.onCopyToClipboard(pubKeyHash.getText())); + + signedMessage.textProperty().bindBidirectional(model.getSignedMessage()); + closeButton.setOnAction(e -> controller.onClose()); backButton.setOnAction(e -> controller.onBack()); learnMore.setOnAction(e -> controller.onLearnMore()); + + requestCertificateButton.disableProperty().bind(model.getRequestCertificateButtonDisabled()); requestCertificateButton.setOnAction(e -> controller.onRequestAuthorization()); } @Override protected void onViewDetached() { pubKeyHash.textProperty().unbind(); - pubKeyHash.getIconButton().setOnAction(null); + + signedMessage.textProperty().unbindBidirectional(model.getSignedMessage()); + closeButton.setOnAction(null); backButton.setOnAction(null); learnMore.setOnAction(null); + + requestCertificateButton.disableProperty().unbind(); requestCertificateButton.setOnAction(null); } + + private VBox createAndGetStepOne(Pane userProfileSelection) { + Label title = createAndGetStepLabel(Res.get("user.reputation.accountAge.import.step1.title")); + Label instruction = createAndGetStepInstructionLabel(Res.get("user.reputation.accountAge.import.step1.instruction")); + VBox vBox = new VBox(title, instruction, userProfileSelection, Layout.hLine()); + vBox.getStyleClass().add("import-step"); + return vBox; + } + + private VBox createAndGetStepTwo() { + Label title = createAndGetStepLabel(Res.get("user.reputation.accountAge.import.step2.title")); + Label instruction = createAndGetStepInstructionLabel(Res.get("user.reputation.accountAge.import.step2.instruction")); + pubKeyHash = new MaterialTextField(Res.get("user.reputation.accountAge.import.step2.profileId"), ""); + pubKeyHash.setEditable(false); + pubKeyHash.showCopyIcon(); + VBox vBox = new VBox(title, instruction, pubKeyHash, Layout.hLine()); + vBox.getStyleClass().add("import-step"); + return vBox; + } + + private VBox createAndGetStepThree() { + Label title = createAndGetStepLabel(Res.get("user.reputation.accountAge.import.step3.title")); + Label instructionOne = createAndGetStepInstructionLabel(Res.get("user.reputation.accountAge.import.step3.instruction1")); + Label instructionTwo = createAndGetStepInstructionLabel(Res.get("user.reputation.accountAge.import.step3.instruction2")); + Label instructionThree = createAndGetStepInstructionLabel(Res.get("user.reputation.accountAge.import.step3.instruction3")); + VBox vBox = new VBox(title, instructionOne, instructionTwo, instructionThree, Layout.hLine()); + vBox.getStyleClass().add("import-step"); + return vBox; + } + + private VBox createAndGetStepFour() { + Label title = createAndGetStepLabel(Res.get("user.reputation.accountAge.import.step4.title")); + Label instruction = createAndGetStepInstructionLabel(Res.get("user.reputation.accountAge.import.step4.instruction")); + signedMessage = new MaterialTextField(Res.get("user.reputation.accountAge.import.step4.signedMessage")); + signedMessage.setEditable(true); + UIThread.runOnNextRenderFrame(signedMessage::requestFocus); + VBox vBox = new VBox(title, instruction, signedMessage); + vBox.getStyleClass().add("import-step"); + return vBox; + } + + private Label createAndGetStepLabel(String text) { + Label label = new Label(text.toUpperCase()); + label.getStyleClass().add("step-title"); + return label; + } + + private Label createAndGetStepInstructionLabel(String text) { + Label label = new Label(text); + label.getStyleClass().add("step-instruction"); + return label; + } } diff --git a/i18n/src/main/resources/user.properties b/i18n/src/main/resources/user.properties index 0eb820f93e..f352ecacb0 100644 --- a/i18n/src/main/resources/user.properties +++ b/i18n/src/main/resources/user.properties @@ -241,7 +241,7 @@ user.reputation.bond.howTo=1. Select the user profile for which you want to atta user.reputation.accountAge=Account age user.reputation.accountAge.tab1=Why user.reputation.accountAge.tab2=Score -user.reputation.accountAge.tab3=How-to +user.reputation.accountAge.tab3=Import user.reputation.accountAge.infoHeadline=Provide trust user.reputation.accountAge.info=By linking your Bisq 1 'account age' you can provide some level of trust.\n\n\ There are some reasonable expectations that a user who has used Bisq for some time is an honest user. But \ @@ -262,13 +262,18 @@ user.reputation.accountAge.score.info=Importing 'account age' is considered a ra # suppress inspection "UnusedProperty" user.reputation.accountAge.totalScore=Account age in days (max. 2000) * weight -user.reputation.accountAge.howToHeadline=Process for importing your 'account age' from Bisq 1 -user.reputation.accountAge.howTo=1. Select the user profile for which you want to attach the reputation.\n\ - 2. Copy the 'profile ID'\n\ - 3. Open Bisq 1 and go to 'ACCOUNT/NATIONAL CURRENCY ACCOUNTS'. Select your oldest account and click the 'EXPORT ACCOUNT AGE' button. \ - This will create a signature with the given data and copies the result to the clipboard.\n\ - 4. Go back to Bisq 2 and click the 'Request authorization' button. This sends the data to the 'Bisq 1 bridge node' for verification and publishing.\n\ - 5. After a short delay your 'account age' should be visible in your reputation score. +user.reputation.accountAge.import.step1.title=Step 1 +user.reputation.accountAge.import.step1.instruction=Select the user profile for which you want to attach the reputation. +user.reputation.accountAge.import.step2.title=Step 2 +user.reputation.accountAge.import.step2.instruction=Copy the profile ID to paste on Bisq 1. +user.reputation.accountAge.import.step2.profileId=Profile ID (Paste on the account screen on Bisq 1) +user.reputation.accountAge.import.step3.title=Step 3 +user.reputation.accountAge.import.step3.instruction1=3.1. - Open Bisq 1 and go to 'ACCOUNT/NATIONAL CURRENCY ACCOUNTS'. +user.reputation.accountAge.import.step3.instruction2=3.2. - Select the oldest account and click 'EXPORT ACCOUNT'. +user.reputation.accountAge.import.step3.instruction3=3.3. - This will add a signed message with the Bisq 2 Profile ID. +user.reputation.accountAge.import.step4.title=Step 4 +user.reputation.accountAge.import.step4.instruction=Paste signature from Bisq 1 +user.reputation.accountAge.import.step4.signedMessage=Signed Message from Bisq 1 user.reputation.signedWitness=Signed account age witness user.reputation.signedWitness.tab1=Why From d5fb2e95e6865c9affc8fc53d19ec1be956dbcd2 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Sat, 23 Mar 2024 22:15:25 +0100 Subject: [PATCH 056/119] Improve styles --- .../reputation/accountAge/AccountAgeView.java | 2 +- .../accountAge/tab3/AccountAgeTab3View.java | 11 +++++++- .../desktop/src/main/resources/css/user.css | 27 ++++++++++++++++--- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/AccountAgeView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/AccountAgeView.java index 6865118d0f..d533fc286a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/AccountAgeView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/AccountAgeView.java @@ -64,7 +64,7 @@ protected void onViewAttached() { closeButton.setOnAction(e -> controller.onClose()); root.setPrefWidth(OverlayModel.WIDTH); - root.setPrefHeight(OverlayModel.HEIGHT + 90); + root.setPrefHeight(OverlayModel.HEIGHT + 170); } @Override diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3View.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3View.java index ea3543fa4f..d222dbc3d9 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3View.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3View.java @@ -23,6 +23,7 @@ import bisq.desktop.components.containers.Spacer; import bisq.desktop.components.controls.MaterialTextField; import bisq.i18n.Res; +import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.Hyperlink; @@ -43,7 +44,7 @@ public AccountAgeTab3View(AccountAgeTab3Model model, Pane userProfileSelection) { super(new VBox(), model, controller); - root.setSpacing(20); + root.setSpacing(5); root.setAlignment(Pos.TOP_LEFT); root.getStyleClass().add("account-age"); @@ -54,6 +55,7 @@ public AccountAgeTab3View(AccountAgeTab3Model model, requestCertificateButton = new Button(Res.get("user.reputation.request")); requestCertificateButton.getStyleClass().add("outlined-button"); + VBox.setMargin(requestCertificateButton, new Insets(-5, 0, 15, 0)); backButton = new Button(Res.get("action.back")); @@ -101,8 +103,10 @@ protected void onViewDetached() { private VBox createAndGetStepOne(Pane userProfileSelection) { Label title = createAndGetStepLabel(Res.get("user.reputation.accountAge.import.step1.title")); Label instruction = createAndGetStepInstructionLabel(Res.get("user.reputation.accountAge.import.step1.instruction")); + VBox.setMargin(userProfileSelection, new Insets(0, 0, -10, -15)); VBox vBox = new VBox(title, instruction, userProfileSelection, Layout.hLine()); vBox.getStyleClass().add("import-step"); + VBox.setMargin(vBox, new Insets(20, 0, 0, 0)); return vBox; } @@ -112,6 +116,8 @@ private VBox createAndGetStepTwo() { pubKeyHash = new MaterialTextField(Res.get("user.reputation.accountAge.import.step2.profileId"), ""); pubKeyHash.setEditable(false); pubKeyHash.showCopyIcon(); + pubKeyHash.getStyleClass().add("material-field"); + VBox.setMargin(pubKeyHash, new Insets(10, 0, 15, 2)); VBox vBox = new VBox(title, instruction, pubKeyHash, Layout.hLine()); vBox.getStyleClass().add("import-step"); return vBox; @@ -122,6 +128,7 @@ private VBox createAndGetStepThree() { Label instructionOne = createAndGetStepInstructionLabel(Res.get("user.reputation.accountAge.import.step3.instruction1")); Label instructionTwo = createAndGetStepInstructionLabel(Res.get("user.reputation.accountAge.import.step3.instruction2")); Label instructionThree = createAndGetStepInstructionLabel(Res.get("user.reputation.accountAge.import.step3.instruction3")); + VBox.setMargin(instructionThree, new Insets(0, 0, 15, 0)); VBox vBox = new VBox(title, instructionOne, instructionTwo, instructionThree, Layout.hLine()); vBox.getStyleClass().add("import-step"); return vBox; @@ -132,6 +139,8 @@ private VBox createAndGetStepFour() { Label instruction = createAndGetStepInstructionLabel(Res.get("user.reputation.accountAge.import.step4.instruction")); signedMessage = new MaterialTextField(Res.get("user.reputation.accountAge.import.step4.signedMessage")); signedMessage.setEditable(true); + signedMessage.getStyleClass().add("material-field"); + VBox.setMargin(signedMessage, new Insets(10, 0, 15, 2)); UIThread.runOnNextRenderFrame(signedMessage::requestFocus); VBox vBox = new VBox(title, instruction, signedMessage); vBox.getStyleClass().add("import-step"); diff --git a/apps/desktop/desktop/src/main/resources/css/user.css b/apps/desktop/desktop/src/main/resources/css/user.css index edf843d2e4..341da89493 100644 --- a/apps/desktop/desktop/src/main/resources/css/user.css +++ b/apps/desktop/desktop/src/main/resources/css/user.css @@ -1,5 +1,5 @@ /******************************************************************************* - * Payment account * + * Payment account * ******************************************************************************/ .user-payment-account-no-data { @@ -24,15 +24,34 @@ } /******************************************************************************* - * Reputation * + * Reputation * ******************************************************************************/ .user-reputation-table-view { -fx-background-color: -bisq-dark-grey-20; } +.account-age .import-step .step-title { + -fx-fill: -fx-light-text-color; + -fx-text-fill: -fx-light-text-color; + -fx-font-family: "IBM Plex Sans Medium"; + -fx-font-size: 0.93em; +} + +.account-age .import-step .step-instruction { + -fx-fill: -fx-mid-text-color; + -fx-text-fill: -fx-mid-text-color; + -fx-font-family: "IBM Plex Sans Light"; + -fx-font-size: 0.9em; + -fx-padding: 2 0 0 2; +} + +.account-age .import-step .material-field { + -fx-font-size: 0.9em; +} + /******************************************************************************* - * Bonded roles * + * Bonded roles * ******************************************************************************/ .user-bonded-roles-info-headline { @@ -67,4 +86,4 @@ .user-bonded-roles-tab-view { -fx-background-color: -bisq-dark-grey-20; -fx-background-radius: 8; -} \ No newline at end of file +} From beb65de23ac180cf0b1f92882a1c32abaeb52160 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Sat, 23 Mar 2024 22:16:12 +0100 Subject: [PATCH 057/119] Remove comment --- .../reputation/accountAge/tab3/AccountAgeTab3Controller.java | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3Controller.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3Controller.java index f1446e0adc..631a12287b 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3Controller.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3Controller.java @@ -100,7 +100,6 @@ void onClose() { public void onRequestAuthorization() { String signedMessage = String.valueOf(model.getSignedMessage()); - // Question: do we still need the prefix? if (signedMessage.startsWith(PREFIX)) { signedMessage = signedMessage.replace(PREFIX, ""); } From b163ccc4f4d0bcdf2ca7b9c78808d5128471a836 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sat, 23 Mar 2024 13:28:34 +0700 Subject: [PATCH 058/119] Use offer data to display localized language instead of text set by the offer maker --- .../bisq_easy/BisqEasyServiceUtil.java | 63 +++++++++++++++++++ .../review/TradeWizardReviewController.java | 38 +++-------- .../TradeWizardSelectOfferController.java | 55 +++++----------- .../chatMessages/ChatMessageListItem.java | 23 ++++++- .../chatMessages/ChatMessagesListView.java | 6 ++ 5 files changed, 114 insertions(+), 71 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/BisqEasyServiceUtil.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/BisqEasyServiceUtil.java index 9eb426bf97..3cc266f7d8 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/BisqEasyServiceUtil.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/BisqEasyServiceUtil.java @@ -18,11 +18,25 @@ package bisq.desktop.main.content.bisq_easy; import bisq.bisq_easy.BisqEasyService; +import bisq.account.payment_method.FiatPaymentMethod; +import bisq.bonded_roles.market_price.MarketPriceService; import bisq.chat.bisqeasy.open_trades.BisqEasyOpenTradeChannel; +import bisq.common.currency.Market; import bisq.desktop.ServiceProvider; +import bisq.i18n.Res; import bisq.network.identity.NetworkId; +import bisq.offer.Direction; +import bisq.offer.amount.OfferAmountFormatter; +import bisq.offer.amount.spec.AmountSpec; +import bisq.offer.amount.spec.RangeAmountSpec; import bisq.offer.bisq_easy.BisqEasyOffer; import bisq.offer.options.OfferOptionUtil; +import bisq.offer.payment_method.PaymentMethodSpecFormatter; +import bisq.offer.price.spec.FixPriceSpec; +import bisq.offer.price.spec.FloatPriceSpec; +import bisq.offer.price.spec.PriceSpec; +import bisq.presentation.formatters.PercentageFormatter; +import bisq.presentation.formatters.PriceFormatter; import bisq.trade.Trade; import bisq.trade.bisq_easy.BisqEasyTrade; import bisq.user.identity.UserIdentity; @@ -31,6 +45,7 @@ import bisq.user.profile.UserProfileService; import bisq.user.reputation.ReputationService; +import java.util.List; import java.util.Optional; public class BisqEasyServiceUtil { @@ -74,4 +89,52 @@ public static boolean offerMatchesMinRequiredReputationScore(ReputationService r return myScoreAsSeller >= offersRequiredScore; } } + + public static String createOfferBookMessageText(MarketPriceService marketPriceService, + Direction direction, + Market market, + List fiatPaymentMethods, + AmountSpec amountSpec, + PriceSpec priceSpec) { + String paymentMethodNames = PaymentMethodSpecFormatter.fromPaymentMethods(fiatPaymentMethods); + return createOfferBookMessageText(marketPriceService, + direction, + market, + paymentMethodNames, + amountSpec, + priceSpec); + } + + public static String createOfferBookMessageText(MarketPriceService marketPriceService, + Direction direction, + Market market, + String paymentMethodNames, + AmountSpec amountSpec, + PriceSpec priceSpec) { + String priceInfo; + if (direction.isSell()) { + if (priceSpec instanceof FixPriceSpec) { + FixPriceSpec fixPriceSpec = (FixPriceSpec) priceSpec; + String price = PriceFormatter.formatWithCode(fixPriceSpec.getPriceQuote()); + priceInfo = Res.get("bisqEasy.tradeWizard.review.chatMessage.fixPrice", price); + } else if (priceSpec instanceof FloatPriceSpec) { + FloatPriceSpec floatPriceSpec = (FloatPriceSpec) priceSpec; + String percent = PercentageFormatter.formatToPercentWithSymbol(floatPriceSpec.getPercentage()); + priceInfo = Res.get("bisqEasy.tradeWizard.review.chatMessage.floatPrice", percent); + } else { + priceInfo = Res.get("bisqEasy.tradeWizard.review.chatMessage.marketPrice"); + } + } else { + priceInfo = ""; + } + + String directionString = Res.get("offer." + direction.name().toLowerCase()).toUpperCase(); + boolean hasAmountRange = amountSpec instanceof RangeAmountSpec; + String quoteAmountAsString = OfferAmountFormatter.formatQuoteAmount(marketPriceService, amountSpec, priceSpec, market, hasAmountRange, true); + return Res.get("bisqEasy.tradeWizard.review.chatMessage", + directionString, + quoteAmountAsString, + paymentMethodNames, + priceInfo); + } } \ No newline at end of file diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/review/TradeWizardReviewController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/review/TradeWizardReviewController.java index d8f91c7f02..69d830d52e 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/review/TradeWizardReviewController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/review/TradeWizardReviewController.java @@ -39,11 +39,11 @@ import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Controller; import bisq.desktop.components.overlay.Popup; +import bisq.desktop.main.content.bisq_easy.BisqEasyServiceUtil; import bisq.desktop.main.content.bisq_easy.components.PriceInput; import bisq.desktop.main.content.bisq_easy.components.ReviewDataDisplay; import bisq.i18n.Res; import bisq.offer.Direction; -import bisq.offer.amount.OfferAmountFormatter; import bisq.offer.amount.OfferAmountUtil; import bisq.offer.amount.spec.AmountSpec; import bisq.offer.amount.spec.FixedAmountSpec; @@ -53,7 +53,6 @@ import bisq.offer.payment_method.PaymentMethodSpec; import bisq.offer.payment_method.PaymentMethodSpecFormatter; import bisq.offer.price.PriceUtil; -import bisq.offer.price.spec.FixPriceSpec; import bisq.offer.price.spec.FloatPriceSpec; import bisq.offer.price.spec.MarketPriceSpec; import bisq.offer.price.spec.PriceSpec; @@ -141,35 +140,12 @@ public void setDataForCreateOffer(Direction direction, AmountSpec amountSpec, PriceSpec priceSpec) { model.setCreateOfferMode(true); - - String priceInfo; - if (direction.isSell()) { - if (priceSpec instanceof FixPriceSpec) { - FixPriceSpec fixPriceSpec = (FixPriceSpec) priceSpec; - String price = PriceFormatter.formatWithCode(fixPriceSpec.getPriceQuote()); - priceInfo = Res.get("bisqEasy.tradeWizard.review.chatMessage.fixPrice", price); - } else if (priceSpec instanceof FloatPriceSpec) { - FloatPriceSpec floatPriceSpec = (FloatPriceSpec) priceSpec; - String percent = PercentageFormatter.formatToPercentWithSymbol(floatPriceSpec.getPercentage()); - priceInfo = Res.get("bisqEasy.tradeWizard.review.chatMessage.floatPrice", percent); - } else { - priceInfo = Res.get("bisqEasy.tradeWizard.review.chatMessage.marketPrice"); - } - } else { - priceInfo = ""; - } - - String directionString = Res.get("offer." + direction.name().toLowerCase()).toUpperCase(); - boolean hasAmountRange = amountSpec instanceof RangeAmountSpec; - String quoteAmountAsString = OfferAmountFormatter.formatQuoteAmount(marketPriceService, amountSpec, priceSpec, market, hasAmountRange, true); - - String paymentMethodNames = PaymentMethodSpecFormatter.fromPaymentMethods(fiatPaymentMethods); - String chatMessageText = Res.get("bisqEasy.tradeWizard.review.chatMessage", - directionString, - quoteAmountAsString, - paymentMethodNames, - priceInfo); - + String chatMessageText = BisqEasyServiceUtil.createOfferBookMessageText(marketPriceService, + direction, + market, + fiatPaymentMethods, + amountSpec, + priceSpec); UserIdentity userIdentity = checkNotNull(userIdentityService.getSelectedUserIdentity()); BisqEasyOffer bisqEasyOffer = new BisqEasyOffer( userIdentity.getUserProfile().getNetworkId(), diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferController.java index 12ad8fab42..10a94bf2d3 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferController.java @@ -39,13 +39,8 @@ import bisq.offer.amount.spec.RangeAmountSpec; import bisq.offer.bisq_easy.BisqEasyOffer; import bisq.offer.payment_method.PaymentMethodSpec; -import bisq.offer.payment_method.PaymentMethodSpecFormatter; import bisq.offer.payment_method.PaymentMethodSpecUtil; -import bisq.offer.price.spec.FixPriceSpec; -import bisq.offer.price.spec.FloatPriceSpec; import bisq.offer.price.spec.PriceSpec; -import bisq.presentation.formatters.PercentageFormatter; -import bisq.presentation.formatters.PriceFormatter; import bisq.settings.SettingsService; import bisq.trade.Trade; import bisq.trade.bisq_easy.BisqEasyTradeService; @@ -161,55 +156,39 @@ public void reset() { @Override public void onActivate() { - UserIdentity userIdentity = checkNotNull(userIdentityService.getSelectedUserIdentity()); - String priceInfo; - PriceSpec priceSpec = model.getPriceSpec(); Direction direction = model.getDirection(); - if (direction.isSell()) { - if (priceSpec instanceof FixPriceSpec) { - FixPriceSpec fixPriceSpec = (FixPriceSpec) priceSpec; - String price = PriceFormatter.formatWithCode(fixPriceSpec.getPriceQuote()); - priceInfo = Res.get("bisqEasy.tradeWizard.review.chatMessage.fixPrice", price); - } else if (priceSpec instanceof FloatPriceSpec) { - FloatPriceSpec floatPriceSpec = (FloatPriceSpec) priceSpec; - String percent = PercentageFormatter.formatToPercentWithSymbol(floatPriceSpec.getPercentage()); - priceInfo = Res.get("bisqEasy.tradeWizard.review.chatMessage.floatPrice", percent); - } else { - priceInfo = Res.get("bisqEasy.tradeWizard.review.chatMessage.marketPrice"); - } - } else { - priceInfo = ""; - } - - String directionString = Res.get("offer." + direction.name().toLowerCase()).toUpperCase(); + Market market = model.getMarket(); AmountSpec amountSpec = model.getAmountSpec(); + PriceSpec priceSpec = model.getPriceSpec(); + List fiatPaymentMethods = model.getFiatPaymentMethods(); + boolean hasAmountRange = amountSpec instanceof RangeAmountSpec; - String quoteAmountAsString = OfferAmountFormatter.formatQuoteAmount(marketPriceService, amountSpec, model.getPriceSpec(), model.getMarket(), hasAmountRange, true); + String quoteAmountAsString = OfferAmountFormatter.formatQuoteAmount(marketPriceService, amountSpec, priceSpec, market, hasAmountRange, true); model.setQuoteAmountAsString(quoteAmountAsString); + model.setQuoteAmountAsString(OfferAmountFormatter.formatQuoteAmount(marketPriceService, + amountSpec, + priceSpec, + market, + hasAmountRange, + true)); - - String paymentMethodNames = PaymentMethodSpecFormatter.fromPaymentMethods(model.getFiatPaymentMethods()); - String chatMessageText = Res.get("bisqEasy.tradeWizard.review.chatMessage", - directionString, - quoteAmountAsString, - paymentMethodNames, - priceInfo); - + String chatMessageText = BisqEasyServiceUtil.createOfferBookMessageText(marketPriceService, direction, market, fiatPaymentMethods, amountSpec, priceSpec); model.setMyOfferText(chatMessageText); + UserIdentity userIdentity = checkNotNull(userIdentityService.getSelectedUserIdentity()); BisqEasyOffer bisqEasyOffer = new BisqEasyOffer( userIdentity.getUserProfile().getNetworkId(), direction, - model.getMarket(), + market, amountSpec, priceSpec, - new ArrayList<>(model.getFiatPaymentMethods()), + new ArrayList<>(fiatPaymentMethods), userIdentity.getUserProfile().getTerms(), bisqEasyService.getMinRequiredReputationScore().get(), new ArrayList<>(settingsService.getSupportedLanguageCodes())); model.setBisqEasyOffer(bisqEasyOffer); - Optional optionalChannel = bisqEasyOfferbookChannelService.findChannel(model.getMarket()); + Optional optionalChannel = bisqEasyOfferbookChannelService.findChannel(market); if (optionalChannel.isPresent()) { BisqEasyOfferbookChannel channel = optionalChannel.get(); model.setSelectedChannel(channel); @@ -242,7 +221,7 @@ public void onActivate() { model.getIsBackButtonHighlighted().set(!showOffers); if (showOffers) { - model.setHeadline(model.getDirection().isBuy() ? + model.setHeadline(direction.isBuy() ? Res.get("bisqEasy.tradeWizard.selectOffer.headline.buyer", quoteAmountAsString) : Res.get("bisqEasy.tradeWizard.selectOffer.headline.seller", quoteAmountAsString)); model.setSubHeadLine(Res.get("bisqEasy.tradeWizard.selectOffer.subHeadline")); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessageListItem.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessageListItem.java index 8166356f2c..c1d6845e0a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessageListItem.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessageListItem.java @@ -17,6 +17,7 @@ package bisq.desktop.main.content.components.chatMessages; +import bisq.bonded_roles.market_price.MarketPriceService; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; import bisq.chat.Citation; @@ -30,6 +31,7 @@ import bisq.common.observable.map.HashMapObserver; import bisq.common.util.StringUtils; import bisq.desktop.common.threading.UIThread; +import bisq.desktop.main.content.bisq_easy.BisqEasyServiceUtil; import bisq.desktop.main.content.components.ReputationScoreDisplay; import bisq.i18n.Res; import bisq.network.NetworkService; @@ -38,6 +40,7 @@ import bisq.network.p2p.services.confidential.resend.ResendMessageService; import bisq.offer.Direction; import bisq.offer.bisq_easy.BisqEasyOffer; +import bisq.offer.payment_method.PaymentMethodSpecFormatter; import bisq.presentation.formatters.DateFormatter; import bisq.trade.Trade; import bisq.trade.bisq_easy.BisqEasyTradeService; @@ -99,10 +102,12 @@ public final class ChatMessageListItem mapPins = new HashSet<>(); @EqualsAndHashCode.Exclude private final Set statusPins = new HashSet<>(); + private final MarketPriceService marketPriceService; private final UserIdentityService userIdentityService; public ChatMessageListItem(M chatMessage, C chatChannel, + MarketPriceService marketPriceService, UserProfileService userProfileService, ReputationService reputationService, BisqEasyTradeService bisqEasyTradeService, @@ -111,6 +116,7 @@ public ChatMessageListItem(M chatMessage, Optional resendMessageService) { this.chatMessage = chatMessage; this.chatChannel = chatChannel; + this.marketPriceService = marketPriceService; this.userIdentityService = userIdentityService; if (chatMessage instanceof PrivateChatMessage) { @@ -118,8 +124,7 @@ public ChatMessageListItem(M chatMessage, } else { senderUserProfile = userProfileService.findUserProfile(chatMessage.getAuthorUserProfileId()); } - String editPostFix = chatMessage.isWasEdited() ? EDITED_POST_FIX : ""; - message = chatMessage.getText() + editPostFix; + citation = chatMessage.getCitation(); date = DateFormatter.formatDateTime(new Date(chatMessage.getDate()), DateFormat.MEDIUM, DateFormat.SHORT, true, " " + Res.get("temporal.at") + " "); @@ -132,6 +137,7 @@ public ChatMessageListItem(M chatMessage, if (chatMessage instanceof BisqEasyOfferbookMessage) { BisqEasyOfferbookMessage bisqEasyOfferbookMessage = (BisqEasyOfferbookMessage) chatMessage; + message = getLocalizedOfferbookMessage(bisqEasyOfferbookMessage); if (userIdentityService.getSelectedUserIdentity() != null && bisqEasyOfferbookMessage.getBisqEasyOffer().isPresent()) { UserProfile userProfile = userIdentityService.getSelectedUserIdentity().getUserProfile(); NetworkId takerNetworkId = userProfile.getNetworkId(); @@ -142,6 +148,8 @@ public ChatMessageListItem(M chatMessage, offerAlreadyTaken = false; } } else { + String editPostFix = chatMessage.isWasEdited() ? EDITED_POST_FIX : ""; + message = chatMessage.getText() + editPostFix; offerAlreadyTaken = false; } @@ -225,6 +233,17 @@ public void clear() { })); } + private String getLocalizedOfferbookMessage(BisqEasyOfferbookMessage chatMessage) { + BisqEasyOffer bisqEasyOffer = chatMessage.getBisqEasyOffer().orElseThrow(); + String fiatPaymentMethods = PaymentMethodSpecFormatter.fromPaymentMethodSpecs(bisqEasyOffer.getQuoteSidePaymentMethodSpecs()); + return BisqEasyServiceUtil.createOfferBookMessageText(marketPriceService, + bisqEasyOffer.getDirection(), + bisqEasyOffer.getMarket(), + fiatPaymentMethods, + bisqEasyOffer.getAmountSpec(), + bisqEasyOffer.getPriceSpec()); + } + @Override public int compareTo(ChatMessageListItem o) { return Comparator.comparingLong(ChatMessage::getDate).compare(this.getChatMessage(), o.getChatMessage()); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesListView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesListView.java index 9ab164a49d..1e025d50fd 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesListView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesListView.java @@ -19,6 +19,8 @@ import bisq.bisq_easy.BisqEasyService; import bisq.bisq_easy.NavigationTarget; +import bisq.bonded_roles.market_price.MarketPriceService; +import bisq.bonded_roles.market_price.MarketPriceService; import bisq.chat.*; import bisq.chat.bisqeasy.BisqEasyOfferMessage; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannel; @@ -163,6 +165,7 @@ public static class Controller implements bisq.desktop.common.view.Controller { private final NetworkService networkService; private final Optional resendMessageService; private final BisqEasyService bisqEasyService; + private final MarketPriceService marketPriceService; private Pin selectedChannelPin, chatMessagesPin, offerOnlySettingsPin; private Subscription selectedChannelSubscription, focusSubscription, scrollValuePin, scrollBarVisiblePin; @@ -182,6 +185,7 @@ private Controller(ServiceProvider serviceProvider, bannedUserService = serviceProvider.getUserService().getBannedUserService(); networkService = serviceProvider.getNetworkService(); resendMessageService = serviceProvider.getNetworkService().getResendMessageService(); + marketPriceService = serviceProvider.getBondedRolesService().getMarketPriceService(); this.mentionUserHandler = mentionUserHandler; this.showChatUserDetailsHandler = showChatUserDetailsHandler; this.replyHandler = replyHandler; @@ -637,6 +641,7 @@ private > Pin bindChatMessages(C model.chatMessages.addAll(channel.getChatMessages().stream() .map(chatMessage -> new ChatMessageListItem<>(chatMessage, channel, + marketPriceService, userProfileService, reputationService, bisqEasyTradeService, @@ -656,6 +661,7 @@ public void add(M chatMessage) { UIThread.runOnNextRenderFrame(() -> { ChatMessageListItem item = new ChatMessageListItem<>(chatMessage, channel, + marketPriceService, userProfileService, reputationService, bisqEasyTradeService, From 0317311db4773a32e4a897172b6a86798e4cc0a3 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sat, 23 Mar 2024 13:35:44 +0700 Subject: [PATCH 059/119] Clean up unused code --- .../trade_wizard/TradeWizardController.java | 10 +--- .../TradeWizardSelectOfferController.java | 50 ++----------------- .../TradeWizardSelectOfferModel.java | 20 -------- 3 files changed, 6 insertions(+), 74 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/TradeWizardController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/TradeWizardController.java index d7f1907425..098b49a7f4 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/TradeWizardController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/TradeWizardController.java @@ -76,9 +76,8 @@ public InitData(boolean isCreateOfferMode) { private final TradeWizardReviewController tradeWizardReviewController; private final EventHandler onKeyPressedHandler = this::onKeyPressed; private final ListChangeListener paymentMethodsListener; - private Subscription directionPin, marketPin, amountSpecPin, - isMinAmountEnabledPin, priceSpecPin, - selectedBisqEasyOfferPin, isBackButtonHighlightedPin; + private Subscription directionPin, marketPin, amountSpecPin, priceSpecPin, selectedBisqEasyOfferPin, + isBackButtonHighlightedPin; public TradeWizardController(ServiceProvider serviceProvider) { super(NavigationTarget.TRADE_WIZARD); @@ -158,10 +157,6 @@ public void onActivate() { amountSpec -> { tradeWizardSelectOfferController.setAmountSpec(amountSpec); }); - isMinAmountEnabledPin = EasyBind.subscribe(tradeWizardAmountController.getIsMinAmountEnabled(), - isMinAmountEnabled -> { - tradeWizardSelectOfferController.setIsMinAmountEnabled(isMinAmountEnabled); - }); priceSpecPin = EasyBind.subscribe(tradeWizardPriceController.getPriceSpec(), priceSpec -> { tradeWizardAmountController.setPriceSpec(priceSpec); @@ -187,7 +182,6 @@ public void onDeactivate() { directionPin.unsubscribe(); marketPin.unsubscribe(); amountSpecPin.unsubscribe(); - isMinAmountEnabledPin.unsubscribe(); priceSpecPin.unsubscribe(); selectedBisqEasyOfferPin.unsubscribe(); isBackButtonHighlightedPin.unsubscribe(); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferController.java index 10a94bf2d3..41872d76b3 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferController.java @@ -24,7 +24,6 @@ import bisq.chat.ChatService; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannel; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannelService; -import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; import bisq.common.currency.Market; import bisq.common.monetary.Monetary; import bisq.desktop.ServiceProvider; @@ -45,7 +44,6 @@ import bisq.trade.Trade; import bisq.trade.bisq_easy.BisqEasyTradeService; import bisq.user.banned.BannedUserService; -import bisq.user.identity.UserIdentity; import bisq.user.identity.UserIdentityService; import bisq.user.profile.UserProfile; import bisq.user.profile.UserProfileService; @@ -55,13 +53,14 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import java.util.*; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkNotNull; - @Slf4j public class TradeWizardSelectOfferController implements Controller { private final TradeWizardSelectOfferModel model; @@ -145,11 +144,6 @@ public void setPriceSpec(PriceSpec priceSpec) { } } - public void setIsMinAmountEnabled(boolean isMinAmountEnabled) { - model.setMinAmountEnabled(isMinAmountEnabled); - resetSelectedOffer(); - } - public void reset() { model.reset(); } @@ -160,49 +154,13 @@ public void onActivate() { Market market = model.getMarket(); AmountSpec amountSpec = model.getAmountSpec(); PriceSpec priceSpec = model.getPriceSpec(); - List fiatPaymentMethods = model.getFiatPaymentMethods(); boolean hasAmountRange = amountSpec instanceof RangeAmountSpec; String quoteAmountAsString = OfferAmountFormatter.formatQuoteAmount(marketPriceService, amountSpec, priceSpec, market, hasAmountRange, true); - model.setQuoteAmountAsString(quoteAmountAsString); - model.setQuoteAmountAsString(OfferAmountFormatter.formatQuoteAmount(marketPriceService, - amountSpec, - priceSpec, - market, - hasAmountRange, - true)); - - String chatMessageText = BisqEasyServiceUtil.createOfferBookMessageText(marketPriceService, direction, market, fiatPaymentMethods, amountSpec, priceSpec); - model.setMyOfferText(chatMessageText); - - UserIdentity userIdentity = checkNotNull(userIdentityService.getSelectedUserIdentity()); - BisqEasyOffer bisqEasyOffer = new BisqEasyOffer( - userIdentity.getUserProfile().getNetworkId(), - direction, - market, - amountSpec, - priceSpec, - new ArrayList<>(fiatPaymentMethods), - userIdentity.getUserProfile().getTerms(), - bisqEasyService.getMinRequiredReputationScore().get(), - new ArrayList<>(settingsService.getSupportedLanguageCodes())); - model.setBisqEasyOffer(bisqEasyOffer); Optional optionalChannel = bisqEasyOfferbookChannelService.findChannel(market); if (optionalChannel.isPresent()) { BisqEasyOfferbookChannel channel = optionalChannel.get(); - model.setSelectedChannel(channel); - - BisqEasyOfferbookMessage myOfferMessage = new BisqEasyOfferbookMessage(channel.getId(), - userIdentity.getUserProfile().getId(), - Optional.of(bisqEasyOffer), - Optional.of(chatMessageText), - Optional.empty(), - new Date().getTime(), - false); - - model.setMyOfferMessage(myOfferMessage); - model.getMatchingOffers().setAll(channel.getChatMessages().stream() .filter(chatMessage -> chatMessage.getBisqEasyOffer().isPresent()) .map(chatMessage -> new TradeWizardSelectOfferView.ListItem(chatMessage.getBisqEasyOffer().get(), diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferModel.java index 25d7bcb33f..bd1c45f4d8 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/select_offer/TradeWizardSelectOfferModel.java @@ -18,8 +18,6 @@ package bisq.desktop.main.content.bisq_easy.trade_wizard.select_offer; import bisq.account.payment_method.FiatPaymentMethod; -import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannel; -import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; import bisq.common.currency.Market; import bisq.desktop.common.view.Model; import bisq.offer.Direction; @@ -43,8 +41,6 @@ @Getter class TradeWizardSelectOfferModel implements Model { - @Setter - private BisqEasyOfferbookChannel selectedChannel; @Setter private Direction direction; @Setter @@ -56,16 +52,6 @@ class TradeWizardSelectOfferModel implements Model { @Setter private AmountSpec amountSpec; @Setter - private boolean isMinAmountEnabled; - @Setter - private BisqEasyOffer bisqEasyOffer; - @Setter - private BisqEasyOfferbookMessage myOfferMessage; - @Setter - private String quoteAmountAsString; - @Setter - private String myOfferText; - @Setter private String headline; @Setter private String subHeadLine; @@ -79,17 +65,11 @@ class TradeWizardSelectOfferModel implements Model { private final BooleanProperty isBackButtonHighlighted = new SimpleBooleanProperty(); void reset() { - selectedChannel = null; direction = null; market = null; fiatPaymentMethods.clear(); priceSpec = new MarketPriceSpec(); amountSpec = null; - isMinAmountEnabled = false; - bisqEasyOffer = null; - myOfferMessage = null; - quoteAmountAsString = null; - myOfferText = null; headline = null; subHeadLine = null; selectedItem = null; From 2020d163950272ea85531f1b0cab3959a6131ea3 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 24 Mar 2024 12:03:08 +0700 Subject: [PATCH 060/119] Filter for BisqEasyOfferbookMessage with offers set --- .../content/components/chatMessages/ChatMessageListItem.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessageListItem.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessageListItem.java index c1d6845e0a..e85c2b4cb4 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessageListItem.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessageListItem.java @@ -135,7 +135,8 @@ public ChatMessageListItem(M chatMessage, reputationScore = senderUserProfile.flatMap(reputationService::findReputationScore).orElse(ReputationScore.NONE); reputationScoreDisplay.setReputationScore(reputationScore); - if (chatMessage instanceof BisqEasyOfferbookMessage) { + if (chatMessage instanceof BisqEasyOfferbookMessage && + ((BisqEasyOfferbookMessage) chatMessage).hasBisqEasyOffer()) { BisqEasyOfferbookMessage bisqEasyOfferbookMessage = (BisqEasyOfferbookMessage) chatMessage; message = getLocalizedOfferbookMessage(bisqEasyOfferbookMessage); if (userIdentityService.getSelectedUserIdentity() != null && bisqEasyOfferbookMessage.getBisqEasyOffer().isPresent()) { @@ -148,6 +149,7 @@ public ChatMessageListItem(M chatMessage, offerAlreadyTaken = false; } } else { + // Normal chat message or BisqEasyOfferbookMessage without offer String editPostFix = chatMessage.isWasEdited() ? EDITED_POST_FIX : ""; message = chatMessage.getText() + editPostFix; offerAlreadyTaken = false; From 170baf40fab7d9eb1c8e30a7eaa573b7fee1d8c0 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 24 Mar 2024 16:19:29 +0700 Subject: [PATCH 061/119] Reduce from 10-15% to 5-10% as common markup. For higher amount trades the 5% seems quite common. --- i18n/src/main/resources/bisq_easy.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/src/main/resources/bisq_easy.properties b/i18n/src/main/resources/bisq_easy.properties index 43e6427c66..0e41fd4601 100644 --- a/i18n/src/main/resources/bisq_easy.properties +++ b/i18n/src/main/resources/bisq_easy.properties @@ -133,7 +133,7 @@ bisqEasy.component.amount.maxRangeValue=Max {0} bisqEasy.component.amount.baseSide.tooltip.price=Calculated with the seller's trade price. bisqEasy.component.amount.baseSide.tooltip.marketPrice=Calculated with current market price.\n\ Sellers may ask for a higher price as they have costs for acquiring reputation.\n\ - 10-15% price premium is common. + 5-15% price premium is common. bisqEasy.component.amount.baseSide.tooltip.buyer.btcAmount=The expected Bitcoin amount to receive. bisqEasy.component.amount.baseSide.tooltip.seller.btcAmount=The expected Bitcoin amount to spend. bisqEasy.component.amount.baseSide.tooltip.bestOfferPrice=Calculated with the best price from matching offers: {0} @@ -304,7 +304,7 @@ bisqEasy.tradeGuide.security.headline=How safe is it to trade on Bisq Easy? bisqEasy.tradeGuide.security.content=\ - Bisq Easy's security model is optimized for small trade amounts.\n\ - The model relies on the reputation of the seller, who is usually an experienced Bisq user and is expected to provide helpful support to new users.\n\ - - Building up reputation can be costly, leading to a common 10-15% price premium to cover extra expenses and compensate for the seller's service.\n\ + - Building up reputation can be costly, leading to a common 5-15% price premium to cover extra expenses and compensate for the seller's service.\n\ - Trading with sellers lacking reputation carries significant risks and should only be undertaken if the risks are thoroughly understood and managed. bisqEasy.tradeGuide.process.headline=How does the trade process works? From c6e49dd9977a5d99b2e790ce0f0a04b080ba7d9e Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 24 Mar 2024 16:28:41 +0700 Subject: [PATCH 062/119] Rest price when market gets changed. --- .../desktop/main/content/bisq_easy/components/PriceInput.java | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/components/PriceInput.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/components/PriceInput.java index 18c43f9c85..43067b7eb5 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/components/PriceInput.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/components/PriceInput.java @@ -110,6 +110,7 @@ private Controller(MarketPriceService marketPriceService) { } public void setMarket(Market market) { + model.reset(); model.market = market; updateFromMarketPrice(); } From 7879933c1cb17ca05d0b5027c03dff3d360d3cfe Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 24 Mar 2024 19:21:33 +0700 Subject: [PATCH 063/119] We only set the headline if defaultHeadline is present (not AlertType.BAN) and if headline is not same as defaultHeadline (for being backward compatible with old data which did not contain the headline field). As nice side effect we save a few bytes if headline is default. --- .../alert/AuthorizedAlertData.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AuthorizedAlertData.java b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AuthorizedAlertData.java index d520e099eb..feecf4223b 100644 --- a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AuthorizedAlertData.java +++ b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AuthorizedAlertData.java @@ -83,6 +83,7 @@ public AuthorizedAlertData(String id, this.staticPublicKeysProvided = staticPublicKeysProvided; verify(); + toProto(); } @Override @@ -105,7 +106,16 @@ public bisq.bonded_roles.protobuf.AuthorizedAlertData toProto() { .setSecurityManagerProfileId(securityManagerProfileId) .setStaticPublicKeysProvided(staticPublicKeysProvided); message.ifPresent(builder::setMessage); - headline.ifPresent(builder::setHeadline); + headline.ifPresent(headline -> { + // We only set the headline if defaultHeadline is present (not AlertType.BAN) and + // if headline is not same as defaultHeadline (for being backward compatible with old data which did not + // contain the headline field). As nice side effect we save a few bytes if headline is default. + getDefaultHeadline(alertType).ifPresent(defaultHeadline -> { + if (!headline.equals(defaultHeadline)) { + builder.setHeadline(headline); + } + }); + }); minVersion.ifPresent(builder::setMinVersion); bannedRole.ifPresent(authorizedBondedRole -> builder.setBannedRole(authorizedBondedRole.toProto())); return builder.build(); @@ -128,7 +138,11 @@ public static AuthorizedAlertData fromProto(bisq.bonded_roles.protobuf.Authorize } private static Optional getDefaultHeadline(AlertType alertType) { - return Optional.of(Res.get("authorizedRole.securityManager.alertType." + alertType.name())); + if (alertType == AlertType.BAN) { + return Optional.empty(); + } else { + return Optional.of(Res.get("authorizedRole.securityManager.alertType." + alertType.name())); + } } public static ProtoResolver getResolver() { From 4e97eb6b6bc467392f4391044a86e9cfae4fdf03 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Sun, 24 Mar 2024 20:07:02 +0100 Subject: [PATCH 064/119] Use MaterialTextArea to input signed message --- .../content/user/reputation/accountAge/AccountAgeView.java | 2 +- .../user/reputation/accountAge/tab3/AccountAgeTab3View.java | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/AccountAgeView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/AccountAgeView.java index d533fc286a..c1c9153f4e 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/AccountAgeView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/AccountAgeView.java @@ -64,7 +64,7 @@ protected void onViewAttached() { closeButton.setOnAction(e -> controller.onClose()); root.setPrefWidth(OverlayModel.WIDTH); - root.setPrefHeight(OverlayModel.HEIGHT + 170); + root.setPrefHeight(OverlayModel.HEIGHT + 150); } @Override diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3View.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3View.java index d222dbc3d9..2ed1a1fbf5 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3View.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/user/reputation/accountAge/tab3/AccountAgeTab3View.java @@ -21,6 +21,7 @@ import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.View; import bisq.desktop.components.containers.Spacer; +import bisq.desktop.components.controls.MaterialTextArea; import bisq.desktop.components.controls.MaterialTextField; import bisq.i18n.Res; import javafx.geometry.Insets; @@ -37,7 +38,8 @@ public class AccountAgeTab3View extends View { private final Button closeButton, backButton, requestCertificateButton; private final Hyperlink learnMore; - private MaterialTextField pubKeyHash, signedMessage; + private MaterialTextField pubKeyHash; + private MaterialTextArea signedMessage; public AccountAgeTab3View(AccountAgeTab3Model model, AccountAgeTab3Controller controller, @@ -137,7 +139,7 @@ private VBox createAndGetStepThree() { private VBox createAndGetStepFour() { Label title = createAndGetStepLabel(Res.get("user.reputation.accountAge.import.step4.title")); Label instruction = createAndGetStepInstructionLabel(Res.get("user.reputation.accountAge.import.step4.instruction")); - signedMessage = new MaterialTextField(Res.get("user.reputation.accountAge.import.step4.signedMessage")); + signedMessage = new MaterialTextArea(Res.get("user.reputation.accountAge.import.step4.signedMessage")); signedMessage.setEditable(true); signedMessage.getStyleClass().add("material-field"); VBox.setMargin(signedMessage, new Insets(10, 0, 15, 2)); From d91ddc692f4f6cd26e0c2f7db7fc63c7047e04af Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Sun, 24 Mar 2024 22:42:18 +0100 Subject: [PATCH 065/119] Add chevron drop down menu icons --- .../offerbook/BisqEasyOfferbookView.java | 4 ++-- .../desktop/src/main/resources/css/images.css | 12 ++++++++++++ .../images/icons/chevron-drop-menu-green.png | Bin 0 -> 180 bytes .../images/icons/chevron-drop-menu-green@2x.png | Bin 0 -> 238 bytes .../images/icons/chevron-drop-menu-grey.png | Bin 0 -> 191 bytes .../images/icons/chevron-drop-menu-grey@2x.png | Bin 0 -> 247 bytes .../images/icons/chevron-drop-menu-white.png | Bin 0 -> 167 bytes .../images/icons/chevron-drop-menu-white@2x.png | Bin 0 -> 201 bytes 8 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 apps/desktop/desktop/src/main/resources/images/icons/chevron-drop-menu-green.png create mode 100644 apps/desktop/desktop/src/main/resources/images/icons/chevron-drop-menu-green@2x.png create mode 100644 apps/desktop/desktop/src/main/resources/images/icons/chevron-drop-menu-grey.png create mode 100644 apps/desktop/desktop/src/main/resources/images/icons/chevron-drop-menu-grey@2x.png create mode 100644 apps/desktop/desktop/src/main/resources/images/icons/chevron-drop-menu-white.png create mode 100644 apps/desktop/desktop/src/main/resources/images/icons/chevron-drop-menu-white@2x.png diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java index d9d2473fdb..8e5696239a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java @@ -446,7 +446,7 @@ private void addChatBox() { } private DropdownMenu createAndGetOfferDirectionOrOwnerFilterMenu() { - DropdownMenu dropdownMenu = new DropdownMenu("arrow-down", "arrow-down", false); + DropdownMenu dropdownMenu = new DropdownMenu("chevron-drop-menu-grey", "chevron-drop-menu-white", false); dropdownMenu.setTooltip(Res.get("bisqEasy.offerbook.dropdownMenu.filterOffersByDirectionOrOwner.tooltip")); dropdownMenu.getStyleClass().add("dropdown-offers-filter-menu"); @@ -464,7 +464,7 @@ private DropdownMenu createAndGetOfferDirectionOrOwnerFilterMenu() { } private DropdownMenu createAndGetPeerReputationFilterMenu() { - DropdownMenu dropdownMenu = new DropdownMenu("arrow-down", "arrow-down", false); + DropdownMenu dropdownMenu = new DropdownMenu("chevron-drop-menu-grey", "chevron-drop-menu-white", false); dropdownMenu.setTooltip(Res.get("bisqEasy.offerbook.dropdownMenu.filterOffersByPeerReputation.tooltip")); dropdownMenu.getStyleClass().add("dropdown-offers-filter-menu"); diff --git a/apps/desktop/desktop/src/main/resources/css/images.css b/apps/desktop/desktop/src/main/resources/css/images.css index efc7f7ef25..8b651fd263 100644 --- a/apps/desktop/desktop/src/main/resources/css/images.css +++ b/apps/desktop/desktop/src/main/resources/css/images.css @@ -214,6 +214,18 @@ -fx-image: url("/images/icons/bisq-broadcast-white.png"); } +#chevron-drop-menu-green { + -fx-image: url("/images/icons/chevron-drop-menu-green.png"); +} + +#chevron-drop-menu-white { + -fx-image: url("/images/icons/chevron-drop-menu-white.png"); +} + +#chevron-drop-menu-grey { + -fx-image: url("/images/icons/chevron-drop-menu-grey.png"); +} + /* ------------------------------------------------------------------------------------ */ /* Navigation */ diff --git a/apps/desktop/desktop/src/main/resources/images/icons/chevron-drop-menu-green.png b/apps/desktop/desktop/src/main/resources/images/icons/chevron-drop-menu-green.png new file mode 100644 index 0000000000000000000000000000000000000000..5f3bd44f1562ac5d1d341772285796e6b744cb95 GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X!aQ9ZLn>}1B}mvPZ~fWFh-{( zJaZG%Q-e|yQz{EjrrH1%P4IMa45_%4lpw*nxM5X;pi9fz&jJiVuN!w1gkI?5ULwVG zC7)r*uSN@j75|xqC&`?6@Hc>UiPR4HRq_vmRIWFvh&J4qC?T#a`GaBAbLOsvOQd>E zJMcJnno7)XjNnyGF;HYKlH&TSKgwd*Mm8^^GjNuEr8iPI0&JsGJH=Y5u)% j=HtT|Gac(01QHl(yTTXEyyKS)bQ^=GtDnm{r-UW|h1pX+ literal 0 HcmV?d00001 diff --git a/apps/desktop/desktop/src/main/resources/images/icons/chevron-drop-menu-grey.png b/apps/desktop/desktop/src/main/resources/images/icons/chevron-drop-menu-grey.png new file mode 100644 index 0000000000000000000000000000000000000000..595830a62df4ac536d4f390d09683e36d0aa9e58 GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X5}1B}mvTSh(;$Zh{( zJaZG%Q-e|yQz{EjrrH1%&Gd9}45_%4lpw*nxM5X;po@u#2?xW_hAiE%jK(>Z!%BQQ1(>&FBMT$a!`VXHQd!>5UR? zLfHm(%p0Y=P9!+7Ju(rQe(;Dy$MpamHgTuPym8E&Qe9~VC#H&XNOCZpY2mRgcyNH9 roxOdk!!i~DcOjz!kqc}LED{V>4N-l1T>o@|j%4t3^>bP0l+XkKn)XU$ literal 0 HcmV?d00001 diff --git a/apps/desktop/desktop/src/main/resources/images/icons/chevron-drop-menu-white.png b/apps/desktop/desktop/src/main/resources/images/icons/chevron-drop-menu-white.png new file mode 100644 index 0000000000000000000000000000000000000000..60f7bda5c1906400a77cd9f58362bc0e7e004033 GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XJUv|;Ln>}1B}mvjxV}D~&E+JsB$GppT!(OhhEKyO?gv*J z77BPWxiXuz_Pmg4xKgLEkKu?yxz(nP8*2|dmz==+*>KG=O$H7FhLS6Rf7373f(&Kw MboFyt=akR{0AL?BNB{r; literal 0 HcmV?d00001 diff --git a/apps/desktop/desktop/src/main/resources/images/icons/chevron-drop-menu-white@2x.png b/apps/desktop/desktop/src/main/resources/images/icons/chevron-drop-menu-white@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..013422db119c851ee44d4e7574d8039efbf17210 GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^q9Dw{1|(OCFP#RYBuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrH1%WqZ0fhE&{2N|0b(-0+{$jeLo__Q~I!ag&kwfL5C>^T_ioa4ys6c^c+l)>^Ps$#d9RF wfaxe(lHm{gP#zcK3cIU3GOQ}h4Ghc-oORc~a=3Xv1zOAC>FVdQ&MBb@08BqZ@c;k- literal 0 HcmV?d00001 From 0b86b4cef461ecdf275deab0d2fdead617f98b23 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Sun, 24 Mar 2024 22:44:05 +0100 Subject: [PATCH 066/119] Correct ellipsis icons --- .../resources/images/icons/ellipsis-h-green.png | Bin 354 -> 183 bytes .../resources/images/icons/ellipsis-h-grey.png | Bin 350 -> 176 bytes .../resources/images/icons/ellipsis-h-white.png | Bin 317 -> 176 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/desktop/desktop/src/main/resources/images/icons/ellipsis-h-green.png b/apps/desktop/desktop/src/main/resources/images/icons/ellipsis-h-green.png index 9ed688b2b16ed097c4213946dc380be5f52bfc59..bb7fb94db1be5ec24d6958105ee128e19879db21 100644 GIT binary patch delta 118 zcmV-+0Ez$N0=EH>NN)gHNkl+400|^0GUmW9OGzS9yM?f7zhwx Y0P2xBisZ$D?f?J)07*qoM6N<$f|$QAsQ>@~ delta 291 zcmV+;0o?w#0pbFXNPhtYNklE4~e(uaUhJONoba_O$gx_3$f9lWAzi_$#|Nb)wDDvsD@v#W23TQy|ffDw^ zkN5OK*Sh_1UTX4x^6BnHATbzr3w1WIn{MzUc&p2Q?PR4z$bYV36=gE;UTpKjeT6w( zAK1&Eetr~v_v0-)Gb0nor8>x(I0QMwI0f0+|Ns5Rz%R#xBFE0pCdMJe!47gMOdr_G zN&>290@NZF00k97G@~aXYf=?dKlS^c)Bu<0?IUvCXB!!JgY?9lQ7bV{L R2bBN-002ovPDHLkV1lCIEt>!U delta 287 zcmV+)0pR|y0p0?TNPhtUNkl(@HSUf|{B6$4`SfB*h5h>3}z$N^mjmxJj8dl~55 zGeDOu0J>Dc&dzQkvLOYZ0SPuBDQrYylO#tQFGIb;fQ3!69OGzS9yM?f7zhwx0Q?gdC1=qX Q%>V!Z07*qoM6N<$f>kdlGXMYp delta 252 zcmVw{q?GkKFYFd>XF;Yx_Y#kd>(L&FCTu#W+pN(iCw9nVur1E-Ll653e(=6rxew5qDy zbzQqG%QTMTqoybdt?N3s0US3Tx&QLr@CO=x1Q-AvD{%8dneSl$0000 Date: Sun, 24 Mar 2024 22:46:37 +0100 Subject: [PATCH 067/119] Improve help icon --- .../resources/images/icons/icon-help-green.png | Bin 356 -> 353 bytes .../images/icons/icon-help-green@2x.png | Bin 652 -> 652 bytes .../resources/images/icons/icon-help-grey.png | Bin 343 -> 340 bytes .../images/icons/icon-help-grey@2x.png | Bin 661 -> 643 bytes .../resources/images/icons/icon-help-white.png | Bin 332 -> 320 bytes .../images/icons/icon-help-white@2x.png | Bin 553 -> 540 bytes 6 files changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/desktop/desktop/src/main/resources/images/icons/icon-help-green.png b/apps/desktop/desktop/src/main/resources/images/icons/icon-help-green.png index 2a84ceea7e51052c87c2e6076471bd4cb1cdf601..3baab1e390008ba40e7a2242dd695428e140d3c8 100644 GIT binary patch delta 289 zcmV++0p9-P0^tIXNq+-LL_t(|+N@Kt4Z=VS42S_TLfC+803BTd1#QR#j6j$GF#*t0 zAkxthHeiH|@FQ@Bb7Z|jG0jc8 z4yZXcx8{^FCZzSXb|W!zItdvyN9NIXNOs0&zt7}!5)xYg#D<-z6d|8X0f}$@Hwf9D zQc|y*$e%*XQTgTqQgp1XEl_jLzCAtdY6Usp0^BeM%~c?A<1L%yINeJ71|^q7M`oK*l!0yc4-QJ?q$e0Wzch}V#j{xwPYJu{yz2CkXKsd+a z1h_x})GYZx2y{RMqeg2SOI&fK8Ph4Dm@9=t?xz7)&OyvT*nd;l5Git*)PzIs+kj_w zNl@JG8Q+OO-hl;oA{>{Reg^^1tV2Wxn!D^lXVeR7)RYO&G|)|=xMEWi!=To^aeiF$ zk>*R7wnGHRxMzQ$?y(n*mG`OX4N5l7j*aex6(Kb*15Q{cS7i|&ggI=&>GV{ZJ&1tV qGRX;39FzN>Q5_!rGmVa?00RJ~?`#pB!axu;Cmw12LMp2X)B(b8pRxMxh1 zi_cr4!exz)NB9m}F_S4VIRv^a2KW@8G~mnz;?BXN62i3^=N2I;;z5IL=uFLf0GiR6 zIoy|k=kTlr;PY3PaF}^x2}T7gt&H;wBEo*@1OU_Tq1BcS4?X(|F%cn77*HmyfqOf+ zL*MC3S9A?6CVwK5rxw@b*gt@g)<~aw5Lq#{+$-?pbdFpAMjY*qfE(bsmRjI_Erc8` z@LCx%jsd{8Y{sP{IGK6v*>f%C8AH}d7I4ZaSDw@N%#*ewQ3-8#8#<=|G-a1{EgH0{ z<)UW9oC%)ejcKj!C7sMDSjtswe9H!)4TFs#Jk)ykK7RncKH7;%sKrjw*eu0+M6)hL#WhDS;$5MV9*B3}6$4z&aRPqqJ+idpRJ3;$G7)Wr zR@Q+e&_3^S7C33|zKN)SX|-=>#AtxI?vjzL_&r1EO^oF?^JDVUzh?jIOMn3YI!XnS T)P~Rf00000NkvXXu0mjfI#~@8 delta 581 zcmV-L0=oT-1&jrdQh&j5!axi(ObT!ZF&)rBoL`0uf>9i~9q&uSMua8#K1v6wO>+z+>UDH7q^tdUSFkQJh2oUVqO?dhGLpWTu4~>3Ja; zu+M9fOp8V<0KS1&&d7+2R7@wuUa+II5pf_imw>#Rw}kI~Bs2mm=D7{ygw0v+rk+h0 zA@3umJ3;#PPSD(=T+mi>u7UU9SQq{Cp&Xb~7P??#>tmaxcF%v1ZGG4)i~b#tzq)fDvIZs!1Ou>)Mo zP>RY(#9W06;uzlv2oGmUgUMM=<=)|5`^wbWOn|i?H=eK=U!B8?NuX?}ye0c0M0y61 zOdZL#C&b%^XM^k+S)c`PWjfpEoMa04x`_=R0n=c;1b<)DTz4BH5ZClRW#D-Oa63MI z1PnXXnvSBTUROq$6L32>=4Dc6)H(NIGZt6}t=P3%+laDDUcJcn9LID;wmaG$SmHTA ze?RI~ooK<@dul{l(etCzU$%KLR+PR-uoJ^0&<2XU_iY8*#Lws+#Se(4j7r* z0Syp<7QAPv)_Ca(jCREgwlFi9aNYrQ!Z>PkHgrwAelZ a3;+g;Zx(O*p$(J(0000zM delta 279 zcmV+y0qFkJ0@nhNNq_lCL_t(|+N_h|4T3Nfge!3XM!*f+K%79GK%Kxi0d)f71nLGb z0d628I07ShjbE`gM1SB*F2L*EwY?T&2G*#rX{}xAu@Ev^51~@O{AwV=RFAWe3RmIA zP%f;6R@jQycuf+CYi63zE`Xs|IS$-U17<$iS8WhBprCIZ2Y>F{fIX)W49mUfbMN5Z zg&AB6cNwl=!Jd6N(E&k925m5YMPtS^ve3YXgt#WuePD3&*(5)X`H1;kW{*BX5}b41 zGoFz}6ZLa6ok2x#ajkq7QLWK*r{Hc_8=Qi#I|whEgp=^^=N~TNF!RY0j0#pd8v7YUfc44=0H(h}t22GCJ?jcF5g<+&&`MeZ=Pq!E zKC_Unm>XJbMSmpkEUwA0-h+|WNbh?SS+TL4%kkuN4qO0Ij&?`DE%9um7I<%j5Yw%_ zuvTgX0N=71502nu=B;PVHJE1vSvOh0DWg1jzVf7PDsDpCo(`4@A*(Z85Q?0gQXBZr zh&U5G+dI=*^Lr|pabYRfW#bz*08<#84B=y~_v!->j(^lX7ICwlScX4s0Fz zv?I^Nrj;cCW)jLY8L0auXspF<(%8I;uZU)S5#^6;y@+3po3y9*g{vAiC>$AWDt+Zw z6s5r!KzYuD3}+Tp!awzZQe1!{qiJ!KclLed1b`f7pC-0Gz!EdAt67%gM3i?mrXxBD zt;zu)!9IX+?gehjyRRe4VOs6yJsn)4ue+xu%YRQ(`V?*X)BKqH^sm|f_!3|MB$Wmp T#-%sK00000NkvXXu0mjf=I;Td delta 599 zcmV-d0;v6i1(gMmNq;v|W7%5mzWr!dPLjVqI&C);MbW2+B1*j_ zL!@bab94YC)G8Yj4T-WH>^0GvsHA=7U;reEvN^g3$VO~#JpBYrCRH!?II0&@31GX zX#PQ;UQl0GaDP1_E3n4oNxY5TF+{zfM_)41g_n3=<*2GVl!MgPrHV zt~|MJEd}q20ERBxQ2}gr3M(OWcrr3?t>zPiI=%w%EL;Q%%#2A&T(nv`rcP$Yy0+t7 zaGR0NIa^Ugj_n*vvg9E?hAHdX_#sFm=y#QAtQmQv1b=V*aCQ&rWC7(>8yOM+(_+7` zeARH@Weh;t(0T>RXDyW5@h$-{(ACTtD zg(yGf)r;gUxlPwJ?}c(;&Ce0m^O#jlyv4Fco(>T}3GrF(5NeCkt0ivHv9W~Mm$7bW zpwud%8&Kl75qd}nsDjgCqIH1pa22%A5Qs%!oA>0Nq^!=L_t(|+N4v_4T3Nbt;F!%5f}kCU<2F$-2^9a1j7W%1n2~~ z0c-%@eEFJO$xFGYNiTWn-D~gY>m9~K+D;>vWjSIgflR0oXwx)(d@X>)9Lo)`Km$Bw zs{krs2F@t-*1nnp6HpUZI8a>6nOrN-`(&(E;)#$^k$$5Y772j+(dMgkd1#0n?@=|C#b z4zvU40M1dLTAMq48mSB=Ho=|NlP)c9;8Co_D1oaHkWMW7DeoQ4zcL7_SjAE zRklsEwfkB`Ck5rv=OTLB=s32Hwrrz)NGw(%wA|nX_wpeHKjq+`DR{gE7y!sn)UDa? S0JQ)B002ovP6b4+LSTX&qIre@ diff --git a/apps/desktop/desktop/src/main/resources/images/icons/icon-help-white@2x.png b/apps/desktop/desktop/src/main/resources/images/icons/icon-help-white@2x.png index f9265a4d5614d1745be6d0d113f20766fdb1d915..8a1adec0c4de489e21c1f9283e95e46e0fbda3a4 100644 GIT binary patch delta 477 zcmV<30V4jX1e^qrNq@dcL_t(|+U!@|O~XJ8E)~jiHXtKp1HuSQP(}z7kWN6Epv?ps zA=#kWAngRnb6&tkKFOH7i!XtK1SeUS>iqNBXP@2G>aVY>P=RqAQ`j<~SR4&NO%z4# zmjeK!Xe*{cru^|ydwN|@D$t$t?}GvoWk6RFeCK{XaYp8 z34QNEEY5L%CgOlHi(JiQVjlpGHt|HK0Ued{!@sy0;Y2JTd5}G$$yDDK^8@(GJfLQ6hY0J3o1KaS~24G-aO|xQF zW{pxNl~KNCheiozbgJ+OYkM0w$oR_mooTX^79^}`(^lQ=r`XYyI^T>i^%VP>O+BHs zP$>;*Mw)gk<^g91oYjs)1`?&7@PWiJlGPU-$u2pR{`gq_`~R4H_g}OB^(Mdo?VZv< TL%ds_00000NkvXXu0mjfaj)L1 delta 490 zcmVlKZ%`gHh-P2VmHk5oWV~HoC?td+$TwL z_;v>X)Pj}3cfe*r{RI39yaQ*ZvmF3%3;ZY|wuWV-nM0=rCx3RUYGb1YZh_w!H&_C1 zc~FP%X)%NP4B&9BOee>)V5zqmp_GHnmZ@QKo2^?-Nys>lyb`i4@F=1r zeiFJB&x97d41gB`utU~5QEKWw`aFiUBgzI$Gp!q3BOoL8k&|QSJV<|ksdd7X=z8?hECkIw_O9%Kq#)iv*DPq3_e^Ktc2|17|qt6+59RY6X z&E>$vrdlj%CtfDA%pK?{1EGoMBF&INo2kIkH==XN*JFs(tvE3MGlibI74u=#Ef}qQ zz2Bj$8%9gx*kd&4Y}7c`JV-omgF*q`Ms+wb-#gctK)uvcbDp7LG4&VqDE-Z6`Q?9@ g{Pd^UKYa)=07p3nb-JdM6951J07*qoM6N<$f~_~`82|tP From 903a984b36476e903b09a2c68dd7d4a34892b9ef Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Sun, 24 Mar 2024 23:21:29 +0100 Subject: [PATCH 068/119] Add mini close icons --- .../offerbook/BisqEasyOfferbookView.java | 19 +++++------------- .../src/main/resources/css/bisq_easy.css | 2 +- .../desktop/src/main/resources/css/images.css | 16 +++++++++++++++ .../images/icons/close-mini-black.png | Bin 0 -> 150 bytes .../images/icons/close-mini-black@2x.png | Bin 0 -> 183 bytes .../images/icons/close-mini-green.png | Bin 0 -> 198 bytes .../images/icons/close-mini-green@2x.png | Bin 0 -> 241 bytes .../images/icons/close-mini-grey.png | Bin 0 -> 196 bytes .../images/icons/close-mini-grey@2x.png | Bin 0 -> 275 bytes .../images/icons/close-mini-white.png | Bin 0 -> 151 bytes .../images/icons/close-mini-white@2x.png | Bin 0 -> 184 bytes 11 files changed, 22 insertions(+), 15 deletions(-) create mode 100644 apps/desktop/desktop/src/main/resources/images/icons/close-mini-black.png create mode 100644 apps/desktop/desktop/src/main/resources/images/icons/close-mini-black@2x.png create mode 100644 apps/desktop/desktop/src/main/resources/images/icons/close-mini-green.png create mode 100644 apps/desktop/desktop/src/main/resources/images/icons/close-mini-green@2x.png create mode 100644 apps/desktop/desktop/src/main/resources/images/icons/close-mini-grey.png create mode 100644 apps/desktop/desktop/src/main/resources/images/icons/close-mini-grey@2x.png create mode 100644 apps/desktop/desktop/src/main/resources/images/icons/close-mini-white.png create mode 100644 apps/desktop/desktop/src/main/resources/images/icons/close-mini-white@2x.png diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java index 8e5696239a..993c9301e0 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java @@ -289,23 +289,14 @@ private void addMarketSelectionList() { subheader.setAlignment(Pos.CENTER); subheader.getStyleClass().add("market-selection-subheader"); - // TODO: Introduce new icons with proper scale - withOffersRemoveFilterDefaultIcon = ImageUtil.getImageViewById("close"); - withOffersRemoveFilterDefaultIcon.setScaleX(0.4); - withOffersRemoveFilterDefaultIcon.setScaleY(0.4); - withOffersRemoveFilterActiveIcon = ImageUtil.getImageViewById("close-white"); - withOffersRemoveFilterActiveIcon.setScaleX(0.4); - withOffersRemoveFilterActiveIcon.setScaleY(0.4); + withOffersRemoveFilterDefaultIcon = ImageUtil.getImageViewById("close-mini-grey"); + withOffersRemoveFilterActiveIcon = ImageUtil.getImageViewById("close-mini-white"); removeWithOffersFilter = createAndGetRemoveFilterLabel(withOffersRemoveFilterDefaultIcon); withOffersDisplayHint = createAndGetDisplayHintHBox( Res.get("bisqEasy.offerbook.dropdownMenu.sortAndFilterMarkets.withOffers"), removeWithOffersFilter); - favouritesRemoveFilterDefaultIcon = ImageUtil.getImageViewById("close"); - favouritesRemoveFilterDefaultIcon.setScaleX(0.4); - favouritesRemoveFilterDefaultIcon.setScaleY(0.4); - favouritesRemoveFilterActiveIcon = ImageUtil.getImageViewById("close-white"); - favouritesRemoveFilterActiveIcon.setScaleX(0.4); - favouritesRemoveFilterActiveIcon.setScaleY(0.4); + favouritesRemoveFilterDefaultIcon = ImageUtil.getImageViewById("close-mini-grey"); + favouritesRemoveFilterActiveIcon = ImageUtil.getImageViewById("close-mini-white"); removeFavouritesFilter = createAndGetRemoveFilterLabel(favouritesRemoveFilterDefaultIcon); onlyFavouritesDisplayHint = createAndGetDisplayHintHBox( Res.get("bisqEasy.offerbook.dropdownMenu.sortAndFilterMarkets.favourites"), removeFavouritesFilter); @@ -348,7 +339,7 @@ private Label createAndGetRemoveFilterLabel(ImageView defaultCloseIcon) { private HBox createAndGetDisplayHintHBox(String labelText, Label removeFilter) { Label label = new Label(labelText); label.getStyleClass().add("small-text"); - HBox displayHintHBox = new HBox(label, removeFilter); + HBox displayHintHBox = new HBox(5, label, removeFilter); displayHintHBox.setAlignment(Pos.CENTER); displayHintHBox.getStyleClass().add("filter-display-hint"); return displayHintHBox; diff --git a/apps/desktop/desktop/src/main/resources/css/bisq_easy.css b/apps/desktop/desktop/src/main/resources/css/bisq_easy.css index 6f4577ea51..dbcd92d4de 100644 --- a/apps/desktop/desktop/src/main/resources/css/bisq_easy.css +++ b/apps/desktop/desktop/src/main/resources/css/bisq_easy.css @@ -236,7 +236,7 @@ -fx-min-height: 20; -fx-pref-height: 20; -fx-max-height: 20; - -fx-padding: 0 0 0 5; + -fx-padding: 0 5 0 5; } .market-selection-show-applied-filters .filter-display-hint:hover { diff --git a/apps/desktop/desktop/src/main/resources/css/images.css b/apps/desktop/desktop/src/main/resources/css/images.css index 8b651fd263..b573039eeb 100644 --- a/apps/desktop/desktop/src/main/resources/css/images.css +++ b/apps/desktop/desktop/src/main/resources/css/images.css @@ -226,6 +226,22 @@ -fx-image: url("/images/icons/chevron-drop-menu-grey.png"); } +#close-mini-black { + -fx-image: url("/images/icons/close-mini-black.png"); +} + +#close-mini-white { + -fx-image: url("/images/icons/close-mini-white.png"); +} + +#close-mini-grey { + -fx-image: url("/images/icons/close-mini-grey.png"); +} + +#close-mini-green { + -fx-image: url("/images/icons/close-mini-green.png"); +} + /* ------------------------------------------------------------------------------------ */ /* Navigation */ diff --git a/apps/desktop/desktop/src/main/resources/images/icons/close-mini-black.png b/apps/desktop/desktop/src/main/resources/images/icons/close-mini-black.png new file mode 100644 index 0000000000000000000000000000000000000000..1f66a3769b83526b9a2b63502af628c5e724f75c GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{Vk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XEIeHtLn>}1B`An(*tAKEVI#w=84MmWu?coQidOQoomkg! vO%Q1k`NMv3hsXo-P7QOxr`Apk3I`ZWg}BuvB7B>GW-xfV`njxgN@xNA5`8IP literal 0 HcmV?d00001 diff --git a/apps/desktop/desktop/src/main/resources/images/icons/close-mini-black@2x.png b/apps/desktop/desktop/src/main/resources/images/icons/close-mini-black@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1a3bf034cb485dbefd4d5c9872b37c78c1f23f28 GIT binary patch literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1Gk)AG&Ar-fh5+t@XOr09a(825xAkT2A+53^bfVx1U2UAEc zgCZ-Zi^L>{HMyKA(oO!%C)*M(du&sT;8D)t5VpM}`ZR8_S31|%O$WD@{Vk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XGCW-zLn>}1B`Aosto^LPki=+W$T-2G<1CA4G{cd<2CV_R z}1B}i;h$eG*DAizFhfgQsV_G9TzlZqK0eRVj= zqQt|vGLrvlbJm4b3uL7mLX-r~A5WJE$+Pp&4dItC5DM7Q5Mk-2loRW4LSPQt!t55V zhbAoR9W&Yt9~iUPUejRO@>MF7X|~oy{}W*~SrQXJ^O@Xy+$(Exm??3kwB1|%O$WD@{Vk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X(mY)pLn>}1B`AoQn3#w#Y-E@sRqGJQ61sw6$vftaN)!GvsGVy`6FR}-D$x7JP0eIA*FrTB rrQL#QK}?&Z7nCT5%&}k)NMLxzqj~DE+ttlL8yP%Z{an^LB{Ts5v?n|^ literal 0 HcmV?d00001 diff --git a/apps/desktop/desktop/src/main/resources/images/icons/close-mini-grey@2x.png b/apps/desktop/desktop/src/main/resources/images/icons/close-mini-grey@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d51b0fbdbf2df790872473945597c41eefb8a512 GIT binary patch literal 275 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1GTRmMILn>}1B}i;puyCOxLkF`*fIPz`=Eug0CoLI1RxL;{%P%<}xxea!fNnJDNA3V;+DU6$B zjCb|!;5nSm!C@f8!M?$5g@B*)2dS1c31&Oz3(37o2Yi-XHxugeO;cJ_!MO0okt~6A zHxyGOj5oe)nUHwqL?ye2uDXbIMq4b)Q;shY)(l4)fABbN5?P`ququ55Qv(AtL(i7} VlSW7FOn@F>@O1TaS?83{1OWPWUR?kH literal 0 HcmV?d00001 diff --git a/apps/desktop/desktop/src/main/resources/images/icons/close-mini-white.png b/apps/desktop/desktop/src/main/resources/images/icons/close-mini-white.png new file mode 100644 index 0000000000000000000000000000000000000000..27d34eddb736c30d6eed3aa4beb69c6ae7a0873c GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{Vk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XEInNuLn>}1B`Apf`So=*LkH`HBP!SZu7SF9ZPFsDf eGZ-8i7#Ws_|GZoAXT>U@EexKnelF{r5}E++Jv(0j literal 0 HcmV?d00001 From 437db3db466af197124a55047ab2cafc5bfd5bb1 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 25 Mar 2024 12:11:03 +0700 Subject: [PATCH 069/119] Add LogMaskConverter and replace home directory name with to avoid leaking private information in logs --- .../java/bisq/common/logging/LogMaskConverter.java | 11 +++++++++++ .../src/main/java/bisq/common/logging/LogSetup.java | 2 +- common/src/main/resources/logback.xml | 3 ++- 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 common/src/main/java/bisq/common/logging/LogMaskConverter.java diff --git a/common/src/main/java/bisq/common/logging/LogMaskConverter.java b/common/src/main/java/bisq/common/logging/LogMaskConverter.java new file mode 100644 index 0000000000..b88e465902 --- /dev/null +++ b/common/src/main/java/bisq/common/logging/LogMaskConverter.java @@ -0,0 +1,11 @@ +package bisq.common.logging; + +import bisq.common.util.OsUtils; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.pattern.CompositeConverter; + +public class LogMaskConverter extends CompositeConverter { + public String transform(ILoggingEvent event, String message) { + return message.replace(OsUtils.getHomeDirectory(), ""); + } +} diff --git a/common/src/main/java/bisq/common/logging/LogSetup.java b/common/src/main/java/bisq/common/logging/LogSetup.java index 853d8c3d3b..5eb738b7ae 100644 --- a/common/src/main/java/bisq/common/logging/LogSetup.java +++ b/common/src/main/java/bisq/common/logging/LogSetup.java @@ -63,7 +63,7 @@ public static void setup(String fileName) { PatternLayoutEncoder encoder = new PatternLayoutEncoder(); encoder.setContext(loggerContext); - encoder.setPattern("%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{15}: %msg %xEx%n"); + encoder.setPattern("%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{15}: %mask(%msg) %xEx%n"); encoder.setCharset(Charsets.UTF_8); encoder.start(); diff --git a/common/src/main/resources/logback.xml b/common/src/main/resources/logback.xml index 647cadf7cb..5ee042587e 100644 --- a/common/src/main/resources/logback.xml +++ b/common/src/main/resources/logback.xml @@ -1,10 +1,11 @@ + - %white(%d{HH:mm:ss.SSS}) %highlight(%-5level) %yellow([%thread]) %boldYellow(%logger{10}:) %highlight(%msg %xEx%n) + %white(%d{HH:mm:ss.SSS}) %highlight(%-5level) %yellow([%thread]) %boldYellow(%logger{10}:) %highlight(%mask(%msg) %xEx%n) From 62d616c829f81cd18937ad4f1b9fdb0aad0f9f50 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 25 Mar 2024 14:15:10 +0700 Subject: [PATCH 070/119] Change behaviour of selectAuthorizationTokenType. Select the peersAuthorizationTokenTypes first entry in case not match is found. --- .../node/authorization/AuthorizationService.java | 5 ++++- .../authorization/AuthorizationServiceTest.java | 13 ++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/network/network/src/main/java/bisq/network/p2p/node/authorization/AuthorizationService.java b/network/network/src/main/java/bisq/network/p2p/node/authorization/AuthorizationService.java index 2b7bf68b96..45d52a04f5 100644 --- a/network/network/src/main/java/bisq/network/p2p/node/authorization/AuthorizationService.java +++ b/network/network/src/main/java/bisq/network/p2p/node/authorization/AuthorizationService.java @@ -126,11 +126,14 @@ private AuthorizationTokenType selectAuthorizationTokenType(List peersF static AuthorizationTokenType selectAuthorizationTokenType(List myPreferredAuthorizationTokenTypes, List peersFeatures) { checkArgument(!myPreferredAuthorizationTokenTypes.isEmpty(), "myPreferredAuthorizationTokenTypes must not be empty"); + if (peersFeatures.isEmpty()) { + return myPreferredAuthorizationTokenTypes.get(0); + } List peersAuthorizationTokenTypes = toAuthorizationTypes(peersFeatures); return myPreferredAuthorizationTokenTypes.stream() .filter(peersAuthorizationTokenTypes::contains) .findFirst() - .orElse(myPreferredAuthorizationTokenTypes.get(0)); + .orElse(peersAuthorizationTokenTypes.get(0)); } private static List toAuthorizationTypes(List features) { diff --git a/network/network/src/test/java/bisq/network/p2p/node/authorization/AuthorizationServiceTest.java b/network/network/src/test/java/bisq/network/p2p/node/authorization/AuthorizationServiceTest.java index 2f9457c83f..4a7efc0300 100644 --- a/network/network/src/test/java/bisq/network/p2p/node/authorization/AuthorizationServiceTest.java +++ b/network/network/src/test/java/bisq/network/p2p/node/authorization/AuthorizationServiceTest.java @@ -37,16 +37,10 @@ void testSelectAuthorizationTokenType() { result = AuthorizationService.selectAuthorizationTokenType(myPreferredAuthorizationTokenTypes, peersFeatures); assertEquals(AuthorizationTokenType.HASH_CASH, result); - // If not match we use first myPreferredAuthorizationTokenTypes item as default + // If not match we use first peersFeatures item as default peersFeatures = List.of(Feature.AUTHORIZATION_HASH_CASH); myPreferredAuthorizationTokenTypes = List.of(AuthorizationTokenType.EQUI_HASH); result = AuthorizationService.selectAuthorizationTokenType(myPreferredAuthorizationTokenTypes, peersFeatures); - assertEquals(AuthorizationTokenType.EQUI_HASH, result); - - // If not match we use first myPreferredAuthorizationTokenTypes item as default - peersFeatures = List.of(Feature.AUTHORIZATION_EQUI_HASH); - myPreferredAuthorizationTokenTypes = List.of(AuthorizationTokenType.HASH_CASH); - result = AuthorizationService.selectAuthorizationTokenType(myPreferredAuthorizationTokenTypes, peersFeatures); assertEquals(AuthorizationTokenType.HASH_CASH, result); // Multiple myPreferredAuthorizationTokenTypes @@ -92,6 +86,11 @@ void testSelectAuthorizationTokenType() { result = AuthorizationService.selectAuthorizationTokenType(myPreferredAuthorizationTokenTypes, peersFeatures); assertEquals(AuthorizationTokenType.EQUI_HASH, result); + peersFeatures = List.of(Feature.AUTHORIZATION_EQUI_HASH); + myPreferredAuthorizationTokenTypes = List.of(AuthorizationTokenType.HASH_CASH); + result = AuthorizationService.selectAuthorizationTokenType(myPreferredAuthorizationTokenTypes, peersFeatures); + assertEquals(AuthorizationTokenType.EQUI_HASH, result); + // Multiple myPreferredAuthorizationTokenTypes and peersFeatures peersFeatures = List.of(Feature.AUTHORIZATION_HASH_CASH, Feature.AUTHORIZATION_EQUI_HASH); From bcb0ac1eec59489b7022296f08e1af342223defa Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 25 Mar 2024 15:05:29 +0700 Subject: [PATCH 071/119] Add fromProtoEnumStream method to ProtobufUtils. Skip not supported Feature proto enum values to support future updates fo the feature list. --- .../java/bisq/common/util/ProtobufUtils.java | 42 +++++++++++++++---- .../bisq/network/p2p/node/Capability.java | 5 +-- .../java/bisq/network/p2p/node/Feature.java | 2 + 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/common/src/main/java/bisq/common/util/ProtobufUtils.java b/common/src/main/java/bisq/common/util/ProtobufUtils.java index 9b61f22a0e..17c65de4dd 100644 --- a/common/src/main/java/bisq/common/util/ProtobufUtils.java +++ b/common/src/main/java/bisq/common/util/ProtobufUtils.java @@ -22,25 +22,31 @@ import com.google.protobuf.Any; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.Nullable; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkArgument; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; @Slf4j public class ProtobufUtils { @Nullable public static > E enumFromProto(Class enumType, String name) { String info = "Enum type= " + enumType.getSimpleName() + "; name=" + name; - checkNotNull(name, "Enum name must not be null. "+info); - checkArgument(!name.endsWith("_UNSPECIFIED"), "Unspecified enum. "+info); + checkNotNull(name, "Enum name must not be null. " + info); + checkArgument(!name.endsWith("_UNSPECIFIED"), "Unspecified enum. " + info); //Remove prefix from enum name. Since enum is based on the enum's class name, we use that to extract the prefix - String enumName = name.replace(ProtoEnum.getProtobufEnumPrefix(enumType),""); + String enumName = name.replace(ProtoEnum.getProtobufEnumPrefix(enumType), ""); E result = Enums.getIfPresent(enumType, enumName).orNull(); - checkNotNull(result, "Enum could not be resolved. "+info); + checkNotNull(result, "Enum could not be resolved. " + info); return result; } @@ -55,7 +61,7 @@ public static > E enumFromProto(Class enumType, String name return fallBack; } //Remove prefix from enum name. Since enum is based on the enum's class name, we use that to extract the prefix - String enumName = name.replace(ProtoEnum.getProtobufEnumPrefix(enumType),""); + String enumName = name.replace(ProtoEnum.getProtobufEnumPrefix(enumType), ""); E result = Enums.getIfPresent(enumType, enumName).orNull(); if (result == null) { log.error("Enum could not be resolved. We use the fallback value instead. {}", info); @@ -81,4 +87,26 @@ public static Any toAny(byte[] bytes) throws IOException { return Any.parseDelimitedFrom(inputStream); } } + + // Convert list of proto enums to a stream of Enums. If the enumFromProto fails we skip the enum. + public static , P> Stream fromProtoEnumStream(Class enumType, List

proto) { + return proto.stream() + .map(enumProto -> { + try { + return enumFromProto(enumType, ((Enum) enumProto).name()); + } catch (Exception e) { + log.warn("Could not resolve enum for proto {}.", enumProto, e); + return null; + } + }) + .filter(Objects::nonNull); + } + + public static , P> List fromProtoEnumList(Class enumType, List

proto) { + return fromProtoEnumStream(enumType, proto).collect(Collectors.toList()); + } + + public static , P> Set fromProtoEnumSet(Class enumType, List

proto) { + return fromProtoEnumStream(enumType, proto).collect(Collectors.toSet()); + } } diff --git a/network/network/src/main/java/bisq/network/p2p/node/Capability.java b/network/network/src/main/java/bisq/network/p2p/node/Capability.java index 898acd5177..57305d5128 100644 --- a/network/network/src/main/java/bisq/network/p2p/node/Capability.java +++ b/network/network/src/main/java/bisq/network/p2p/node/Capability.java @@ -74,11 +74,8 @@ public static Capability fromProto(bisq.network.protobuf.Capability proto) { List supportedTransportTypes = proto.getSupportedTransportTypesList().stream() .map(e -> ProtobufUtils.enumFromProto(TransportType.class, e)) .collect(Collectors.toList()); - List features = proto.getFeaturesList().stream() - .map(Feature::fromProto) - .collect(Collectors.toList()); return new Capability(Address.fromProto(proto.getAddress()), supportedTransportTypes, - features); + ProtobufUtils.fromProtoEnumList(Feature.class, proto.getFeaturesList())); } } diff --git a/network/network/src/main/java/bisq/network/p2p/node/Feature.java b/network/network/src/main/java/bisq/network/p2p/node/Feature.java index 99464b03b1..a5da600946 100644 --- a/network/network/src/main/java/bisq/network/p2p/node/Feature.java +++ b/network/network/src/main/java/bisq/network/p2p/node/Feature.java @@ -18,6 +18,8 @@ public bisq.network.protobuf.Feature toProto() { return bisq.network.protobuf.Feature.valueOf(getProtobufEnumPrefix() + name()); } + // Not used. Feature is used as list in Capability thus we use ProtobufUtils.fromProtoEnumList. + // Still keep fromProto for following convention and potential future usage. public static Feature fromProto(bisq.network.protobuf.Feature proto) { return ProtobufUtils.enumFromProto(Feature.class, proto.name()); } From c4757c08b8368d64f0b2b8c42d30f44f3ba41232 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Mon, 25 Mar 2024 20:05:06 +0100 Subject: [PATCH 072/119] add service --- .../alert/AlertNotificationsService.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java new file mode 100644 index 0000000000..c687af0107 --- /dev/null +++ b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java @@ -0,0 +1,34 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.bonded_roles.security_manager.alert; + +import bisq.common.application.Service; + +import java.util.concurrent.CompletableFuture; + +public class AlertNotificationsService implements Service { + @Override + public CompletableFuture initialize() { + return null; + } + + @Override + public CompletableFuture shutdown() { + return null; + } +} From 6c81d1842b6c1c7db878b5f786d448df82af8ea0 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Mon, 25 Mar 2024 20:06:14 +0100 Subject: [PATCH 073/119] Remove unused property --- .../src/main/java/bisq/desktop/main/alert/AlertBannerModel.java | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java index f394904b87..bc385dd6bc 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java @@ -36,7 +36,6 @@ public class AlertBannerModel implements Model { private final ObservableList observableList = FXCollections.observableArrayList(); private final FilteredList filteredList = new FilteredList<>(observableList); private final SortedList sortedList = new SortedList<>(filteredList); - private final Set displayedAlerts = new HashSet<>(); @Setter private AuthorizedAlertData displayedAuthorizedAlertData; From a2f365d8047ed343b03ea60d4e01497a707326de Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Mon, 25 Mar 2024 21:39:21 +0100 Subject: [PATCH 074/119] Implement alert notifications service --- .../DesktopApplicationService.java | 10 ++- .../java/bisq/desktop/ServiceProvider.java | 6 +- bonded-roles/build.gradle.kts | 1 + .../alert/AlertNotificationsService.java | 70 ++++++++++++++++++- 4 files changed, 83 insertions(+), 4 deletions(-) diff --git a/apps/desktop/desktop-app/src/main/java/bisq/desktop_app/DesktopApplicationService.java b/apps/desktop/desktop-app/src/main/java/bisq/desktop_app/DesktopApplicationService.java index d524997a47..25cd5409a2 100644 --- a/apps/desktop/desktop-app/src/main/java/bisq/desktop_app/DesktopApplicationService.java +++ b/apps/desktop/desktop-app/src/main/java/bisq/desktop_app/DesktopApplicationService.java @@ -22,6 +22,7 @@ import bisq.application.ShutDownHandler; import bisq.bisq_easy.BisqEasyService; import bisq.bonded_roles.BondedRolesService; +import bisq.bonded_roles.security_manager.alert.AlertNotificationsService; import bisq.chat.ChatService; import bisq.common.application.Service; import bisq.common.observable.Observable; @@ -47,6 +48,7 @@ import bisq.wallets.electrum.ElectrumWalletService; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.units.qual.A; import java.util.Optional; import java.util.concurrent.CompletableFuture; @@ -92,6 +94,7 @@ public class DesktopApplicationService extends ApplicationService { private final TradeService tradeService; private final UpdaterService updaterService; private final BisqEasyService bisqEasyService; + private final AlertNotificationsService alertNotificationsService; public DesktopApplicationService(String[] args, ShutDownHandler shutDownHandler) { super("desktop", args); @@ -179,6 +182,8 @@ public DesktopApplicationService(String[] args, ShutDownHandler shutDownHandler) sendNotificationService, tradeService); + alertNotificationsService = new AlertNotificationsService(settingsService, bondedRolesService.getAlertService()); + // TODO (refactor, low prio): Not sure if ServiceProvider is still needed as we added BisqEasyService which exposes most of the services. serviceProvider = new ServiceProvider(shutDownHandler, getConfig(), @@ -198,7 +203,8 @@ public DesktopApplicationService(String[] args, ShutDownHandler shutDownHandler) sendNotificationService, tradeService, updaterService, - bisqEasyService); + bisqEasyService, + alertNotificationsService); } @Override @@ -243,6 +249,7 @@ public CompletableFuture initialize() { .thenCompose(result -> tradeService.initialize()) .thenCompose(result -> updaterService.initialize()) .thenCompose(result -> bisqEasyService.initialize()) + .thenCompose(result -> alertNotificationsService.initialize()) .orTimeout(STARTUP_TIMEOUT_SEC, TimeUnit.SECONDS) .handle((result, throwable) -> { if (throwable == null) { @@ -274,6 +281,7 @@ public CompletableFuture shutdown() { .thenCompose(result -> sendNotificationService.shutdown()) .thenCompose(result -> chatService.shutdown()) .thenCompose(result -> offerService.shutdown()) + .thenCompose(result -> alertNotificationsService.shutdown()) .thenCompose(result -> settingsService.shutdown()) .thenCompose(result -> userService.shutdown()) .thenCompose(result -> contractService.shutdown()) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/ServiceProvider.java b/apps/desktop/desktop/src/main/java/bisq/desktop/ServiceProvider.java index 5d78525914..983ba48eec 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/ServiceProvider.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/ServiceProvider.java @@ -26,6 +26,7 @@ import bisq.application.ShutDownHandler; import bisq.bisq_easy.BisqEasyService; import bisq.bonded_roles.BondedRolesService; +import bisq.bonded_roles.security_manager.alert.AlertNotificationsService; import bisq.chat.ChatService; import bisq.contract.ContractService; import bisq.identity.IdentityService; @@ -67,6 +68,7 @@ public class ServiceProvider { private final TradeService tradeService; private final UpdaterService updaterService; private final BisqEasyService bisqEasyService; + private final AlertNotificationsService alertNotificationsService; public ServiceProvider(ShutDownHandler shutDownHandler, ApplicationService.Config config, @@ -86,7 +88,8 @@ public ServiceProvider(ShutDownHandler shutDownHandler, SendNotificationService sendNotificationService, TradeService tradeService, UpdaterService updaterService, - BisqEasyService bisqEasyService) { + BisqEasyService bisqEasyService, + AlertNotificationsService alertNotificationsService) { this.shutDownHandler = shutDownHandler; this.config = config; this.persistenceService = persistenceService; @@ -106,5 +109,6 @@ public ServiceProvider(ShutDownHandler shutDownHandler, this.tradeService = tradeService; this.updaterService = updaterService; this.bisqEasyService = bisqEasyService; + this.alertNotificationsService = alertNotificationsService; } } diff --git a/bonded-roles/build.gradle.kts b/bonded-roles/build.gradle.kts index 898edb1dd7..f7e4c37d79 100644 --- a/bonded-roles/build.gradle.kts +++ b/bonded-roles/build.gradle.kts @@ -8,6 +8,7 @@ dependencies { implementation(project(":security")) implementation(project(":i18n")) implementation(project(":identity")) + implementation(project(":settings")) implementation("network:network-common") implementation("network:network-identity") diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java index c687af0107..0f1fb671af 100644 --- a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java +++ b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java @@ -18,17 +18,83 @@ package bisq.bonded_roles.security_manager.alert; import bisq.common.application.Service; +import bisq.common.observable.Observable; +import bisq.common.observable.Pin; +import bisq.common.observable.collection.CollectionObserver; +import bisq.common.observable.collection.ObservableSet; +import bisq.settings.SettingsService; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; import java.util.concurrent.CompletableFuture; +@Slf4j public class AlertNotificationsService implements Service { + private final SettingsService settingsService; + private final AlertService alertService; + @Getter + private final Observable isNotificationBannerVisible = new Observable<>(); + private Pin authorizedAlertDataSetPin, unconsumedAlertsPin; + private ObservableSet unconsumedAlerts; + + public AlertNotificationsService(SettingsService settingsService, AlertService alertService) { + this.settingsService = settingsService; + this.alertService = alertService; + } + @Override public CompletableFuture initialize() { - return null; + log.info("initialize"); + + authorizedAlertDataSetPin = alertService.getAuthorizedAlertDataSet().addObserver(new CollectionObserver<>() { + @Override + public void add(AuthorizedAlertData authorizedAlertData) { + if (authorizedAlertData == null) { + return; + } + + if (shouldProcessAlert(authorizedAlertData)) { + unconsumedAlerts.add(authorizedAlertData); + } + } + + @Override + public void remove(Object element) { + if (element instanceof AuthorizedAlertData) { + unconsumedAlerts.remove((AuthorizedAlertData) element); + } + } + + @Override + public void clear() { + unconsumedAlerts.clear(); + } + }); + + unconsumedAlertsPin = unconsumedAlerts.addObserver(this::updateIsNotificationBannerVisible); + + return CompletableFuture.completedFuture(true); } @Override public CompletableFuture shutdown() { - return null; + authorizedAlertDataSetPin.unbind(); + unconsumedAlertsPin.unbind(); + + return CompletableFuture.completedFuture(true); + } + + public void dismissAlert(AuthorizedAlertData authorizedAlertData) { + settingsService.getConsumedAlertIds().add(authorizedAlertData.getId()); + unconsumedAlerts.remove(authorizedAlertData); + } + + private void updateIsNotificationBannerVisible() { + isNotificationBannerVisible.set(!unconsumedAlerts.isEmpty()); + } + + private boolean shouldProcessAlert(AuthorizedAlertData authorizedAlertData) { + return authorizedAlertData.getAlertType() != AlertType.BAN + && !settingsService.getConsumedAlertIds().contains(authorizedAlertData.getId()); } } From d1cc4ebc7f9cf679eab27e3ea4ea5a8f53b9c860 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Mon, 25 Mar 2024 21:56:06 +0100 Subject: [PATCH 075/119] Use AlertNotificationsService instead of AlertService --- .../bisq/desktop/main/MainController.java | 10 ++---- .../main/alert/AlertBannerController.java | 32 +++++-------------- .../desktop/main/alert/AlertBannerModel.java | 3 +- .../alert/AlertNotificationsService.java | 3 +- 4 files changed, 14 insertions(+), 34 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java index 1deba25165..4a3393e25c 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java @@ -19,7 +19,7 @@ import bisq.application.ApplicationService; import bisq.bisq_easy.NavigationTarget; -import bisq.bonded_roles.security_manager.alert.AlertService; +import bisq.bonded_roles.security_manager.alert.AlertNotificationsService; import bisq.desktop.ServiceProvider; import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Controller; @@ -31,7 +31,6 @@ import bisq.desktop.main.left.LeftNavController; import bisq.desktop.main.notification.NotificationPanelController; import bisq.desktop.main.top.TopPanelController; -import bisq.settings.SettingsService; import bisq.updater.UpdaterService; import bisq.updater.UpdaterUtils; import lombok.Getter; @@ -48,24 +47,21 @@ public class MainController extends NavigationController { private final MainView view; private final ServiceProvider serviceProvider; private final LeftNavController leftNavController; - private final SettingsService settingsService; private final UpdaterService updaterService; private final ApplicationService.Config config; - private final AlertBannerController alertBannerController; public MainController(ServiceProvider serviceProvider) { super(NavigationTarget.MAIN); this.serviceProvider = serviceProvider; - settingsService = serviceProvider.getSettingsService(); - AlertService alertService = serviceProvider.getBondedRolesService().getAlertService(); + AlertNotificationsService alertNotificationsService = serviceProvider.getAlertNotificationsService(); updaterService = serviceProvider.getUpdaterService(); config = serviceProvider.getConfig(); leftNavController = new LeftNavController(serviceProvider); TopPanelController topPanelController = new TopPanelController(serviceProvider); NotificationPanelController notificationPanelController = new NotificationPanelController(serviceProvider); - alertBannerController = new AlertBannerController(settingsService, alertService); + AlertBannerController alertBannerController = new AlertBannerController(alertNotificationsService); view = new MainView(model, this, leftNavController.getView().getRoot(), diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java index 6499d75edb..70e4c2febf 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java @@ -17,14 +17,12 @@ package bisq.desktop.main.alert; -import bisq.bonded_roles.security_manager.alert.AlertService; -import bisq.bonded_roles.security_manager.alert.AlertType; +import bisq.bonded_roles.security_manager.alert.AlertNotificationsService; import bisq.bonded_roles.security_manager.alert.AuthorizedAlertData; import bisq.common.observable.Pin; import bisq.common.observable.collection.CollectionObserver; import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Controller; -import bisq.settings.SettingsService; import javafx.collections.ListChangeListener; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -37,14 +35,12 @@ public class AlertBannerController implements Controller { private final AlertBannerModel model; @Getter private final AlertBannerView view; - private final SettingsService settingsService; - private final AlertService alertService; + private final AlertNotificationsService alertNotificationsService; private final ListChangeListener listChangeListener; - private Pin authorizedAlertDataSetPin; + private Pin unconsumedAlertsPin; - public AlertBannerController(SettingsService settingsService, AlertService alertService) { - this.settingsService = settingsService; - this.alertService = alertService; + public AlertBannerController(AlertNotificationsService alertNotificationsService) { + this.alertNotificationsService = alertNotificationsService; model = new AlertBannerModel(); view = new AlertBannerView(model, this); @@ -69,14 +65,9 @@ public AlertBannerController(SettingsService settingsService, AlertService alert @Override public void onActivate() { - updatePredicate(); - - authorizedAlertDataSetPin = alertService.getAuthorizedAlertDataSet().addObserver(new CollectionObserver<>() { + unconsumedAlertsPin = alertNotificationsService.getUnconsumedAlerts().addObserver(new CollectionObserver<>() { @Override public void add(AuthorizedAlertData authorizedAlertData) { - if (authorizedAlertData == null) { - return; - } UIThread.run(() -> model.getObservableList().add(authorizedAlertData)); } @@ -99,13 +90,12 @@ public void clear() { @Override public void onDeactivate() { - authorizedAlertDataSetPin.unbind(); + unconsumedAlertsPin.unbind(); model.getSortedList().removeListener(listChangeListener); } void onClose() { - settingsService.getConsumedAlertIds().add(model.getDisplayedAuthorizedAlertData().getId()); - updatePredicate(); + alertNotificationsService.dismissAlert(model.getDisplayedAuthorizedAlertData()); handleRemove(); } @@ -129,10 +119,4 @@ private void handleRemove() { model.reset(); model.getSortedList().stream().findFirst().ifPresent(this::add); } - - private void updatePredicate() { - model.getFilteredList().setPredicate(authorizedAlertData -> - !settingsService.getConsumedAlertIds().contains(authorizedAlertData.getId()) && - authorizedAlertData.getAlertType() != AlertType.BAN); - } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java index bc385dd6bc..41442c6ba4 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java @@ -34,8 +34,7 @@ @Getter public class AlertBannerModel implements Model { private final ObservableList observableList = FXCollections.observableArrayList(); - private final FilteredList filteredList = new FilteredList<>(observableList); - private final SortedList sortedList = new SortedList<>(filteredList); + private final SortedList sortedList = new SortedList<>(observableList); @Setter private AuthorizedAlertData displayedAuthorizedAlertData; diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java index 0f1fb671af..2cc8b787ff 100644 --- a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java +++ b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java @@ -35,7 +35,8 @@ public class AlertNotificationsService implements Service { @Getter private final Observable isNotificationBannerVisible = new Observable<>(); private Pin authorizedAlertDataSetPin, unconsumedAlertsPin; - private ObservableSet unconsumedAlerts; + @Getter + private final ObservableSet unconsumedAlerts = new ObservableSet<>(); public AlertNotificationsService(SettingsService settingsService, AlertService alertService) { this.settingsService = settingsService; From bdf8ab890085fb3fc6bd179ff9f8b2683d75347c Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Mon, 25 Mar 2024 22:41:30 +0100 Subject: [PATCH 076/119] Set visibility of alerts --- .../desktop/main/alert/AlertBannerController.java | 15 ++++++++++++--- .../alert/AlertNotificationsService.java | 15 +++++---------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java index 70e4c2febf..3f63f10637 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java @@ -26,6 +26,8 @@ import javafx.collections.ListChangeListener; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.fxmisc.easybind.EasyBind; +import org.fxmisc.easybind.Subscription; import java.util.Comparator; import java.util.Optional; @@ -38,6 +40,7 @@ public class AlertBannerController implements Controller { private final AlertNotificationsService alertNotificationsService; private final ListChangeListener listChangeListener; private Pin unconsumedAlertsPin; + private Subscription isAlertVisiblePin; public AlertBannerController(AlertNotificationsService alertNotificationsService) { this.alertNotificationsService = alertNotificationsService; @@ -84,6 +87,8 @@ public void clear() { } }); + isAlertVisiblePin = EasyBind.subscribe(model.getIsAlertVisible(), this::updateIsNotificationBannerVisible); + model.getSortedList().addListener(listChangeListener); model.getSortedList().stream().findFirst().ifPresent(this::add); } @@ -91,6 +96,9 @@ public void clear() { @Override public void onDeactivate() { unconsumedAlertsPin.unbind(); + + isAlertVisiblePin.unsubscribe(); + model.getSortedList().removeListener(listChangeListener); } @@ -109,9 +117,6 @@ private void add(AuthorizedAlertData authorizedAlertData) { model.getMessage().set(authorizedAlertData.getMessage().orElseThrow()); model.getAlertType().set(authorizedAlertData.getAlertType()); model.getIsAlertVisible().set(true); - } else { - log.warn("optionalMessage not present"); - model.getIsAlertVisible().set(false); } } @@ -119,4 +124,8 @@ private void handleRemove() { model.reset(); model.getSortedList().stream().findFirst().ifPresent(this::add); } + + private void updateIsNotificationBannerVisible(boolean isVisible) { + alertNotificationsService.getIsNotificationBannerVisible().set(isVisible); + } } diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java index 2cc8b787ff..7d0186694d 100644 --- a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java +++ b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java @@ -24,6 +24,7 @@ import bisq.common.observable.collection.ObservableSet; import bisq.settings.SettingsService; import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.CompletableFuture; @@ -33,8 +34,8 @@ public class AlertNotificationsService implements Service { private final SettingsService settingsService; private final AlertService alertService; @Getter - private final Observable isNotificationBannerVisible = new Observable<>(); - private Pin authorizedAlertDataSetPin, unconsumedAlertsPin; + private final Observable isNotificationBannerVisible = new Observable<>(false); + private Pin authorizedAlertDataSetPin; @Getter private final ObservableSet unconsumedAlerts = new ObservableSet<>(); @@ -72,15 +73,12 @@ public void clear() { } }); - unconsumedAlertsPin = unconsumedAlerts.addObserver(this::updateIsNotificationBannerVisible); - return CompletableFuture.completedFuture(true); } @Override public CompletableFuture shutdown() { authorizedAlertDataSetPin.unbind(); - unconsumedAlertsPin.unbind(); return CompletableFuture.completedFuture(true); } @@ -90,12 +88,9 @@ public void dismissAlert(AuthorizedAlertData authorizedAlertData) { unconsumedAlerts.remove(authorizedAlertData); } - private void updateIsNotificationBannerVisible() { - isNotificationBannerVisible.set(!unconsumedAlerts.isEmpty()); - } - private boolean shouldProcessAlert(AuthorizedAlertData authorizedAlertData) { return authorizedAlertData.getAlertType() != AlertType.BAN - && !settingsService.getConsumedAlertIds().contains(authorizedAlertData.getId()); + && !settingsService.getConsumedAlertIds().contains(authorizedAlertData.getId()) + && authorizedAlertData.getMessage().isPresent(); } } From 65587d5d49bd62f6fc15b93902026fa5d7a17f2a Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Mon, 25 Mar 2024 22:58:14 +0100 Subject: [PATCH 077/119] Update paddings in content tab controller according to alerts as well --- .../main/alert/AlertBannerController.java | 6 +++--- .../main/content/ContentTabController.java | 21 ++++++++++++++----- .../alert/AlertNotificationsService.java | 3 +-- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java index 3f63f10637..8af82d1a56 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java @@ -87,7 +87,7 @@ public void clear() { } }); - isAlertVisiblePin = EasyBind.subscribe(model.getIsAlertVisible(), this::updateIsNotificationBannerVisible); + isAlertVisiblePin = EasyBind.subscribe(model.getIsAlertVisible(), this::updateIsAlertBannerVisible); model.getSortedList().addListener(listChangeListener); model.getSortedList().stream().findFirst().ifPresent(this::add); @@ -125,7 +125,7 @@ private void handleRemove() { model.getSortedList().stream().findFirst().ifPresent(this::add); } - private void updateIsNotificationBannerVisible(boolean isVisible) { - alertNotificationsService.getIsNotificationBannerVisible().set(isVisible); + private void updateIsAlertBannerVisible(boolean isVisible) { + alertNotificationsService.getIsAlertBannerVisible().set(isVisible); } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/ContentTabController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/ContentTabController.java index 6e2bf6f01b..3d7f170c33 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/ContentTabController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/ContentTabController.java @@ -19,9 +19,9 @@ import bisq.bisq_easy.BisqEasyNotificationsService; import bisq.bisq_easy.NavigationTarget; +import bisq.bonded_roles.security_manager.alert.AlertNotificationsService; import bisq.common.observable.Pin; import bisq.desktop.ServiceProvider; -import bisq.desktop.common.observable.FxBindings; import bisq.desktop.common.view.TabController; import lombok.extern.slf4j.Slf4j; @@ -29,25 +29,36 @@ public abstract class ContentTabController extends TabController { protected final ServiceProvider serviceProvider; private final BisqEasyNotificationsService bisqEasyNotificationsService; - private Pin isNotificationVisiblePin; + private final AlertNotificationsService alertNotificationsService; + private Pin isNotificationPanelVisiblePin, isAlertBannerVisiblePin; public ContentTabController(M model, NavigationTarget host, ServiceProvider serviceProvider) { super(model, host); this.serviceProvider = serviceProvider; bisqEasyNotificationsService = serviceProvider.getBisqEasyService().getBisqEasyNotificationsService(); + alertNotificationsService = serviceProvider.getAlertNotificationsService(); } @Override public void onActivate() { - isNotificationVisiblePin = FxBindings.bind(model.getIsNotificationVisible()) - .to(bisqEasyNotificationsService.getIsNotificationPanelVisible()); + isNotificationPanelVisiblePin = bisqEasyNotificationsService.getIsNotificationPanelVisible().addObserver( + isVisible -> updateIsNotificationVisible()); + isAlertBannerVisiblePin = alertNotificationsService.getIsAlertBannerVisible().addObserver( + isVisible -> updateIsNotificationVisible()); } @Override public void onDeactivate() { - isNotificationVisiblePin.unbind(); + isNotificationPanelVisiblePin.unbind(); + isAlertBannerVisiblePin.unbind(); resetResolvedTarget(); } + + private void updateIsNotificationVisible() { + model.getIsNotificationVisible().set( + bisqEasyNotificationsService.getIsNotificationPanelVisible().get() + || alertNotificationsService.getIsAlertBannerVisible().get()); + } } diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java index 7d0186694d..d16f17bf80 100644 --- a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java +++ b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java @@ -24,7 +24,6 @@ import bisq.common.observable.collection.ObservableSet; import bisq.settings.SettingsService; import lombok.Getter; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.CompletableFuture; @@ -34,7 +33,7 @@ public class AlertNotificationsService implements Service { private final SettingsService settingsService; private final AlertService alertService; @Getter - private final Observable isNotificationBannerVisible = new Observable<>(false); + private final Observable isAlertBannerVisible = new Observable<>(false); private Pin authorizedAlertDataSetPin; @Getter private final ObservableSet unconsumedAlerts = new ObservableSet<>(); From 1ab16d47b7c6c4ee56e00e0eece01e50358e4301 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Tue, 26 Mar 2024 21:47:36 +0100 Subject: [PATCH 078/119] Replicate current functionality using service for alert management --- .../main/alert/AlertBannerController.java | 95 +++++-------------- .../desktop/main/alert/AlertBannerModel.java | 10 -- .../alert/AlertNotificationsService.java | 16 +++- 3 files changed, 37 insertions(+), 84 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java index 8af82d1a56..716d6df3dd 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java @@ -20,14 +20,10 @@ import bisq.bonded_roles.security_manager.alert.AlertNotificationsService; import bisq.bonded_roles.security_manager.alert.AuthorizedAlertData; import bisq.common.observable.Pin; -import bisq.common.observable.collection.CollectionObserver; import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Controller; -import javafx.collections.ListChangeListener; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.fxmisc.easybind.EasyBind; -import org.fxmisc.easybind.Subscription; import java.util.Comparator; import java.util.Optional; @@ -38,94 +34,51 @@ public class AlertBannerController implements Controller { @Getter private final AlertBannerView view; private final AlertNotificationsService alertNotificationsService; - private final ListChangeListener listChangeListener; private Pin unconsumedAlertsPin; - private Subscription isAlertVisiblePin; public AlertBannerController(AlertNotificationsService alertNotificationsService) { this.alertNotificationsService = alertNotificationsService; model = new AlertBannerModel(); view = new AlertBannerView(model, this); - - model.getSortedList().setComparator(Comparator.comparing(AuthorizedAlertData::getAlertType).reversed()); - - listChangeListener = change -> { - change.next(); - if (change.wasAdded()) { - AuthorizedAlertData newItem = model.getSortedList().get(0); - AuthorizedAlertData displayed = model.getDisplayedAuthorizedAlertData(); - if (displayed == null || newItem.getAlertType().ordinal() >= displayed.getAlertType().ordinal()) { - add(newItem); - } - } else if (change.wasRemoved()) { - change.getRemoved().stream() - .filter(e -> e.equals(model.getDisplayedAuthorizedAlertData())) - .findFirst() - .ifPresent(e -> handleRemove()); - } - }; } @Override public void onActivate() { - unconsumedAlertsPin = alertNotificationsService.getUnconsumedAlerts().addObserver(new CollectionObserver<>() { - @Override - public void add(AuthorizedAlertData authorizedAlertData) { - UIThread.run(() -> model.getObservableList().add(authorizedAlertData)); - } - - @Override - public void remove(Object element) { - if (element instanceof AuthorizedAlertData) { - UIThread.run(() -> model.getObservableList().remove((AuthorizedAlertData) element)); - } - } - - @Override - public void clear() { - UIThread.run(() -> model.getObservableList().clear()); - } - }); - - isAlertVisiblePin = EasyBind.subscribe(model.getIsAlertVisible(), this::updateIsAlertBannerVisible); - - model.getSortedList().addListener(listChangeListener); - model.getSortedList().stream().findFirst().ifPresent(this::add); + unconsumedAlertsPin = alertNotificationsService.getUnconsumedAlerts().addObserver(this::showAlertBanner); } @Override public void onDeactivate() { unconsumedAlertsPin.unbind(); - - isAlertVisiblePin.unsubscribe(); - - model.getSortedList().removeListener(listChangeListener); } void onClose() { - alertNotificationsService.dismissAlert(model.getDisplayedAuthorizedAlertData()); - handleRemove(); - } - - private void add(AuthorizedAlertData authorizedAlertData) { - model.setDisplayedAuthorizedAlertData(authorizedAlertData); - Optional optionalMessage = authorizedAlertData.getMessage(); - - if (optionalMessage.isPresent()) { - log.info("Showing alert with message {}", optionalMessage.get()); - model.getHeadline().set(authorizedAlertData.getHeadline().orElseThrow()); - model.getMessage().set(authorizedAlertData.getMessage().orElseThrow()); - model.getAlertType().set(authorizedAlertData.getAlertType()); - model.getIsAlertVisible().set(true); - } + UIThread.run(() -> { + alertNotificationsService.dismissAlert(model.getDisplayedAuthorizedAlertData()); + model.reset(); + showAlertBanner(); + }); } - private void handleRemove() { - model.reset(); - model.getSortedList().stream().findFirst().ifPresent(this::add); + private void showAlertBanner() { + UIThread.run(() -> { + Optional mostRelevantAlert = alertNotificationsService.getUnconsumedAlerts().stream() + .max(Comparator.comparing(AuthorizedAlertData::getAlertType).thenComparing(AuthorizedAlertData::getDate)); + if (mostRelevantAlert.isPresent()) { + if (!mostRelevantAlert.get().equals(model.getDisplayedAuthorizedAlertData())) { + // Only show incoming alert effect if newly added alert is more relevant than current one + model.reset(); + } + add(mostRelevantAlert.get()); + } + }); } - private void updateIsAlertBannerVisible(boolean isVisible) { - alertNotificationsService.getIsAlertBannerVisible().set(isVisible); + private void add(AuthorizedAlertData authorizedAlertData) { + model.setDisplayedAuthorizedAlertData(authorizedAlertData); + model.getHeadline().set(authorizedAlertData.getHeadline().orElseThrow()); + authorizedAlertData.getMessage().ifPresent(message -> model.getMessage().set(message)); + model.getAlertType().set(authorizedAlertData.getAlertType()); + model.getIsAlertVisible().set(true); } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java index 41442c6ba4..4897aa18d0 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java @@ -21,21 +21,11 @@ import bisq.bonded_roles.security_manager.alert.AuthorizedAlertData; import bisq.desktop.common.view.Model; import javafx.beans.property.*; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.collections.transformation.FilteredList; -import javafx.collections.transformation.SortedList; import lombok.Getter; import lombok.Setter; -import java.util.HashSet; -import java.util.Set; - @Getter public class AlertBannerModel implements Model { - private final ObservableList observableList = FXCollections.observableArrayList(); - private final SortedList sortedList = new SortedList<>(observableList); - @Setter private AuthorizedAlertData displayedAuthorizedAlertData; private final BooleanProperty isAlertVisible = new SimpleBooleanProperty(); diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java index d16f17bf80..378c6471c1 100644 --- a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java +++ b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java @@ -28,15 +28,17 @@ import java.util.concurrent.CompletableFuture; +import static com.google.common.base.Preconditions.checkNotNull; + @Slf4j public class AlertNotificationsService implements Service { private final SettingsService settingsService; private final AlertService alertService; @Getter - private final Observable isAlertBannerVisible = new Observable<>(false); - private Pin authorizedAlertDataSetPin; - @Getter private final ObservableSet unconsumedAlerts = new ObservableSet<>(); + @Getter + private final Observable isAlertBannerVisible = new Observable<>(false); + private Pin authorizedAlertDataSetPin, unconsumedAlertsPin; public AlertNotificationsService(SettingsService settingsService, AlertService alertService) { this.settingsService = settingsService; @@ -72,17 +74,21 @@ public void clear() { } }); + unconsumedAlertsPin = unconsumedAlerts.addObserver(this::updateIsNotificationBannerVisible); + return CompletableFuture.completedFuture(true); } @Override public CompletableFuture shutdown() { authorizedAlertDataSetPin.unbind(); + unconsumedAlertsPin.unbind(); return CompletableFuture.completedFuture(true); } public void dismissAlert(AuthorizedAlertData authorizedAlertData) { + checkNotNull(authorizedAlertData, "Cannot dismiss alert because it's null."); settingsService.getConsumedAlertIds().add(authorizedAlertData.getId()); unconsumedAlerts.remove(authorizedAlertData); } @@ -92,4 +98,8 @@ private boolean shouldProcessAlert(AuthorizedAlertData authorizedAlertData) { && !settingsService.getConsumedAlertIds().contains(authorizedAlertData.getId()) && authorizedAlertData.getMessage().isPresent(); } + + private void updateIsNotificationBannerVisible() { + isAlertBannerVisible.set(!unconsumedAlerts.isEmpty()); + } } From 4bcaaa92c3bf31baab6a19ebad3e969796bc8087 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Tue, 26 Mar 2024 22:10:47 +0100 Subject: [PATCH 079/119] Correct paddings based on whether there's another notification --- .../java/bisq/desktop/main/MainController.java | 3 +-- .../main/alert/AlertBannerController.java | 17 ++++++++++++++--- .../desktop/main/alert/AlertBannerModel.java | 1 + .../desktop/main/alert/AlertBannerView.java | 16 +++++++++++----- .../desktop/main/content/ContentTabModel.java | 2 +- 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java index 4a3393e25c..f210192de9 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/MainController.java @@ -54,14 +54,13 @@ public MainController(ServiceProvider serviceProvider) { super(NavigationTarget.MAIN); this.serviceProvider = serviceProvider; - AlertNotificationsService alertNotificationsService = serviceProvider.getAlertNotificationsService(); updaterService = serviceProvider.getUpdaterService(); config = serviceProvider.getConfig(); leftNavController = new LeftNavController(serviceProvider); TopPanelController topPanelController = new TopPanelController(serviceProvider); NotificationPanelController notificationPanelController = new NotificationPanelController(serviceProvider); - AlertBannerController alertBannerController = new AlertBannerController(alertNotificationsService); + AlertBannerController alertBannerController = new AlertBannerController(serviceProvider); view = new MainView(model, this, leftNavController.getView().getRoot(), diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java index 716d6df3dd..1dfa2712ec 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java @@ -17,9 +17,11 @@ package bisq.desktop.main.alert; +import bisq.bisq_easy.BisqEasyNotificationsService; import bisq.bonded_roles.security_manager.alert.AlertNotificationsService; import bisq.bonded_roles.security_manager.alert.AuthorizedAlertData; import bisq.common.observable.Pin; +import bisq.desktop.ServiceProvider; import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Controller; import lombok.Getter; @@ -34,10 +36,12 @@ public class AlertBannerController implements Controller { @Getter private final AlertBannerView view; private final AlertNotificationsService alertNotificationsService; - private Pin unconsumedAlertsPin; + private final BisqEasyNotificationsService bisqEasyNotificationsService; + private Pin unconsumedAlertsPin, isBisqEasyNotificationVisiblePin; - public AlertBannerController(AlertNotificationsService alertNotificationsService) { - this.alertNotificationsService = alertNotificationsService; + public AlertBannerController(ServiceProvider serviceProvider) { + alertNotificationsService = serviceProvider.getAlertNotificationsService(); + bisqEasyNotificationsService = serviceProvider.getBisqEasyService().getBisqEasyNotificationsService(); model = new AlertBannerModel(); view = new AlertBannerView(model, this); } @@ -45,11 +49,14 @@ public AlertBannerController(AlertNotificationsService alertNotificationsService @Override public void onActivate() { unconsumedAlertsPin = alertNotificationsService.getUnconsumedAlerts().addObserver(this::showAlertBanner); + isBisqEasyNotificationVisiblePin = bisqEasyNotificationsService.getIsNotificationPanelVisible().addObserver( + this::updateIsBisqEasyNotificationVisible); } @Override public void onDeactivate() { unconsumedAlertsPin.unbind(); + isBisqEasyNotificationVisiblePin.unbind(); } void onClose() { @@ -81,4 +88,8 @@ private void add(AuthorizedAlertData authorizedAlertData) { model.getAlertType().set(authorizedAlertData.getAlertType()); model.getIsAlertVisible().set(true); } + + private void updateIsBisqEasyNotificationVisible(boolean isVisible) { + model.getIsBisqEasyNotificationVisible().set(isVisible); + } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java index 4897aa18d0..7d8857580e 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerModel.java @@ -32,6 +32,7 @@ public class AlertBannerModel implements Model { private final StringProperty headline = new SimpleStringProperty(); private final StringProperty message = new SimpleStringProperty(); private final ObjectProperty alertType = new SimpleObjectProperty<>(); + private final BooleanProperty isBisqEasyNotificationVisible = new SimpleBooleanProperty(); public AlertBannerModel() { } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java index 751f928ded..473a607bcf 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerView.java @@ -40,15 +40,17 @@ @Slf4j public class AlertBannerView extends View { public static final int DURATION = Transitions.DEFAULT_DURATION / 2; - private final VBox contentVBox; + public static final Insets DEFAULT_PADDING = new Insets(20, 40, 20, 40); + public static final Insets PADDING_WITH_NOTIFICATION = new Insets(0, 40, 20, 40); - Label headline = new Label(); - Label message = new Label(); + private final VBox contentVBox; private final Button closeButton; private final HBox banner; private final ChangeListener heightListener; + Label headline = new Label(); + Label message = new Label(); private Timeline slideInRightTimeline, slideOutTopTimeline; - private Subscription isVisiblePin, alertTypePin; + private Subscription isVisiblePin, alertTypePin, isBisqEasyNotificationVisiblePin; public AlertBannerView(AlertBannerModel model, AlertBannerController controller) { super(new BorderPane(), model, controller); @@ -74,7 +76,7 @@ public AlertBannerView(AlertBannerModel model, AlertBannerController controller) heightListener = ((observable, oldValue, newValue) -> banner.setMinHeight(contentVBox.getHeight() + 25)); // padding = 25 root.setCenter(banner); - root.setPadding(new Insets(20, 40, 20, 40)); + root.setPadding(DEFAULT_PADDING); } @Override @@ -119,6 +121,9 @@ protected void onViewAttached() { } }); + isBisqEasyNotificationVisiblePin = EasyBind.subscribe(model.getIsBisqEasyNotificationVisible(), isVisible -> + root.setPadding(isVisible ? PADDING_WITH_NOTIFICATION : DEFAULT_PADDING)); + message.heightProperty().addListener(heightListener); closeButton.setOnAction(e -> controller.onClose()); @@ -131,6 +136,7 @@ protected void onViewDetached() { isVisiblePin.unsubscribe(); alertTypePin.unsubscribe(); + isBisqEasyNotificationVisiblePin.unsubscribe(); message.heightProperty().removeListener(heightListener); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/ContentTabModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/ContentTabModel.java index 0036b51e8e..da9c0e6160 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/ContentTabModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/ContentTabModel.java @@ -25,4 +25,4 @@ @Getter public abstract class ContentTabModel extends TabModel { private final BooleanProperty isNotificationVisible = new SimpleBooleanProperty(); -} \ No newline at end of file +} From 5d97a3395bbc99de6ce5048968688767956689ae Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Tue, 26 Mar 2024 22:21:41 +0100 Subject: [PATCH 080/119] Remove unused import --- .../main/java/bisq/desktop_app/DesktopApplicationService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/desktop/desktop-app/src/main/java/bisq/desktop_app/DesktopApplicationService.java b/apps/desktop/desktop-app/src/main/java/bisq/desktop_app/DesktopApplicationService.java index 25cd5409a2..9fcbbb0ffc 100644 --- a/apps/desktop/desktop-app/src/main/java/bisq/desktop_app/DesktopApplicationService.java +++ b/apps/desktop/desktop-app/src/main/java/bisq/desktop_app/DesktopApplicationService.java @@ -48,7 +48,6 @@ import bisq.wallets.electrum.ElectrumWalletService; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.checkerframework.checker.units.qual.A; import java.util.Optional; import java.util.concurrent.CompletableFuture; From a8dcc617815ba01b85896151cb7eb4dfd970a65e Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Tue, 26 Mar 2024 22:27:39 +0100 Subject: [PATCH 081/119] Only add alert if different from current --- .../bisq/desktop/main/alert/AlertBannerController.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java index 1dfa2712ec..0ad1019d22 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/alert/AlertBannerController.java @@ -71,11 +71,8 @@ private void showAlertBanner() { UIThread.run(() -> { Optional mostRelevantAlert = alertNotificationsService.getUnconsumedAlerts().stream() .max(Comparator.comparing(AuthorizedAlertData::getAlertType).thenComparing(AuthorizedAlertData::getDate)); - if (mostRelevantAlert.isPresent()) { - if (!mostRelevantAlert.get().equals(model.getDisplayedAuthorizedAlertData())) { - // Only show incoming alert effect if newly added alert is more relevant than current one - model.reset(); - } + if (mostRelevantAlert.isPresent() && !mostRelevantAlert.get().equals(model.getDisplayedAuthorizedAlertData())) { + model.reset(); add(mostRelevantAlert.get()); } }); From 43300a253d100c4ae4687f99205cf3bc3b01e25b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Garc=C3=ADa?= Date: Wed, 27 Mar 2024 15:15:04 +0100 Subject: [PATCH 082/119] Bump version number for v2.0.2 --- .../main/java/bisq/desktop_app_launcher/DesktopAppLauncher.java | 2 +- apps/desktop/desktop-app/build.gradle.kts | 2 +- apps/desktop/desktop-app/src/main/resources/desktop.conf | 2 +- .../oracle-node-app/src/main/resources/oracle_node.conf | 2 +- apps/rest-api-app/src/main/resources/rest_api.conf | 2 +- apps/seed-node-app/src/main/resources/seed_node.conf | 2 +- .../src/main/kotlin/bisq/gradle/packaging/PackagingPlugin.kt | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/desktop/desktop-app-launcher/src/main/java/bisq/desktop_app_launcher/DesktopAppLauncher.java b/apps/desktop/desktop-app-launcher/src/main/java/bisq/desktop_app_launcher/DesktopAppLauncher.java index 3b2d6e2d55..0b28b7f44a 100644 --- a/apps/desktop/desktop-app-launcher/src/main/java/bisq/desktop_app_launcher/DesktopAppLauncher.java +++ b/apps/desktop/desktop-app-launcher/src/main/java/bisq/desktop_app_launcher/DesktopAppLauncher.java @@ -61,7 +61,7 @@ */ @Slf4j public class DesktopAppLauncher { - private static final String VERSION = "2.0.1"; + private static final String VERSION = "2.0.2"; private static final String APP_NAME = "Bisq2"; private static final String FINGER_PRINT_ALEJANDRO_GARCIA = "E222AA02"; // B493 3191 06CC 3D1F 252E 19CB F806 F422 E222 AA02 private static final String FINGER_PRINT_HENRIK_JANNSEN = "387C8307"; // B8A5 D214 ADFA A387 A14C 8BCF 02AA 2BAE 387C 8307 diff --git a/apps/desktop/desktop-app/build.gradle.kts b/apps/desktop/desktop-app/build.gradle.kts index d12af31705..2ca23bff8f 100644 --- a/apps/desktop/desktop-app/build.gradle.kts +++ b/apps/desktop/desktop-app/build.gradle.kts @@ -10,7 +10,7 @@ application { mainClass.set("bisq.desktop_app.DesktopApp") } -version = "2.0.1" +version = "2.0.2" javafx { version = "17.0.1" diff --git a/apps/desktop/desktop-app/src/main/resources/desktop.conf b/apps/desktop/desktop-app/src/main/resources/desktop.conf index 92cdf042ae..9df8d46409 100644 --- a/apps/desktop/desktop-app/src/main/resources/desktop.conf +++ b/apps/desktop/desktop-app/src/main/resources/desktop.conf @@ -1,6 +1,6 @@ application { appName = "Bisq2" - version = "2.0.1" + version = "2.0.2" devMode = false keyIds = "E222AA02,387C8307" ignoreSigningKeyInResourcesCheck = false diff --git a/apps/oracle-node/oracle-node-app/src/main/resources/oracle_node.conf b/apps/oracle-node/oracle-node-app/src/main/resources/oracle_node.conf index 576d170c44..bfbd5b57d4 100644 --- a/apps/oracle-node/oracle-node-app/src/main/resources/oracle_node.conf +++ b/apps/oracle-node/oracle-node-app/src/main/resources/oracle_node.conf @@ -1,6 +1,6 @@ application { appName = "OracleNode" - version = "2.0.1" + version = "2.0.2" devMode = false keyIds = "E222AA02,387C8307" ignoreSigningKeyInResourcesCheck = false diff --git a/apps/rest-api-app/src/main/resources/rest_api.conf b/apps/rest-api-app/src/main/resources/rest_api.conf index 4fc489e5da..3f9ffade5e 100644 --- a/apps/rest-api-app/src/main/resources/rest_api.conf +++ b/apps/rest-api-app/src/main/resources/rest_api.conf @@ -1,6 +1,6 @@ application { appName = "Bisq2" - version = "2.0.1" + version = "2.0.2" devMode = false keyIds = "E222AA02,387C8307" ignoreSigningKeyInResourcesCheck = false diff --git a/apps/seed-node-app/src/main/resources/seed_node.conf b/apps/seed-node-app/src/main/resources/seed_node.conf index 00ce458f90..34d0b074cd 100644 --- a/apps/seed-node-app/src/main/resources/seed_node.conf +++ b/apps/seed-node-app/src/main/resources/seed_node.conf @@ -1,6 +1,6 @@ application { appName = "Bisq2_seed_node" - version = "2.0.1" + version = "2.0.2" devMode = false keyIds = "E222AA02,387C8307" ignoreSigningKeyInResourcesCheck = false diff --git a/build-logic/packaging/src/main/kotlin/bisq/gradle/packaging/PackagingPlugin.kt b/build-logic/packaging/src/main/kotlin/bisq/gradle/packaging/PackagingPlugin.kt index e705909db2..36b3ab1568 100644 --- a/build-logic/packaging/src/main/kotlin/bisq/gradle/packaging/PackagingPlugin.kt +++ b/build-logic/packaging/src/main/kotlin/bisq/gradle/packaging/PackagingPlugin.kt @@ -22,7 +22,7 @@ import javax.inject.Inject class PackagingPlugin @Inject constructor(private val javaToolchainService: JavaToolchainService) : Plugin { companion object { - const val APP_VERSION = "2.0.1" + const val APP_VERSION = "2.0.2" const val OUTPUT_DIR_PATH = "packaging/jpackage/packages" } From d28a78eb47cbc2d1c13b4d76707094dea3ae76db Mon Sep 17 00:00:00 2001 From: namloan Date: Wed, 27 Mar 2024 15:55:33 +0100 Subject: [PATCH 083/119] Add Hashcash tests with benchmarks Imported from Bisq1, updated benchmarks for newer processor. --- gradle/libs.versions.toml | 2 + security/build.gradle.kts | 2 + .../HashCashProofOfWorkServiceTest.java | 109 ++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 security/src/test/java/bisq/security/pow/hashcash/HashCashProofOfWorkServiceTest.java diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8db9724c3a..721ede1a63 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,6 +7,7 @@ [versions] annotations-lib = { strictly = '23.0.0' } +apache-commons-lang-lib = { strictly = '3.14.0' } apache-httpcomponents-core-lib = { strictly = '4.4.13' } apache-httpcomponents-httpclient-lib = { strictly = '4.5.13' } apache-tomcat-annotations-api = { strictly = '6.0.53' } @@ -71,6 +72,7 @@ typesafe-config-lib = { strictly = '1.4.2' } # Note: keys can contain dash (protobuf-java) but the dash is replaced by dot when referenced # in a build.gradle ('implementation libs.protobuf.java') [libraries] +apache-commons-lang = { module = 'org.apache.commons:commons-lang3', version.ref = 'apache-commons-lang-lib' } apache-httpcomponents-core = { module = 'org.apache.httpcomponents:httpcore', version.ref = 'apache-httpcomponents-core-lib' } apache-httpcomponents-httpclient = { module = 'org.apache.httpcomponents:httpclient', version.ref = 'apache-httpcomponents-httpclient-lib' } apache-tomcat-annotations-api = { module = 'org.apache.tomcat:annotations-api', version.ref = 'apache-tomcat-annotations-api' } diff --git a/security/build.gradle.kts b/security/build.gradle.kts index 784fe7f2f3..0d542cdd5d 100644 --- a/security/build.gradle.kts +++ b/security/build.gradle.kts @@ -9,4 +9,6 @@ dependencies { implementation(libs.bouncycastle) implementation(libs.bouncycastle.pg) implementation(libs.typesafe.config) + + testImplementation(libs.apache.commons.lang) } diff --git a/security/src/test/java/bisq/security/pow/hashcash/HashCashProofOfWorkServiceTest.java b/security/src/test/java/bisq/security/pow/hashcash/HashCashProofOfWorkServiceTest.java new file mode 100644 index 0000000000..cf7ed7cfd8 --- /dev/null +++ b/security/src/test/java/bisq/security/pow/hashcash/HashCashProofOfWorkServiceTest.java @@ -0,0 +1,109 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.security.pow.hashcash; + +import bisq.security.DigestUtil; +import bisq.security.pow.ProofOfWork; +import org.apache.commons.lang3.RandomStringUtils; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static bisq.security.pow.hashcash.HashCashProofOfWorkService.numberOfLeadingZeros; +import static bisq.security.pow.hashcash.HashCashProofOfWorkService.toNumLeadingZeros; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class HashCashProofOfWorkServiceTest { + + private final static Logger log = LoggerFactory.getLogger(HashCashProofOfWorkServiceTest.class); + + @Test + public void testNumberOfLeadingZeros() { + assertEquals(8, numberOfLeadingZeros((byte) 0x0)); + assertEquals(0, numberOfLeadingZeros((byte) 0xFF)); + assertEquals(6, numberOfLeadingZeros((byte) 0x2)); + assertEquals(2, numberOfLeadingZeros(Byte.parseByte("00100000", 2))); + assertEquals(1, numberOfLeadingZeros(new byte[]{Byte.parseByte("01000000", 2), Byte.parseByte("00000000", 2)})); + assertEquals(9, numberOfLeadingZeros(new byte[]{Byte.parseByte("00000000", 2), Byte.parseByte("01000000", 2)})); + assertEquals(17, numberOfLeadingZeros(new byte[]{Byte.parseByte("00000000", 2), Byte.parseByte("00000000", 2), Byte.parseByte("01000000", 2)})); + assertEquals(9, numberOfLeadingZeros(new byte[]{Byte.parseByte("00000000", 2), Byte.parseByte("01010000", 2)})); + } + + @Test + public void testToNumLeadingZeros() { + assertEquals(0, toNumLeadingZeros(-1.0)); + assertEquals(0, toNumLeadingZeros(0.0)); + assertEquals(0, toNumLeadingZeros(1.0)); + assertEquals(1, toNumLeadingZeros(1.1)); + assertEquals(1, toNumLeadingZeros(2.0)); + assertEquals(8, toNumLeadingZeros(256.0)); + assertEquals(1024, toNumLeadingZeros(Double.POSITIVE_INFINITY)); + } + + @Test + public void testDiffIncrease() throws ExecutionException, InterruptedException { + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < 9; i++) { + run(i, stringBuilder); + } + log.info(stringBuilder.toString()); + +// Test results on 10-core M2 Pro: +// Minting 1000 tokens with > 0 leading zeros took 0.096 ms per token and 2 iterations in average. Verification took 0.004 ms per token. +// Minting 1000 tokens with > 1 leading zeros took 0.007 ms per token and 4 iterations in average. Verification took 0.001 ms per token. +// Minting 1000 tokens with > 2 leading zeros took 0.012 ms per token and 8 iterations in average. Verification took 0.001 ms per token. +// Minting 1000 tokens with > 3 leading zeros took 0.015 ms per token and 17 iterations in average. Verification took 0.001 ms per token. +// Minting 1000 tokens with > 4 leading zeros took 0.023 ms per token and 33 iterations in average. Verification took 0.001 ms per token. +// Minting 1000 tokens with > 5 leading zeros took 0.046 ms per token and 66 iterations in average. Verification took 0.001 ms per token. +// Minting 1000 tokens with > 6 leading zeros took 0.087 ms per token and 131 iterations in average. Verification took 0.0 ms per token. +// Minting 1000 tokens with > 7 leading zeros took 0.15 ms per token and 240 iterations in average. Verification took 0.001 ms per token. +// Minting 1000 tokens with > 8 leading zeros took 0.318 ms per token and 526 iterations in average. Verification took 0.001 ms per token. + } + + private void run(int log2Difficulty, StringBuilder stringBuilder) throws ExecutionException, InterruptedException { + double difficulty = Math.scalb(1.0, log2Difficulty); + int numTokens = 1000; + byte[] payload = RandomStringUtils.random(50, true, true).getBytes(StandardCharsets.UTF_8); + long ts = System.currentTimeMillis(); + List tokens = new ArrayList<>(); + for (int i = 0; i < numTokens; i++) { + byte[] challenge = DigestUtil.sha256(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)); + tokens.add(new HashCashProofOfWorkService().mint(payload, challenge, difficulty)); + } + double size = tokens.size(); + long ts2 = System.currentTimeMillis(); + long averageCounter = Math.round(tokens.stream().mapToLong(ProofOfWork::getCounter).average().orElse(0)); + boolean allValid = tokens.stream().allMatch(new HashCashProofOfWorkService()::verify); + assertTrue(allValid); + double time1 = (System.currentTimeMillis() - ts) / size; + double time2 = (System.currentTimeMillis() - ts2) / size; + stringBuilder.append("\nMinting ").append(numTokens) + .append(" tokens with > ").append(log2Difficulty) + .append(" leading zeros took ").append(time1) + .append(" ms per token and ").append(averageCounter) + .append(" iterations in average. Verification took ").append(time2) + .append(" ms per token."); + } +} \ No newline at end of file From 97821de2e46619a3dfba33a5d252e1727232e478 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Thu, 28 Mar 2024 10:52:50 +0700 Subject: [PATCH 084/119] Fix nullpointer at shutdown --- .../security_manager/alert/AlertNotificationsService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java index 378c6471c1..8b1603c52f 100644 --- a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java +++ b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AlertNotificationsService.java @@ -81,8 +81,10 @@ public void clear() { @Override public CompletableFuture shutdown() { - authorizedAlertDataSetPin.unbind(); - unconsumedAlertsPin.unbind(); + if (authorizedAlertDataSetPin != null) { + authorizedAlertDataSetPin.unbind(); + unconsumedAlertsPin.unbind(); + } return CompletableFuture.completedFuture(true); } From e66c04eb4e169f8620cf399e4db6de4d708c2ae5 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Thu, 28 Mar 2024 11:05:49 +0700 Subject: [PATCH 085/119] Change PRIORITY in MetaData --- .../java/bisq/bonded_roles/release/ReleaseNotification.java | 4 ++-- .../security_manager/alert/AuthorizedAlertData.java | 4 ++-- .../AuthorizedDifficultyAdjustmentData.java | 4 ++-- .../AuthorizedMinRequiredReputationScoreData.java | 4 ++-- .../src/main/java/bisq/user/banned/BannedUserProfileData.java | 4 ++-- .../bisq/user/reputation/data/AuthorizedAccountAgeData.java | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/release/ReleaseNotification.java b/bonded-roles/src/main/java/bisq/bonded_roles/release/ReleaseNotification.java index e74682d74b..f679ccf5f9 100644 --- a/bonded-roles/src/main/java/bisq/bonded_roles/release/ReleaseNotification.java +++ b/bonded-roles/src/main/java/bisq/bonded_roles/release/ReleaseNotification.java @@ -34,7 +34,7 @@ import java.util.Set; -import static bisq.network.p2p.services.data.storage.MetaData.HIGH_PRIORITY; +import static bisq.network.p2p.services.data.storage.MetaData.HIGHEST_PRIORITY; import static bisq.network.p2p.services.data.storage.MetaData.TTL_100_DAYS; @Slf4j @@ -44,7 +44,7 @@ public final class ReleaseNotification implements AuthorizedDistributedData { public final static int MAX_MESSAGE_LENGTH = 10_000; - private final MetaData metaData = new MetaData(TTL_100_DAYS, HIGH_PRIORITY, getClass().getSimpleName()); + private final MetaData metaData = new MetaData(TTL_100_DAYS, HIGHEST_PRIORITY, getClass().getSimpleName()); private final String id; private final long date; private final boolean isPreRelease; diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AuthorizedAlertData.java b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AuthorizedAlertData.java index feecf4223b..2f32a6a708 100644 --- a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AuthorizedAlertData.java +++ b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/alert/AuthorizedAlertData.java @@ -36,7 +36,7 @@ import java.util.Optional; import java.util.Set; -import static bisq.network.p2p.services.data.storage.MetaData.HIGH_PRIORITY; +import static bisq.network.p2p.services.data.storage.MetaData.HIGHEST_PRIORITY; import static bisq.network.p2p.services.data.storage.MetaData.TTL_30_DAYS; @Slf4j @@ -46,7 +46,7 @@ public final class AuthorizedAlertData implements AuthorizedDistributedData { public final static int MAX_MESSAGE_LENGTH = 1000; - private final MetaData metaData = new MetaData(TTL_30_DAYS, HIGH_PRIORITY, getClass().getSimpleName()); + private final MetaData metaData = new MetaData(TTL_30_DAYS, HIGHEST_PRIORITY, getClass().getSimpleName()); private final String id; private final long date; private final AlertType alertType; diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/difficulty_adjustment/AuthorizedDifficultyAdjustmentData.java b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/difficulty_adjustment/AuthorizedDifficultyAdjustmentData.java index 9856df6ae7..3d339450de 100644 --- a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/difficulty_adjustment/AuthorizedDifficultyAdjustmentData.java +++ b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/difficulty_adjustment/AuthorizedDifficultyAdjustmentData.java @@ -33,7 +33,7 @@ import java.util.Set; -import static bisq.network.p2p.services.data.storage.MetaData.HIGH_PRIORITY; +import static bisq.network.p2p.services.data.storage.MetaData.HIGHEST_PRIORITY; import static bisq.network.p2p.services.data.storage.MetaData.TTL_100_DAYS; @Slf4j @@ -41,7 +41,7 @@ @EqualsAndHashCode @Getter public final class AuthorizedDifficultyAdjustmentData implements AuthorizedDistributedData { - private final MetaData metaData = new MetaData(TTL_100_DAYS, HIGH_PRIORITY, getClass().getSimpleName()); + private final MetaData metaData = new MetaData(TTL_100_DAYS, HIGHEST_PRIORITY, getClass().getSimpleName()); private final long date; private final double difficultyAdjustmentFactor; private final String securityManagerProfileId; diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/min_reputation_score/AuthorizedMinRequiredReputationScoreData.java b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/min_reputation_score/AuthorizedMinRequiredReputationScoreData.java index 04796f8dd4..94050564b4 100644 --- a/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/min_reputation_score/AuthorizedMinRequiredReputationScoreData.java +++ b/bonded-roles/src/main/java/bisq/bonded_roles/security_manager/min_reputation_score/AuthorizedMinRequiredReputationScoreData.java @@ -33,7 +33,7 @@ import java.util.Set; -import static bisq.network.p2p.services.data.storage.MetaData.HIGH_PRIORITY; +import static bisq.network.p2p.services.data.storage.MetaData.HIGHEST_PRIORITY; import static bisq.network.p2p.services.data.storage.MetaData.TTL_100_DAYS; @Slf4j @@ -41,7 +41,7 @@ @EqualsAndHashCode @Getter public final class AuthorizedMinRequiredReputationScoreData implements AuthorizedDistributedData { - private final MetaData metaData = new MetaData(TTL_100_DAYS, HIGH_PRIORITY, getClass().getSimpleName()); + private final MetaData metaData = new MetaData(TTL_100_DAYS, HIGHEST_PRIORITY, getClass().getSimpleName()); private final long date; private final long minRequiredReputationScore; private final String securityManagerProfileId; diff --git a/user/src/main/java/bisq/user/banned/BannedUserProfileData.java b/user/src/main/java/bisq/user/banned/BannedUserProfileData.java index fb4958dbb9..f6c9d7ece2 100644 --- a/user/src/main/java/bisq/user/banned/BannedUserProfileData.java +++ b/user/src/main/java/bisq/user/banned/BannedUserProfileData.java @@ -32,14 +32,14 @@ import java.util.Set; -import static bisq.network.p2p.services.data.storage.MetaData.HIGH_PRIORITY; +import static bisq.network.p2p.services.data.storage.MetaData.HIGHEST_PRIORITY; import static bisq.network.p2p.services.data.storage.MetaData.TTL_100_DAYS; @Slf4j @EqualsAndHashCode @Getter public final class BannedUserProfileData implements AuthorizedDistributedData { - private final MetaData metaData = new MetaData(TTL_100_DAYS, HIGH_PRIORITY, getClass().getSimpleName()); + private final MetaData metaData = new MetaData(TTL_100_DAYS, HIGHEST_PRIORITY, getClass().getSimpleName()); private final UserProfile userProfile; private final boolean staticPublicKeysProvided; diff --git a/user/src/main/java/bisq/user/reputation/data/AuthorizedAccountAgeData.java b/user/src/main/java/bisq/user/reputation/data/AuthorizedAccountAgeData.java index a7de0a1fd0..ace2fc5d54 100644 --- a/user/src/main/java/bisq/user/reputation/data/AuthorizedAccountAgeData.java +++ b/user/src/main/java/bisq/user/reputation/data/AuthorizedAccountAgeData.java @@ -33,7 +33,7 @@ import java.util.Date; import java.util.Set; -import static bisq.network.p2p.services.data.storage.MetaData.HIGHEST_PRIORITY; +import static bisq.network.p2p.services.data.storage.MetaData.HIGH_PRIORITY; import static bisq.network.p2p.services.data.storage.MetaData.TTL_100_DAYS; @Slf4j @@ -42,7 +42,7 @@ public final class AuthorizedAccountAgeData implements AuthorizedDistributedData { public static final long TTL = TTL_100_DAYS; - private final MetaData metaData = new MetaData(TTL, HIGHEST_PRIORITY, getClass().getSimpleName()); + private final MetaData metaData = new MetaData(TTL, HIGH_PRIORITY, getClass().getSimpleName()); private final String profileId; private final long date; private final boolean staticPublicKeysProvided; From 612ed33eab8c86ced35c36f9fad9c9c99d4a5357 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Fri, 29 Mar 2024 20:48:25 +0100 Subject: [PATCH 086/119] Remove redundant method --- .../p2p/services/data/storage/DataStorageService.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/network/network/src/main/java/bisq/network/p2p/services/data/storage/DataStorageService.java b/network/network/src/main/java/bisq/network/p2p/services/data/storage/DataStorageService.java index fcd7aa1007..643bdc608f 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/data/storage/DataStorageService.java +++ b/network/network/src/main/java/bisq/network/p2p/services/data/storage/DataStorageService.java @@ -48,6 +48,7 @@ public abstract class DataStorageService extends RateLimi public DataStorageService(PersistenceService persistenceService, String storeName, String storeKey) { super(); + this.storeKey = storeKey; String storageFileName = StringUtils.camelCaseToSnakeCase(storeKey + STORE_POST_FIX); subDirectory = DbSubDirectory.NETWORK_DB.getDbPath() + File.separator + storeName; @@ -60,11 +61,6 @@ public DataStorageService(PersistenceService persistenceService, String storeNam public void shutdown() { } - @Override - protected long getMaxWriteRateInMs() { - return 1000; - } - @Override public DataStore prunePersisted(DataStore persisted) { Map map = persisted.getMap(); From d79cf3fc386109168ee64d51ba6e0bde3b7b89bf Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 12:07:02 +0700 Subject: [PATCH 087/119] Fix incorrect instanceof check --- .../main/java/bisq/oracle_node/timestamp/TimestampService.java | 3 +-- .../bonded_roles/bonded_role/AuthorizedBondedRolesService.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/oracle-node/oracle-node/src/main/java/bisq/oracle_node/timestamp/TimestampService.java b/apps/oracle-node/oracle-node/src/main/java/bisq/oracle_node/timestamp/TimestampService.java index cffa5b437f..9c06c8ebc8 100644 --- a/apps/oracle-node/oracle-node/src/main/java/bisq/oracle_node/timestamp/TimestampService.java +++ b/apps/oracle-node/oracle-node/src/main/java/bisq/oracle_node/timestamp/TimestampService.java @@ -19,7 +19,6 @@ import bisq.bonded_roles.BondedRoleType; import bisq.bonded_roles.bonded_role.AuthorizedBondedRolesService; -import bisq.bonded_roles.release.ReleaseNotification; import bisq.common.application.Service; import bisq.identity.Identity; import bisq.network.NetworkService; @@ -114,7 +113,7 @@ public void onMessage(EnvelopePayloadMessage envelopePayloadMessage) { @Override public void onAuthorizedDataAdded(AuthorizedData authorizedData) { - if (authorizedData.getAuthorizedDistributedData() instanceof ReleaseNotification) { + if (authorizedData.getAuthorizedDistributedData() instanceof AuthorizedTimestampData) { if (isAuthorized(authorizedData)) { AuthorizedTimestampData authorizedTimestampData = (AuthorizedTimestampData) authorizedData.getAuthorizedDistributedData(); // We might get data published from other oracle nodes and put it into our local store. diff --git a/bonded-roles/src/main/java/bisq/bonded_roles/bonded_role/AuthorizedBondedRolesService.java b/bonded-roles/src/main/java/bisq/bonded_roles/bonded_role/AuthorizedBondedRolesService.java index 0274162c41..a7570dffa4 100644 --- a/bonded-roles/src/main/java/bisq/bonded_roles/bonded_role/AuthorizedBondedRolesService.java +++ b/bonded-roles/src/main/java/bisq/bonded_roles/bonded_role/AuthorizedBondedRolesService.java @@ -180,7 +180,7 @@ public void onAuthorizedDataAdded(AuthorizedData authorizedData) { try { listener.onAuthorizedDataAdded(authorizedData); } catch (Exception e) { - log.error("Error at onAuthorizedDataAdded", e); + log.error("Error at listener.onAuthorizedDataAdded. listener={}", listener, e); } }); } From a1f0bf92a02780c07406dd340840330037614c39 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 12:40:36 +0700 Subject: [PATCH 088/119] Truncate log output --- .../authorization/token/hash_cash/HashCashTokenService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/network/network/src/main/java/bisq/network/p2p/node/authorization/token/hash_cash/HashCashTokenService.java b/network/network/src/main/java/bisq/network/p2p/node/authorization/token/hash_cash/HashCashTokenService.java index d86917848e..3bd19ba836 100644 --- a/network/network/src/main/java/bisq/network/p2p/node/authorization/token/hash_cash/HashCashTokenService.java +++ b/network/network/src/main/java/bisq/network/p2p/node/authorization/token/hash_cash/HashCashTokenService.java @@ -4,6 +4,7 @@ import bisq.common.encoding.Hex; import bisq.common.util.ByteArrayUtils; import bisq.common.util.MathUtils; +import bisq.common.util.StringUtils; import bisq.network.p2p.message.EnvelopePayloadMessage; import bisq.network.p2p.node.authorization.AuthorizationToken; import bisq.network.p2p.node.authorization.AuthorizationTokenService; @@ -117,7 +118,7 @@ public boolean isAuthorized(EnvelopePayloadMessage message, log.warn("Message payload not matching proof of work payload. " + "getPayload(message)={}; proofOfWork.getPayload()={}; " + "getPayload(message).length={}; proofOfWork.getPayload().length={}", - Hex.encode(payload), Hex.encode(proofOfWork.getPayload()), + StringUtils.truncate(Hex.encode(payload), 200), StringUtils.truncate(Hex.encode(proofOfWork.getPayload()), 200), payload.length, proofOfWork.getPayload().length); return false; } From b872fe19d08cf3e11dd28cc87b4785ac6dcc5f64 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 14:41:21 +0700 Subject: [PATCH 089/119] Exclude that baseCurrencyName and quoteCurrencyName from EqualsAndHashCode. --- common/src/main/java/bisq/common/currency/Market.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/src/main/java/bisq/common/currency/Market.java b/common/src/main/java/bisq/common/currency/Market.java index b0cd99f8e6..5822048875 100644 --- a/common/src/main/java/bisq/common/currency/Market.java +++ b/common/src/main/java/bisq/common/currency/Market.java @@ -22,8 +22,10 @@ import bisq.common.validation.NetworkDataValidation; import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; +@Slf4j @Getter @EqualsAndHashCode public final class Market implements NetworkProto, PersistableProto, Comparable { @@ -32,7 +34,9 @@ public final class Market implements NetworkProto, PersistableProto, Comparable< private final String baseCurrencyCode; private final String quoteCurrencyCode; + @EqualsAndHashCode.Exclude private final String baseCurrencyName; + @EqualsAndHashCode.Exclude private final String quoteCurrencyName; public Market(String baseCurrencyCode, From ccdc29ab12f1da104296466212200ec11c7b600e Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 14:41:46 +0700 Subject: [PATCH 090/119] Add warn log if price quote is not present --- .../take_offer/amount/TakeOfferAmountController.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/take_offer/amount/TakeOfferAmountController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/take_offer/amount/TakeOfferAmountController.java index d33721ba0c..c587959971 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/take_offer/amount/TakeOfferAmountController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/take_offer/amount/TakeOfferAmountController.java @@ -20,6 +20,7 @@ import bisq.bonded_roles.market_price.MarketPriceService; import bisq.common.currency.Market; import bisq.common.monetary.Monetary; +import bisq.common.monetary.PriceQuote; import bisq.desktop.ServiceProvider; import bisq.desktop.common.view.Controller; import bisq.desktop.main.content.bisq_easy.components.AmountComponent; @@ -101,7 +102,13 @@ public void init(BisqEasyOffer bisqEasyOffer, Optional takersAmountS public void setTradePriceSpec(PriceSpec priceSpec) { // priceSpec from price view in case we are the seller if (priceSpec != null && model.getBisqEasyOffer() != null && model.getBisqEasyOffer().getTakersDirection().isSell()) { - PriceUtil.findQuote(marketPriceService, priceSpec, model.getBisqEasyOffer().getMarket()).ifPresent(amountComponent::setQuote); + Market market = model.getBisqEasyOffer().getMarket(); + Optional priceQuote = PriceUtil.findQuote(marketPriceService, priceSpec, market); + if (priceQuote.isPresent()) { + amountComponent.setQuote(priceQuote.get()); + } else { + log.warn("Could not find price quote for market {}", market); + } } } From affae4ebaad00adeff787e37af06453294d50a32 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 16:53:29 +0700 Subject: [PATCH 091/119] Refactor: Rename package --- .../authorized_role/mediator/MediatorController.java | 2 +- .../bisq_easy/offerbook/BisqEasyOfferbookView.java | 7 +++++-- .../main/content/bisq_easy/offerbook/Filters.java | 2 +- .../content/bisq_easy/offerbook/MyOfferMessageBox.java | 6 +++--- .../bisq_easy/offerbook/PeerOfferMessageBox.java | 6 +++--- .../bisq_easy/open_trades/MyProtocolLogMessageBox.java | 4 ++-- .../open_trades/PeerProtocolLogMessageBox.java | 4 ++-- .../desktop/main/content/chat/BaseChatController.java | 2 +- .../ChatMessageListCellFactory.java | 10 +++++----- .../ChatMessageListItem.java | 4 ++-- .../ChatMessagesComponent.java | 2 +- .../ChatMessagesListView.java | 3 +-- .../messages/BubbleMessageBox.java | 6 +++--- .../messages/LeaveChatMessageBox.java | 6 +++--- .../messages/MessageBox.java | 2 +- .../messages/MyMessageBox.java | 6 +++--- .../messages/PeerMessageBox.java | 6 +++--- 17 files changed, 40 insertions(+), 38 deletions(-) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/{chatMessages => chat_messages}/ChatMessageListCellFactory.java (94%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/{chatMessages => chat_messages}/ChatMessageListItem.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/{chatMessages => chat_messages}/ChatMessagesComponent.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/{chatMessages => chat_messages}/ChatMessagesListView.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/{chatMessages => chat_messages}/messages/BubbleMessageBox.java (97%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/{chatMessages => chat_messages}/messages/LeaveChatMessageBox.java (87%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/{chatMessages => chat_messages}/messages/MessageBox.java (92%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/{chatMessages => chat_messages}/messages/MyMessageBox.java (97%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/{chatMessages => chat_messages}/messages/PeerMessageBox.java (96%) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorController.java index 1c4d13b90e..54a9ca80cd 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorController.java @@ -28,7 +28,7 @@ import bisq.desktop.common.observable.FxBindings; import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Controller; -import bisq.desktop.main.content.components.chatMessages.ChatMessagesComponent; +import bisq.desktop.main.content.components.chat_messages.ChatMessagesComponent; import bisq.support.mediation.MediationCase; import bisq.support.mediation.MediationRequest; import bisq.support.mediation.MediatorService; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java index 993c9301e0..8a9575ff02 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java @@ -29,7 +29,7 @@ import bisq.desktop.components.table.BisqTableColumn; import bisq.desktop.components.table.BisqTableView; import bisq.desktop.main.content.chat.ChatView; -import bisq.desktop.main.content.components.chatMessages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; import bisq.i18n.Res; import javafx.beans.binding.Bindings; import javafx.collections.ListChangeListener; @@ -43,7 +43,10 @@ import javafx.scene.control.Label; import javafx.scene.control.SeparatorMenuItem; import javafx.scene.image.ImageView; -import javafx.scene.layout.*; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.fxmisc.easybind.EasyBind; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java index b64138e82b..8458d41261 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java @@ -19,7 +19,7 @@ import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; -import bisq.desktop.main.content.components.chatMessages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; import lombok.Getter; import java.util.function.Predicate; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java index b418c66bff..71af2f89f8 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java @@ -22,9 +22,9 @@ import bisq.desktop.components.containers.Spacer; import bisq.desktop.components.controls.DropdownMenu; import bisq.desktop.components.controls.DropdownMenuItem; -import bisq.desktop.main.content.components.chatMessages.ChatMessageListItem; -import bisq.desktop.main.content.components.chatMessages.ChatMessagesListView; -import bisq.desktop.main.content.components.chatMessages.messages.BubbleMessageBox; +import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.messages.BubbleMessageBox; import bisq.i18n.Res; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java index 84b9cd2b8a..0e6f1af1f7 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java @@ -21,9 +21,9 @@ import bisq.chat.ChatMessage; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; import bisq.desktop.components.containers.Spacer; -import bisq.desktop.main.content.components.chatMessages.ChatMessageListItem; -import bisq.desktop.main.content.components.chatMessages.ChatMessagesListView; -import bisq.desktop.main.content.components.chatMessages.messages.PeerMessageBox; +import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.messages.PeerMessageBox; import bisq.i18n.Res; import javafx.geometry.Insets; import javafx.geometry.Pos; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java index fe3f1d5941..20872849c1 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java @@ -21,8 +21,8 @@ import bisq.chat.ChatMessage; import bisq.desktop.common.Icons; import bisq.desktop.components.controls.BisqTooltip; -import bisq.desktop.main.content.components.chatMessages.ChatMessageListItem; -import bisq.desktop.main.content.components.chatMessages.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.ChatMessagesListView; import bisq.network.p2p.services.confidential.ack.MessageDeliveryStatus; import de.jensd.fx.fontawesome.AwesomeDude; import javafx.geometry.Pos; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java index b91e846879..039c9d1f14 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java @@ -19,8 +19,8 @@ import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; -import bisq.desktop.main.content.components.chatMessages.ChatMessageListItem; -import bisq.desktop.main.content.components.chatMessages.messages.MessageBox; +import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.messages.MessageBox; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Label; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/BaseChatController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/BaseChatController.java index 4b1a8dae08..f55758408e 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/BaseChatController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/BaseChatController.java @@ -34,7 +34,7 @@ import bisq.desktop.components.controls.BisqIconButton; import bisq.desktop.main.content.chat.sidebar.ChannelSidebar; import bisq.desktop.main.content.chat.sidebar.UserProfileSidebar; -import bisq.desktop.main.content.components.chatMessages.ChatMessagesComponent; +import bisq.desktop.main.content.components.chat_messages.ChatMessagesComponent; import bisq.i18n.Res; import bisq.user.identity.UserIdentityService; import bisq.user.profile.UserProfile; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessageListCellFactory.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageListCellFactory.java similarity index 94% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessageListCellFactory.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageListCellFactory.java index dcfb64faf7..8ddad4b38a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessageListCellFactory.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageListCellFactory.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chatMessages; +package bisq.desktop.main.content.components.chat_messages; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -23,10 +23,10 @@ import bisq.desktop.main.content.bisq_easy.offerbook.PeerOfferMessageBox; import bisq.desktop.main.content.bisq_easy.open_trades.MyProtocolLogMessageBox; import bisq.desktop.main.content.bisq_easy.open_trades.PeerProtocolLogMessageBox; -import bisq.desktop.main.content.components.chatMessages.messages.LeaveChatMessageBox; -import bisq.desktop.main.content.components.chatMessages.messages.MessageBox; -import bisq.desktop.main.content.components.chatMessages.messages.MyMessageBox; -import bisq.desktop.main.content.components.chatMessages.messages.PeerMessageBox; +import bisq.desktop.main.content.components.chat_messages.messages.LeaveChatMessageBox; +import bisq.desktop.main.content.components.chat_messages.messages.MessageBox; +import bisq.desktop.main.content.components.chat_messages.messages.MyMessageBox; +import bisq.desktop.main.content.components.chat_messages.messages.PeerMessageBox; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessageListItem.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageListItem.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessageListItem.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageListItem.java index e85c2b4cb4..fe20ae5afc 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessageListItem.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageListItem.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chatMessages; +package bisq.desktop.main.content.components.chat_messages; import bisq.bonded_roles.market_price.MarketPriceService; import bisq.chat.ChatChannel; @@ -67,7 +67,7 @@ import static bisq.chat.ChatMessageType.LEAVE; import static bisq.chat.ChatMessageType.PROTOCOL_LOG_MESSAGE; -import static bisq.desktop.main.content.components.chatMessages.ChatMessagesComponent.View.EDITED_POST_FIX; +import static bisq.desktop.main.content.components.chat_messages.ChatMessagesComponent.View.EDITED_POST_FIX; @Slf4j @Getter diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesComponent.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesComponent.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java index fbb5cd27aa..0c5173d267 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesComponent.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chatMessages; +package bisq.desktop.main.content.components.chat_messages; import bisq.bisq_easy.NavigationTarget; import bisq.chat.*; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesListView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesListView.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesListView.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesListView.java index 1e025d50fd..c057a3d8b0 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/ChatMessagesListView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesListView.java @@ -15,12 +15,11 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chatMessages; +package bisq.desktop.main.content.components.chat_messages; import bisq.bisq_easy.BisqEasyService; import bisq.bisq_easy.NavigationTarget; import bisq.bonded_roles.market_price.MarketPriceService; -import bisq.bonded_roles.market_price.MarketPriceService; import bisq.chat.*; import bisq.chat.bisqeasy.BisqEasyOfferMessage; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannel; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/messages/BubbleMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/BubbleMessageBox.java similarity index 97% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/messages/BubbleMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/BubbleMessageBox.java index 5817adf034..fc5654a903 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/messages/BubbleMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/BubbleMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chatMessages.messages; +package bisq.desktop.main.content.components.chat_messages.messages; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -25,8 +25,8 @@ import bisq.desktop.common.utils.ClipboardUtil; import bisq.desktop.components.controls.BisqTooltip; import bisq.desktop.main.content.components.UserProfileIcon; -import bisq.desktop.main.content.components.chatMessages.ChatMessageListItem; -import bisq.desktop.main.content.components.chatMessages.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.ChatMessagesListView; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; import javafx.geometry.Pos; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/messages/LeaveChatMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/LeaveChatMessageBox.java similarity index 87% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/messages/LeaveChatMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/LeaveChatMessageBox.java index 1ec0f6e4de..7354b2bf42 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/messages/LeaveChatMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/LeaveChatMessageBox.java @@ -15,14 +15,14 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chatMessages.messages; +package bisq.desktop.main.content.components.chat_messages.messages; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; import bisq.desktop.common.utils.ImageUtil; import bisq.desktop.main.content.bisq_easy.open_trades.MyProtocolLogMessageBox; -import bisq.desktop.main.content.components.chatMessages.ChatMessageListItem; -import bisq.desktop.main.content.components.chatMessages.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.ChatMessagesListView; import bisq.i18n.Res; import javafx.scene.control.Hyperlink; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/messages/MessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MessageBox.java similarity index 92% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/messages/MessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MessageBox.java index 1637b84225..00a3a80e57 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/messages/MessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chatMessages.messages; +package bisq.desktop.main.content.components.chat_messages.messages; import javafx.scene.layout.VBox; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/messages/MyMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MyMessageBox.java similarity index 97% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/messages/MyMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MyMessageBox.java index 2d596e58f1..e764001b5d 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/messages/MyMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MyMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chatMessages.messages; +package bisq.desktop.main.content.components.chat_messages.messages; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -24,8 +24,8 @@ import bisq.desktop.components.containers.Spacer; import bisq.desktop.components.controls.BisqTextArea; import bisq.desktop.components.controls.BisqTooltip; -import bisq.desktop.main.content.components.chatMessages.ChatMessageListItem; -import bisq.desktop.main.content.components.chatMessages.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.ChatMessagesListView; import bisq.i18n.Res; import bisq.network.p2p.services.confidential.ack.MessageDeliveryStatus; import de.jensd.fx.fontawesome.AwesomeDude; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/messages/PeerMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/PeerMessageBox.java similarity index 96% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/messages/PeerMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/PeerMessageBox.java index 183d06156b..5dfc0a911d 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chatMessages/messages/PeerMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/PeerMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chatMessages.messages; +package bisq.desktop.main.content.components.chat_messages.messages; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -24,8 +24,8 @@ import bisq.desktop.components.controls.BisqPopup; import bisq.desktop.components.controls.BisqPopupMenu; import bisq.desktop.components.controls.BisqPopupMenuItem; -import bisq.desktop.main.content.components.chatMessages.ChatMessageListItem; -import bisq.desktop.main.content.components.chatMessages.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.ChatMessagesListView; import bisq.i18n.Res; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; From 5435b6b312578c0ecd83bd838b3b9f582982c176 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 16:54:28 +0700 Subject: [PATCH 092/119] Refactor: Move classes to new package --- .../main/content/bisq_easy/offerbook/MyOfferMessageBox.java | 2 +- .../main/content/bisq_easy/offerbook/PeerOfferMessageBox.java | 2 +- .../content/bisq_easy/open_trades/MyProtocolLogMessageBox.java | 2 +- .../components/chat_messages/ChatMessagesComponent.java | 1 + .../{ => list_view}/ChatMessageListCellFactory.java | 3 ++- .../chat_messages/{ => list_view}/ChatMessagesListView.java | 3 ++- .../components/chat_messages/messages/BubbleMessageBox.java | 2 +- .../components/chat_messages/messages/LeaveChatMessageBox.java | 2 +- .../components/chat_messages/messages/MyMessageBox.java | 2 +- .../components/chat_messages/messages/PeerMessageBox.java | 2 +- 10 files changed, 12 insertions(+), 9 deletions(-) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/{ => list_view}/ChatMessageListCellFactory.java (97%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/{ => list_view}/ChatMessagesListView.java (99%) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java index 71af2f89f8..3f867a71cb 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java @@ -23,7 +23,7 @@ import bisq.desktop.components.controls.DropdownMenu; import bisq.desktop.components.controls.DropdownMenuItem; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListView; import bisq.desktop.main.content.components.chat_messages.messages.BubbleMessageBox; import bisq.i18n.Res; import de.jensd.fx.fontawesome.AwesomeIcon; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java index 0e6f1af1f7..bcc9f84566 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java @@ -22,7 +22,7 @@ import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; import bisq.desktop.components.containers.Spacer; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListView; import bisq.desktop.main.content.components.chat_messages.messages.PeerMessageBox; import bisq.i18n.Res; import javafx.geometry.Insets; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java index 20872849c1..9fb8de9d7a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java @@ -22,7 +22,7 @@ import bisq.desktop.common.Icons; import bisq.desktop.components.controls.BisqTooltip; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListView; import bisq.network.p2p.services.confidential.ack.MessageDeliveryStatus; import de.jensd.fx.fontawesome.AwesomeDude; import javafx.geometry.Pos; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java index 0c5173d267..78c623e339 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java @@ -37,6 +37,7 @@ import bisq.desktop.main.content.components.ChatMentionPopupMenu; import bisq.desktop.main.content.components.CitationBlock; import bisq.desktop.main.content.components.UserProfileSelection; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListView; import bisq.i18n.Res; import bisq.settings.SettingsService; import bisq.user.identity.UserIdentity; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageListCellFactory.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java similarity index 97% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageListCellFactory.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java index 8ddad4b38a..a7150b357c 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageListCellFactory.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages; +package bisq.desktop.main.content.components.chat_messages.list_view; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -23,6 +23,7 @@ import bisq.desktop.main.content.bisq_easy.offerbook.PeerOfferMessageBox; import bisq.desktop.main.content.bisq_easy.open_trades.MyProtocolLogMessageBox; import bisq.desktop.main.content.bisq_easy.open_trades.PeerProtocolLogMessageBox; +import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; import bisq.desktop.main.content.components.chat_messages.messages.LeaveChatMessageBox; import bisq.desktop.main.content.components.chat_messages.messages.MessageBox; import bisq.desktop.main.content.components.chat_messages.messages.MyMessageBox; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesListView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListView.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesListView.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListView.java index c057a3d8b0..c4bdc06d8c 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesListView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListView.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages; +package bisq.desktop.main.content.components.chat_messages.list_view; import bisq.bisq_easy.BisqEasyService; import bisq.bisq_easy.NavigationTarget; @@ -52,6 +52,7 @@ import bisq.desktop.main.content.bisq_easy.take_offer.TakeOfferController; import bisq.desktop.main.content.chat.ChatUtil; import bisq.desktop.main.content.components.ReportToModeratorWindow; +import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; import bisq.i18n.Res; import bisq.network.NetworkService; import bisq.network.identity.NetworkId; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/BubbleMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/BubbleMessageBox.java index fc5654a903..50e3b3fe81 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/BubbleMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/BubbleMessageBox.java @@ -26,7 +26,7 @@ import bisq.desktop.components.controls.BisqTooltip; import bisq.desktop.main.content.components.UserProfileIcon; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListView; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; import javafx.geometry.Pos; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/LeaveChatMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/LeaveChatMessageBox.java index 7354b2bf42..c18823d5f1 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/LeaveChatMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/LeaveChatMessageBox.java @@ -22,7 +22,7 @@ import bisq.desktop.common.utils.ImageUtil; import bisq.desktop.main.content.bisq_easy.open_trades.MyProtocolLogMessageBox; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListView; import bisq.i18n.Res; import javafx.scene.control.Hyperlink; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MyMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MyMessageBox.java index e764001b5d..63e9a20475 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MyMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MyMessageBox.java @@ -25,7 +25,7 @@ import bisq.desktop.components.controls.BisqTextArea; import bisq.desktop.components.controls.BisqTooltip; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListView; import bisq.i18n.Res; import bisq.network.p2p.services.confidential.ack.MessageDeliveryStatus; import de.jensd.fx.fontawesome.AwesomeDude; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/PeerMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/PeerMessageBox.java index 5dfc0a911d..b82a7e986a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/PeerMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/PeerMessageBox.java @@ -25,7 +25,7 @@ import bisq.desktop.components.controls.BisqPopupMenu; import bisq.desktop.components.controls.BisqPopupMenuItem; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListView; import bisq.i18n.Res; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; From 24ebc5478777f96314e5e5b4b4458f5c69a537ed Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 17:33:23 +0700 Subject: [PATCH 093/119] Refactor: Move inner MCV classes out as the component has become too large. --- .../offerbook/MyOfferMessageBox.java | 5 +- .../offerbook/PeerOfferMessageBox.java | 5 +- .../open_trades/MyProtocolLogMessageBox.java | 4 +- .../chat_messages/ChatMessagesComponent.java | 16 +- .../list_view/ChatMessageListCellFactory.java | 6 +- .../list_view/ChatMessagesListController.java | 642 ++++++++++++ .../list_view/ChatMessagesListModel.java | 53 + .../list_view/ChatMessagesListView.java | 978 +++--------------- .../messages/BubbleMessageBox.java | 11 +- .../messages/LeaveChatMessageBox.java | 4 +- .../chat_messages/messages/MyMessageBox.java | 5 +- .../messages/PeerMessageBox.java | 5 +- 12 files changed, 856 insertions(+), 878 deletions(-) create mode 100644 apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListController.java create mode 100644 apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListModel.java diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java index 3f867a71cb..3890422d92 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java @@ -23,7 +23,8 @@ import bisq.desktop.components.controls.DropdownMenu; import bisq.desktop.components.controls.DropdownMenuItem; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; import bisq.desktop.main.content.components.chat_messages.messages.BubbleMessageBox; import bisq.i18n.Res; import de.jensd.fx.fontawesome.AwesomeIcon; @@ -41,7 +42,7 @@ public final class MyOfferMessageBox extends BubbleMessageBox { public MyOfferMessageBox(ChatMessageListItem> item, ListView>> list, - ChatMessagesListView.Controller controller, ChatMessagesListView.Model model) { + ChatMessagesListController controller, ChatMessagesListModel model) { super(item, list, controller, model); // Message diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java index bcc9f84566..e938e2d4bd 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java @@ -22,7 +22,8 @@ import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; import bisq.desktop.components.containers.Spacer; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; import bisq.desktop.main.content.components.chat_messages.messages.PeerMessageBox; import bisq.i18n.Res; import javafx.geometry.Insets; @@ -39,7 +40,7 @@ public final class PeerOfferMessageBox extends PeerMessageBox { public PeerOfferMessageBox(ChatMessageListItem> item, ListView>> list, - ChatMessagesListView.Controller controller, ChatMessagesListView.Model model) { + ChatMessagesListController controller, ChatMessagesListModel model) { super(item, list, controller, model); reactionsHBox.getChildren().setAll(replyIcon, pmIcon, moreOptionsIcon, supportedLanguages, Spacer.fillHBox()); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java index 9fb8de9d7a..29d5168a8d 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java @@ -22,7 +22,7 @@ import bisq.desktop.common.Icons; import bisq.desktop.components.controls.BisqTooltip; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; import bisq.network.p2p.services.confidential.ack.MessageDeliveryStatus; import de.jensd.fx.fontawesome.AwesomeDude; import javafx.geometry.Pos; @@ -37,7 +37,7 @@ public class MyProtocolLogMessageBox extends PeerProtocolLogMessageBox { private final Subscription messageDeliveryStatusIconPin; public MyProtocolLogMessageBox(ChatMessageListItem> item, - ChatMessagesListView.Controller controller) { + ChatMessagesListController controller) { super(item); deliveryState = new Label(); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java index 78c623e339..3e5759bdca 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java @@ -37,7 +37,7 @@ import bisq.desktop.main.content.components.ChatMentionPopupMenu; import bisq.desktop.main.content.components.CitationBlock; import bisq.desktop.main.content.components.UserProfileSelection; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; import bisq.i18n.Res; import bisq.settings.SettingsService; import bisq.user.identity.UserIdentity; @@ -94,15 +94,15 @@ public void mentionUser(UserProfile userProfile) { } public void setSearchPredicate(Predicate>> predicate) { - controller.chatMessagesListView.setSearchPredicate(predicate); + controller.chatMessagesListController.setSearchPredicate(predicate); } public void setBisqEasyOfferDirectionOrOwnerFilterPredicate(Predicate>> predicate) { - controller.chatMessagesListView.setBisqEasyOfferDirectionOrOwnerFilterPredicate(predicate); + controller.chatMessagesListController.setBisqEasyOfferDirectionOrOwnerFilterPredicate(predicate); } public void setBisqEasyPeerReputationFilterPredicate(Predicate>> predicate) { - controller.chatMessagesListView.setBisqEasyPeerReputationFilterPredicate(predicate); + controller.chatMessagesListController.setBisqEasyPeerReputationFilterPredicate(predicate); } public void resetSelectedChatMessage() { @@ -114,7 +114,7 @@ public void createAndSelectTwoPartyPrivateChatChannel(UserProfile peer) { } public void refreshMessages() { - controller.chatMessagesListView.refreshMessages(); + controller.chatMessagesListController.refreshMessages(); } public void enableChatDialog(boolean isEnabled) { @@ -128,7 +128,7 @@ private static class Controller implements bisq.desktop.common.view.Controller { private final Consumer openUserProfileSidebarHandler; private final UserIdentityService userIdentityService; private final CitationBlock citationBlock; - private final ChatMessagesListView chatMessagesListView; + private final ChatMessagesListController chatMessagesListController; private final UserProfileService userProfileService; private final SettingsService settingsService; private final ChatService chatService; @@ -149,7 +149,7 @@ private Controller(ServiceProvider serviceProvider, UserProfileSelection userProfileSelection = new UserProfileSelection(serviceProvider); - chatMessagesListView = new ChatMessagesListView(serviceProvider, + chatMessagesListController = new ChatMessagesListController(serviceProvider, this::mentionUserHandler, this::showChatUserDetailsHandler, this::replyHandler, @@ -157,7 +157,7 @@ private Controller(ServiceProvider serviceProvider, model = new Model(chatChannelDomain, chatService); view = new View(model, this, - chatMessagesListView.getRoot(), + chatMessagesListController.getView().getRoot(), citationBlock.getRoot(), userProfileSelection); } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java index a7150b357c..0a5bbf7e6b 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java @@ -41,10 +41,10 @@ final class ChatMessageListCellFactory implements Callback>>, ListCell>>> { - private final ChatMessagesListView.Controller controller; - private final ChatMessagesListView.Model model; + private final ChatMessagesListController controller; + private final ChatMessagesListModel model; - public ChatMessageListCellFactory(ChatMessagesListView.Controller controller, ChatMessagesListView.Model model) { + public ChatMessageListCellFactory(ChatMessagesListController controller, ChatMessagesListModel model) { this.controller = controller; this.model = model; } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListController.java new file mode 100644 index 0000000000..f8bd923396 --- /dev/null +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListController.java @@ -0,0 +1,642 @@ +package bisq.desktop.main.content.components.chat_messages.list_view; + +import bisq.bisq_easy.BisqEasyService; +import bisq.bisq_easy.NavigationTarget; +import bisq.bonded_roles.market_price.MarketPriceService; +import bisq.chat.*; +import bisq.chat.bisqeasy.BisqEasyOfferMessage; +import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannel; +import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; +import bisq.chat.bisqeasy.open_trades.BisqEasyOpenTradeChannel; +import bisq.chat.common.CommonPublicChatChannel; +import bisq.chat.common.CommonPublicChatChannelService; +import bisq.chat.common.CommonPublicChatMessage; +import bisq.chat.notifications.ChatNotificationService; +import bisq.chat.priv.PrivateChatChannel; +import bisq.chat.priv.PrivateChatChannelService; +import bisq.chat.priv.PrivateChatMessage; +import bisq.chat.pub.PublicChatChannel; +import bisq.chat.pub.PublicChatMessage; +import bisq.chat.two_party.TwoPartyPrivateChatChannel; +import bisq.common.observable.Pin; +import bisq.common.observable.collection.CollectionObserver; +import bisq.desktop.ServiceProvider; +import bisq.desktop.common.observable.FxBindings; +import bisq.desktop.common.threading.UIScheduler; +import bisq.desktop.common.threading.UIThread; +import bisq.desktop.common.utils.ClipboardUtil; +import bisq.desktop.common.view.Navigation; +import bisq.desktop.components.controls.BisqPopup; +import bisq.desktop.components.controls.BisqPopupMenu; +import bisq.desktop.components.controls.BisqPopupMenuItem; +import bisq.desktop.components.overlay.Popup; +import bisq.desktop.main.content.bisq_easy.BisqEasyServiceUtil; +import bisq.desktop.main.content.bisq_easy.take_offer.TakeOfferController; +import bisq.desktop.main.content.components.ReportToModeratorWindow; +import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.i18n.Res; +import bisq.network.NetworkService; +import bisq.network.identity.NetworkId; +import bisq.network.p2p.services.confidential.resend.ResendMessageService; +import bisq.offer.bisq_easy.BisqEasyOffer; +import bisq.offer.options.OfferOptionUtil; +import bisq.settings.SettingsService; +import bisq.trade.Trade; +import bisq.trade.bisq_easy.BisqEasyTradeService; +import bisq.user.banned.BannedUserService; +import bisq.user.identity.UserIdentity; +import bisq.user.identity.UserIdentityService; +import bisq.user.profile.UserProfile; +import bisq.user.profile.UserProfileService; +import bisq.user.reputation.ReputationScore; +import bisq.user.reputation.ReputationService; +import javafx.scene.Node; +import javafx.scene.Scene; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.fxmisc.easybind.EasyBind; +import org.fxmisc.easybind.Subscription; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +@Slf4j +public class ChatMessagesListController implements bisq.desktop.common.view.Controller { + private final ChatService chatService; + private final UserIdentityService userIdentityService; + private final UserProfileService userProfileService; + private final ReputationService reputationService; + private final SettingsService settingsService; + private final Consumer mentionUserHandler; + private final Consumer replyHandler; + private final Consumer showChatUserDetailsHandler; + private final ChatMessagesListModel model; + @Getter + private final ChatMessagesListView view; + private final ChatNotificationService chatNotificationService; + private final BisqEasyTradeService bisqEasyTradeService; + private final BannedUserService bannedUserService; + private final NetworkService networkService; + private final Optional resendMessageService; + private final BisqEasyService bisqEasyService; + private final MarketPriceService marketPriceService; + private Pin selectedChannelPin, chatMessagesPin, offerOnlySettingsPin; + private Subscription selectedChannelSubscription, focusSubscription, scrollValuePin, scrollBarVisiblePin; + + public ChatMessagesListController(ServiceProvider serviceProvider, + Consumer mentionUserHandler, + Consumer showChatUserDetailsHandler, + Consumer replyHandler, + ChatChannelDomain chatChannelDomain) { + chatService = serviceProvider.getChatService(); + chatNotificationService = chatService.getChatNotificationService(); + userIdentityService = serviceProvider.getUserService().getUserIdentityService(); + userProfileService = serviceProvider.getUserService().getUserProfileService(); + reputationService = serviceProvider.getUserService().getReputationService(); + settingsService = serviceProvider.getSettingsService(); + bisqEasyService = serviceProvider.getBisqEasyService(); + bisqEasyTradeService = serviceProvider.getTradeService().getBisqEasyTradeService(); + bannedUserService = serviceProvider.getUserService().getBannedUserService(); + networkService = serviceProvider.getNetworkService(); + resendMessageService = serviceProvider.getNetworkService().getResendMessageService(); + marketPriceService = serviceProvider.getBondedRolesService().getMarketPriceService(); + this.mentionUserHandler = mentionUserHandler; + this.showChatUserDetailsHandler = showChatUserDetailsHandler; + this.replyHandler = replyHandler; + + model = new ChatMessagesListModel(userIdentityService, chatChannelDomain); + view = new ChatMessagesListView(model, this); + } + + @Override + public void onActivate() { + model.getSortedChatMessages().setComparator(ChatMessageListItem::compareTo); + + offerOnlySettingsPin = FxBindings.subscribe(settingsService.getOffersOnly(), offerOnly -> UIThread.run(this::applyPredicate)); + + if (selectedChannelPin != null) { + selectedChannelPin.unbind(); + } + + ChatChannelSelectionService selectionService = chatService.getChatChannelSelectionServices().get(model.getChatChannelDomain()); + + selectedChannelPin = selectionService.getSelectedChannel().addObserver(this::selectedChannelChanged); + + scrollValuePin = EasyBind.subscribe(model.getScrollValue(), scrollValue -> { + if (scrollValue != null) { + applyScrollValue(scrollValue.doubleValue()); + } + }); + + scrollBarVisiblePin = EasyBind.subscribe(model.getScrollBarVisible(), scrollBarVisible -> { + if (scrollBarVisible != null && scrollBarVisible) { + applyScrollValue(1); + } + }); + + applyScrollValue(1); + } + + @Override + public void onDeactivate() { + if (offerOnlySettingsPin != null) { + offerOnlySettingsPin.unbind(); + } + if (selectedChannelPin != null) { + selectedChannelPin.unbind(); + selectedChannelPin = null; + } + if (chatMessagesPin != null) { + chatMessagesPin.unbind(); + chatMessagesPin = null; + } + if (focusSubscription != null) { + focusSubscription.unsubscribe(); + } + if (selectedChannelSubscription != null) { + selectedChannelSubscription.unsubscribe(); + } + + scrollValuePin.unsubscribe(); + scrollBarVisiblePin.unsubscribe(); + + model.getChatMessages().forEach(ChatMessageListItem::dispose); + model.getChatMessages().clear(); + } + + private void selectedChannelChanged(ChatChannel channel) { + UIThread.run(() -> { + model.getSelectedChannel().set(channel); + model.getIsPublicChannel().set(channel instanceof PublicChatChannel); + + if (chatMessagesPin != null) { + chatMessagesPin.unbind(); + } + + // Clear and call dispose on the current messages when we change the channel. + model.getChatMessages().forEach(ChatMessageListItem::dispose); + model.getChatMessages().clear(); + + if (channel instanceof BisqEasyOfferbookChannel) { + chatMessagesPin = bindChatMessages((BisqEasyOfferbookChannel) channel); + } else if (channel instanceof BisqEasyOpenTradeChannel) { + chatMessagesPin = bindChatMessages((BisqEasyOpenTradeChannel) channel); + } else if (channel instanceof CommonPublicChatChannel) { + chatMessagesPin = bindChatMessages((CommonPublicChatChannel) channel); + } else if (channel instanceof TwoPartyPrivateChatChannel) { + chatMessagesPin = bindChatMessages((TwoPartyPrivateChatChannel) channel); + } + + if (focusSubscription != null) { + focusSubscription.unsubscribe(); + } + if (selectedChannelSubscription != null) { + selectedChannelSubscription.unsubscribe(); + } + if (channel != null) { + Scene scene = view.getRoot().getScene(); + if (scene != null) { + focusSubscription = EasyBind.subscribe(scene.getWindow().focusedProperty(), + focused -> { + if (focused && model.getSelectedChannel().get() != null) { + chatNotificationService.consume(model.getSelectedChannel().get().getId()); + } + }); + } + + selectedChannelSubscription = EasyBind.subscribe(model.getSelectedChannel(), + selectedChannel -> { + if (selectedChannel != null) { + chatNotificationService.consume(model.getSelectedChannel().get().getId()); + } + }); + } + }); + } + + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // API - called from client + /////////////////////////////////////////////////////////////////////////////////////////////////// + + public void refreshMessages() { + model.getChatMessages().setAll(new ArrayList<>(model.getChatMessages())); + } + + public void setSearchPredicate(Predicate>> predicate) { + model.setSearchPredicate(Objects.requireNonNullElseGet(predicate, () -> e -> true)); + applyPredicate(); + } + + public void setBisqEasyOfferDirectionOrOwnerFilterPredicate(Predicate>> predicate) { + model.setBisqEasyOfferDirectionOrOwnerFilterPredicate(Objects.requireNonNullElseGet(predicate, () -> e -> true)); + applyPredicate(); + } + + public void setBisqEasyPeerReputationFilterPredicate(Predicate>> predicate) { + model.setBisqEasyPeerReputationFilterPredicate(Objects.requireNonNullElseGet(predicate, () -> e -> true)); + applyPredicate(); + } + + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // UI - delegate to client + /////////////////////////////////////////////////////////////////////////////////////////////////// + + public void onMention(UserProfile userProfile) { + mentionUserHandler.accept(userProfile); + } + + public void onShowChatUserDetails(ChatMessage chatMessage) { + showChatUserDetailsHandler.accept(chatMessage); + } + + public void onReply(ChatMessage chatMessage) { + replyHandler.accept(chatMessage); + } + + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // UI - handler + /////////////////////////////////////////////////////////////////////////////////////////////////// + + public void onTakeOffer(BisqEasyOfferbookMessage bisqEasyOfferbookMessage) { + checkArgument(bisqEasyOfferbookMessage.getBisqEasyOffer().isPresent(), "message must contain offer"); + checkArgument(userIdentityService.getSelectedUserIdentity() != null, + "userIdentityService.getSelectedUserIdentity() must not be null"); + checkArgument(!model.isMyMessage(bisqEasyOfferbookMessage), "tradeChatMessage must not be mine"); + + UserProfile userProfile = userIdentityService.getSelectedUserIdentity().getUserProfile(); + NetworkId takerNetworkId = userProfile.getNetworkId(); + BisqEasyOffer bisqEasyOffer = bisqEasyOfferbookMessage.getBisqEasyOffer().get(); + String tradeId = Trade.createId(bisqEasyOffer.getId(), takerNetworkId.getId()); + if (bisqEasyTradeService.tradeExists(tradeId)) { + new Popup().information(Res.get("chat.message.offer.offerAlreadyTaken.warn")).show(); + return; + } + + if (!BisqEasyServiceUtil.offerMatchesMinRequiredReputationScore(reputationService, + bisqEasyService, + userIdentityService, + userProfileService, + bisqEasyOffer)) { + if (bisqEasyOffer.getDirection().isSell()) { + long makerAsSellersScore = userProfileService.findUserProfile(bisqEasyOffer.getMakersUserProfileId()) + .map(reputationService::getReputationScore) + .map(ReputationScore::getTotalScore) + .orElse(0L); + long myMinRequiredScore = bisqEasyService.getMinRequiredReputationScore().get(); + new Popup().information(Res.get("chat.message.takeOffer.makersReputationScoreTooLow.warn", + myMinRequiredScore, makerAsSellersScore)).show(); + } else { + long myScoreAsSeller = reputationService.getReputationScore(userIdentityService.getSelectedUserIdentity().getUserProfile()).getTotalScore(); + long offersRequiredScore = OfferOptionUtil.findRequiredTotalReputationScore(bisqEasyOffer).orElse(0L); + new Popup().information(Res.get("chat.message.takeOffer.myReputationScoreTooLow.warn", + offersRequiredScore, myScoreAsSeller)).show(); + } + return; + } + + if (bannedUserService.isUserProfileBanned(bisqEasyOfferbookMessage.getAuthorUserProfileId()) || + bannedUserService.isUserProfileBanned(userProfile)) { + return; + } + + Navigation.navigateTo(NavigationTarget.TAKE_OFFER, new TakeOfferController.InitData(bisqEasyOffer)); + } + + public void onDeleteMessage(ChatMessage chatMessage) { + String authorUserProfileId = chatMessage.getAuthorUserProfileId(); + userIdentityService.findUserIdentity(authorUserProfileId) + .ifPresent(authorUserIdentity -> { + if (authorUserIdentity.equals(userIdentityService.getSelectedUserIdentity())) { + boolean isBisqEasyPublicChatMessageWithOffer = + chatMessage instanceof BisqEasyOfferbookMessage + && ((BisqEasyOfferMessage) chatMessage).hasBisqEasyOffer(); + if (isBisqEasyPublicChatMessageWithOffer) { + new Popup().warning(Res.get("bisqEasy.offerbook.chatMessage.deleteOffer.confirmation")) + .actionButtonText(Res.get("confirmation.yes")) + .onAction(() -> doDeleteMessage(chatMessage, authorUserIdentity)) + .closeButtonText(Res.get("confirmation.no")) + .show(); + } else { + doDeleteMessage(chatMessage, authorUserIdentity); + } + } else { + new Popup().warning(Res.get("chat.message.delete.differentUserProfile.warn")) + .closeButtonText(Res.get("confirmation.no")) + .actionButtonText(Res.get("confirmation.yes")) + .onAction(() -> { + userIdentityService.selectChatUserIdentity(authorUserIdentity); + doDeleteMessage(chatMessage, authorUserIdentity); + }) + .show(); + } + }); + } + + private void doDeleteMessage(ChatMessage chatMessage, UserIdentity userIdentity) { + checkArgument(chatMessage instanceof PublicChatMessage); + + if (chatMessage instanceof BisqEasyOfferbookMessage) { + BisqEasyOfferbookMessage bisqEasyOfferbookMessage = (BisqEasyOfferbookMessage) chatMessage; + chatService.getBisqEasyOfferbookChannelService().deleteChatMessage(bisqEasyOfferbookMessage, userIdentity.getNetworkIdWithKeyPair()) + .whenComplete((result, throwable) -> { + if (throwable != null) { + log.error("We got an error at doDeleteMessage: " + throwable); + } + }); + } else if (chatMessage instanceof CommonPublicChatMessage) { + CommonPublicChatChannelService commonPublicChatChannelService = chatService.getCommonPublicChatChannelServices().get(model.getChatChannelDomain()); + CommonPublicChatMessage commonPublicChatMessage = (CommonPublicChatMessage) chatMessage; + commonPublicChatChannelService.findChannel(chatMessage) + .ifPresent(channel -> commonPublicChatChannelService.deleteChatMessage(commonPublicChatMessage, userIdentity.getNetworkIdWithKeyPair())); + } + } + + public void onOpenPrivateChannel(ChatMessage chatMessage) { + checkArgument(!model.isMyMessage(chatMessage)); + + userProfileService.findUserProfile(chatMessage.getAuthorUserProfileId()) + .ifPresent(this::createAndSelectTwoPartyPrivateChatChannel); + } + + public void onSaveEditedMessage(ChatMessage chatMessage, String editedText) { + checkArgument(chatMessage instanceof PublicChatMessage); + checkArgument(model.isMyMessage(chatMessage)); + + if (editedText.length() > ChatMessage.MAX_TEXT_LENGTH) { + new Popup().warning(Res.get("validation.tooLong", ChatMessage.MAX_TEXT_LENGTH)).show(); + return; + } + + UserIdentity userIdentity = checkNotNull(userIdentityService.getSelectedUserIdentity()); + if (chatMessage instanceof BisqEasyOfferbookMessage) { + BisqEasyOfferbookMessage bisqEasyOfferbookMessage = (BisqEasyOfferbookMessage) chatMessage; + chatService.getBisqEasyOfferbookChannelService().publishEditedChatMessage(bisqEasyOfferbookMessage, editedText, userIdentity); + } else if (chatMessage instanceof CommonPublicChatMessage) { + CommonPublicChatMessage commonPublicChatMessage = (CommonPublicChatMessage) chatMessage; + chatService.getCommonPublicChatChannelServices().get(model.getChatChannelDomain()).publishEditedChatMessage(commonPublicChatMessage, editedText, userIdentity); + } + } + + void onOpenMoreOptions(Node owner, ChatMessage chatMessage, Runnable onClose) { + if (chatMessage.equals(model.getSelectedChatMessageForMoreOptionsPopup().get())) { + return; + } + model.getSelectedChatMessageForMoreOptionsPopup().set(chatMessage); + + List items = new ArrayList<>(); + items.add(new BisqPopupMenuItem(Res.get("chat.message.contextMenu.copyMessage"), + () -> onCopyMessage(chatMessage))); + if (!model.isMyMessage(chatMessage)) { + if (chatMessage instanceof PublicChatMessage) { + items.add(new BisqPopupMenuItem(Res.get("chat.message.contextMenu.ignoreUser"), + () -> onIgnoreUser(chatMessage))); + } + items.add(new BisqPopupMenuItem(Res.get("chat.message.contextMenu.reportUser"), + () -> onReportUser(chatMessage))); + } + + BisqPopupMenu menu = new BisqPopupMenu(items, onClose); + menu.setAlignment(BisqPopup.Alignment.LEFT); + menu.show(owner); + } + + public void onReportUser(ChatMessage chatMessage) { + ChatChannelDomain chatChannelDomain = model.getSelectedChannel().get().getChatChannelDomain(); + if (chatMessage instanceof PrivateChatMessage) { + PrivateChatMessage privateChatMessage = (PrivateChatMessage) chatMessage; + Navigation.navigateTo(NavigationTarget.REPORT_TO_MODERATOR, + new ReportToModeratorWindow.InitData(privateChatMessage.getSenderUserProfile(), chatChannelDomain)); + } else { + userProfileService.findUserProfile(chatMessage.getAuthorUserProfileId()) + .ifPresent(accusedUserProfile -> Navigation.navigateTo(NavigationTarget.REPORT_TO_MODERATOR, + new ReportToModeratorWindow.InitData(accusedUserProfile, chatChannelDomain))); + } + } + + public void onIgnoreUser(ChatMessage chatMessage) { + new Popup().warning(Res.get("chat.ignoreUser.warn")) + .actionButtonText(Res.get("chat.ignoreUser.confirm")) + .onAction(() -> doIgnoreUser(chatMessage)) + .closeButtonText(Res.get("action.cancel")) + .show(); + } + + public void doIgnoreUser(ChatMessage chatMessage) { + userProfileService.findUserProfile(chatMessage.getAuthorUserProfileId()) + .ifPresent(userProfileService::ignoreUserProfile); + } + + public void onCopyMessage(ChatMessage chatMessage) { + ClipboardUtil.copyToClipboard(chatMessage.getText()); + } + + public void onLeaveChannel() { + ChatChannel chatChannel = model.getSelectedChannel().get(); + checkArgument(chatChannel instanceof PrivateChatChannel, + "Not possible to leave a channel which is not a private chat."); + + new Popup().information(Res.get("chat.leave.info")) + .actionButtonText(Res.get("chat.leave.confirmLeaveChat")) + .onAction(this::doLeaveChannel) + .closeButtonText(Res.get("action.cancel")) + .show(); + } + + public void doLeaveChannel() { + ChatChannel chatChannel = model.getSelectedChannel().get(); + checkArgument(chatChannel instanceof PrivateChatChannel, + "Not possible to leave a channel which is not a private chat."); + + chatService.findChatChannelService(chatChannel) + .filter(service -> service instanceof PrivateChatChannelService) + .map(service -> (PrivateChatChannelService) service).stream() + .findAny() + .ifPresent(service -> { + service.leaveChannel(chatChannel.getId()); + chatService.getChatChannelSelectionServices().get(model.getChatChannelDomain()).maybeSelectFirstChannel(); + }); + } + + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // Scrolling + /////////////////////////////////////////////////////////////////////////////////////////////////// + + private void applyScrollValue(double scrollValue) { + model.getScrollValue().set(scrollValue); + model.getHasUnreadMessages().set(model.getNumReadMessages() < model.getChatMessages().size()); + boolean isAtBottom = scrollValue == 1d; + model.getShowScrolledDownButton().set(!isAtBottom && model.getScrollBarVisible().get()); + model.setAutoScrollToBottom(isAtBottom); + if (isAtBottom) { + model.setNumReadMessages(model.getChatMessages().size()); + } + + int numUnReadMessages = model.getChatMessages().size() - model.getNumReadMessages(); + model.getNumUnReadMessages().set(numUnReadMessages > 0 ? String.valueOf(numUnReadMessages) : ""); + } + + private void maybeScrollDownOnNewItemAdded() { + if (model.isAutoScrollToBottom()) { + // The 100 ms delay is needed as when the item gets added to the listview it updates the scroll property + // to a value < 1. After the render process is done we set it to 1. + UIScheduler.run(() -> applyScrollValue(1)).after(100); + } else { + applyScrollValue(model.getScrollValue().get()); + } + } + + void onScrollToBottom() { + applyScrollValue(1); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////////////// + + private void createAndSelectTwoPartyPrivateChatChannel(UserProfile peer) { + chatService.createAndSelectTwoPartyPrivateChatChannel(model.getChatChannelDomain(), peer) + .ifPresent(channel -> { + if (model.getChatChannelDomain() == ChatChannelDomain.BISQ_EASY_OFFERBOOK) { + Navigation.navigateTo(NavigationTarget.BISQ_EASY_PRIVATE_CHAT); + } + if (model.getChatChannelDomain() == ChatChannelDomain.DISCUSSION) { + Navigation.navigateTo(NavigationTarget.DISCUSSION_PRIVATECHATS); + } + if (model.getChatChannelDomain() == ChatChannelDomain.EVENTS) { + Navigation.navigateTo(NavigationTarget.EVENTS_PRIVATECHATS); + } + if (model.getChatChannelDomain() == ChatChannelDomain.SUPPORT) { + Navigation.navigateTo(NavigationTarget.SUPPORT_PRIVATECHATS); + } + }); + } + + private void applyPredicate() { + boolean offerOnly = settingsService.getOffersOnly().get(); + Predicate>> predicate = item -> { + Optional senderUserProfile = item.getSenderUserProfile(); + if (senderUserProfile.isEmpty()) { + return false; + } + if (bannedUserService.isUserProfileBanned(item.getChatMessage().getAuthorUserProfileId()) || + bannedUserService.isUserProfileBanned(senderUserProfile.get())) { + return false; + } + + boolean offerOnlyPredicate = true; + if (item.getChatMessage() instanceof BisqEasyOfferbookMessage) { + BisqEasyOfferbookMessage bisqEasyOfferbookMessage = (BisqEasyOfferbookMessage) item.getChatMessage(); + offerOnlyPredicate = !offerOnly || bisqEasyOfferbookMessage.hasBisqEasyOffer(); + } + // We do not display the take offer message as it has no text and is used only for sending the offer + // to the peer and signalling the take offer event. + if (item.getChatMessage().getChatMessageType() == ChatMessageType.TAKE_BISQ_EASY_OFFER) { + return false; + } + + return offerOnlyPredicate && + !userProfileService.getIgnoredUserProfileIds().contains(senderUserProfile.get().getId()) && + userProfileService.findUserProfile(senderUserProfile.get().getId()).isPresent(); + }; + model.getFilteredChatMessages().setPredicate(item -> model.getSearchPredicate().test(item) + && model.getBisqEasyOfferDirectionOrOwnerFilterPredicate().test(item) + && model.getBisqEasyPeerReputationFilterPredicate().test(item) + && predicate.test(item)); + } + + private > Pin bindChatMessages(C channel) { + // We clear and fill the list at channel change. The addObserver triggers the add method for each item, + // but as we have a contains() check there it will not have any effect. + model.getChatMessages().clear(); + model.getChatMessages().addAll(channel.getChatMessages().stream() + .map(chatMessage -> new ChatMessageListItem<>(chatMessage, + channel, + marketPriceService, + userProfileService, + reputationService, + bisqEasyTradeService, + userIdentityService, + networkService, + resendMessageService)) + .collect(Collectors.toSet())); + maybeScrollDownOnNewItemAdded(); + + return channel.getChatMessages().addObserver(new CollectionObserver<>() { + @Override + public void add(M chatMessage) { + // TODO (low prio) Delaying to the next render frame can cause duplicated items in case we get the channel + // change called 2 times in short interval (should be avoid as well). + // @namloan Could you re-test the performance issues with testing if using UIThread.run makes a difference? + // There have been many changes in the meantime, so maybe the performance issue was fixed by other changes. + UIThread.runOnNextRenderFrame(() -> { + ChatMessageListItem item = new ChatMessageListItem<>(chatMessage, + channel, + marketPriceService, + userProfileService, + reputationService, + bisqEasyTradeService, + userIdentityService, + networkService, + resendMessageService); + // As long as we use runOnNextRenderFrame we need to check to avoid adding duplicates + // The model is updated async in stages, verify that messages belong to the selected channel + if (!model.getChatMessages().contains(item) && channel.equals(model.getSelectedChannel().get())) { + model.getChatMessages().add(item); + maybeScrollDownOnNewItemAdded(); + } + }); + } + + @Override + public void remove(Object element) { + if (element instanceof ChatMessage) { + UIThread.runOnNextRenderFrame(() -> { + ChatMessage chatMessage = (ChatMessage) element; + Optional>> toRemove = + model.getChatMessages().stream() + .filter(item -> item.getChatMessage().getId().equals(chatMessage.getId())) + .findAny(); + toRemove.ifPresent(item -> { + item.dispose(); + model.getChatMessages().remove(item); + }); + }); + } + } + + @Override + public void clear() { + UIThread.runOnNextRenderFrame(() -> { + model.getChatMessages().forEach(ChatMessageListItem::dispose); + model.getChatMessages().clear(); + }); + } + }); + } + + public String getUserName(String userProfileId) { + return userProfileService.findUserProfile(userProfileId) + .map(UserProfile::getUserName) + .orElse(Res.get("data.na")); + } + + public void onResendMessage(String messageId) { + resendMessageService.ifPresent(service -> service.resendMessage(messageId)); + } + + public boolean canResendMessage(String messageId) { + return resendMessageService.map(service -> service.canResendMessage(messageId)).orElse(false); + } +} diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListModel.java new file mode 100644 index 0000000000..ce42b9dc43 --- /dev/null +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListModel.java @@ -0,0 +1,53 @@ +package bisq.desktop.main.content.components.chat_messages.list_view; + +import bisq.chat.ChatChannel; +import bisq.chat.ChatChannelDomain; +import bisq.chat.ChatMessage; +import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.user.identity.UserIdentityService; +import javafx.beans.property.*; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import javafx.collections.transformation.SortedList; +import lombok.Getter; +import lombok.Setter; + +import java.util.function.Predicate; + +@Getter +public class ChatMessagesListModel implements bisq.desktop.common.view.Model { + private final UserIdentityService userIdentityService; + private final ObjectProperty> selectedChannel = new SimpleObjectProperty<>(); + private final ObservableList>> chatMessages = FXCollections.observableArrayList(); + private final FilteredList>> filteredChatMessages = new FilteredList<>(chatMessages); + private final SortedList>> sortedChatMessages = new SortedList<>(filteredChatMessages); + private final BooleanProperty isPublicChannel = new SimpleBooleanProperty(); + private final ObjectProperty selectedChatMessageForMoreOptionsPopup = new SimpleObjectProperty<>(null); + private final ChatChannelDomain chatChannelDomain; + @Setter + private Predicate>> searchPredicate = e -> true; + @Setter + private Predicate>> BisqEasyOfferDirectionOrOwnerFilterPredicate = e -> true; + @Setter + private Predicate>> BisqEasyPeerReputationFilterPredicate = e -> true; + @Setter + private boolean autoScrollToBottom; + @Setter + private int numReadMessages; + private final BooleanProperty hasUnreadMessages = new SimpleBooleanProperty(); + private final StringProperty numUnReadMessages = new SimpleStringProperty(); + private final BooleanProperty showScrolledDownButton = new SimpleBooleanProperty(); + private final BooleanProperty scrollBarVisible = new SimpleBooleanProperty(); + private final DoubleProperty scrollValue = new SimpleDoubleProperty(); + + public ChatMessagesListModel(UserIdentityService userIdentityService, + ChatChannelDomain chatChannelDomain) { + this.userIdentityService = userIdentityService; + this.chatChannelDomain = chatChannelDomain; + } + + boolean isMyMessage(ChatMessage chatMessage) { + return chatMessage.isMyMessage(userIdentityService); + } +} diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListView.java index c4bdc06d8c..940ff000c4 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListView.java @@ -1,89 +1,24 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - package bisq.desktop.main.content.components.chat_messages.list_view; -import bisq.bisq_easy.BisqEasyService; -import bisq.bisq_easy.NavigationTarget; -import bisq.bonded_roles.market_price.MarketPriceService; -import bisq.chat.*; -import bisq.chat.bisqeasy.BisqEasyOfferMessage; -import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannel; -import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; -import bisq.chat.bisqeasy.open_trades.BisqEasyOpenTradeChannel; -import bisq.chat.common.CommonPublicChatChannel; -import bisq.chat.common.CommonPublicChatChannelService; -import bisq.chat.common.CommonPublicChatMessage; -import bisq.chat.notifications.ChatNotificationService; -import bisq.chat.priv.PrivateChatChannel; -import bisq.chat.priv.PrivateChatChannelService; -import bisq.chat.priv.PrivateChatMessage; -import bisq.chat.pub.PublicChatChannel; -import bisq.chat.pub.PublicChatMessage; -import bisq.chat.two_party.TwoPartyPrivateChatChannel; -import bisq.common.observable.Pin; -import bisq.common.observable.collection.CollectionObserver; -import bisq.desktop.ServiceProvider; +import bisq.chat.ChatChannel; +import bisq.chat.ChatMessage; import bisq.desktop.common.Transitions; -import bisq.desktop.common.observable.FxBindings; -import bisq.desktop.common.threading.UIScheduler; -import bisq.desktop.common.threading.UIThread; -import bisq.desktop.common.utils.ClipboardUtil; -import bisq.desktop.common.view.Navigation; -import bisq.desktop.components.controls.*; +import bisq.desktop.components.controls.Badge; +import bisq.desktop.components.controls.BisqTooltip; import bisq.desktop.components.list_view.ListViewUtil; import bisq.desktop.components.list_view.NoSelectionModel; -import bisq.desktop.components.overlay.Popup; -import bisq.desktop.main.content.bisq_easy.BisqEasyServiceUtil; -import bisq.desktop.main.content.bisq_easy.take_offer.TakeOfferController; import bisq.desktop.main.content.chat.ChatUtil; -import bisq.desktop.main.content.components.ReportToModeratorWindow; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; import bisq.i18n.Res; -import bisq.network.NetworkService; -import bisq.network.identity.NetworkId; -import bisq.network.p2p.services.confidential.resend.ResendMessageService; -import bisq.offer.bisq_easy.BisqEasyOffer; -import bisq.offer.options.OfferOptionUtil; -import bisq.settings.SettingsService; -import bisq.trade.Trade; -import bisq.trade.bisq_easy.BisqEasyTradeService; -import bisq.user.banned.BannedUserService; -import bisq.user.identity.UserIdentity; -import bisq.user.identity.UserIdentityService; -import bisq.user.profile.UserProfile; -import bisq.user.profile.UserProfileService; -import bisq.user.reputation.ReputationScore; -import bisq.user.reputation.ReputationService; import javafx.animation.Interpolator; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; -import javafx.beans.property.*; -import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import javafx.collections.transformation.FilteredList; -import javafx.collections.transformation.SortedList; import javafx.geometry.Insets; import javafx.geometry.Orientation; import javafx.geometry.Pos; import javafx.scene.Cursor; -import javafx.scene.Node; -import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.ListView; import javafx.scene.control.ScrollBar; @@ -94,816 +29,159 @@ import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.util.Duration; -import lombok.Getter; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; import java.util.Optional; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; @Slf4j -public class ChatMessagesListView { - private final Controller controller; - - public ChatMessagesListView(ServiceProvider serviceProvider, - Consumer mentionUserHandler, - Consumer showChatUserDetailsHandler, - Consumer replyHandler, - ChatChannelDomain chatChannelDomain) { - controller = new Controller(serviceProvider, - mentionUserHandler, - showChatUserDetailsHandler, - replyHandler, - chatChannelDomain); - } - - public Pane getRoot() { - return controller.view.getRoot(); - } - - public void setSearchPredicate(Predicate>> predicate) { - controller.setSearchPredicate(predicate); - } - - public void setBisqEasyOfferDirectionOrOwnerFilterPredicate(Predicate>> predicate) { - controller.setBisqEasyOfferDirectionOrOwnerFilterPredicate(predicate); - } - - public void setBisqEasyPeerReputationFilterPredicate(Predicate>> predicate) { - controller.setBisqEasyPeerReputationFilterPredicate(predicate); +public class ChatMessagesListView extends bisq.desktop.common.view.View { + private final ListView>> listView; + private final ImageView scrollDownImageView; + private final Badge scrollDownBadge; + private final BisqTooltip scrollDownTooltip; + private final Label placeholderTitle = new Label(""); + private final Label placeholderDescription = new Label(""); + private final Pane scrollDownBackground; + private Optional scrollBar = Optional.empty(); + private Subscription hasUnreadMessagesPin, showScrolledDownButtonPin; + private Timeline fadeInScrollDownBadgeTimeline; + + public ChatMessagesListView(ChatMessagesListModel model, ChatMessagesListController controller) { + super(new StackPane(), model, controller); + + listView = new ListView<>(model.getSortedChatMessages()); + listView.getStyleClass().add("chat-messages-list-view"); + + VBox placeholder = ChatUtil.createEmptyChatPlaceholder(placeholderTitle, placeholderDescription); + listView.setPlaceholder(placeholder); + + listView.setCellFactory(new ChatMessageListCellFactory(controller, model)); + + // https://stackoverflow.com/questions/20621752/javafx-make-listview-not-selectable-via-mouse + listView.setSelectionModel(new NoSelectionModel<>()); + VBox.setVgrow(listView, Priority.ALWAYS); + + scrollDownBackground = new Pane(); + scrollDownBackground.getStyleClass().add("scroll-down-bg"); + + scrollDownImageView = new ImageView(); + scrollDownBadge = new Badge(scrollDownImageView); + scrollDownBadge.setMaxSize(25, 25); + scrollDownBadge.getStyleClass().add("chat-messages-badge"); + scrollDownBadge.setPosition(Pos.BOTTOM_RIGHT); + scrollDownBadge.setBadgeInsets(new Insets(20, 10, -2, 55)); + scrollDownBadge.setCursor(Cursor.HAND); + + scrollDownTooltip = new BisqTooltip(Res.get("chat.listView.scrollDown")); + Tooltip.install(scrollDownBadge, scrollDownTooltip); + + StackPane.setAlignment(scrollDownBadge, Pos.BOTTOM_CENTER); + StackPane.setAlignment(scrollDownBackground, Pos.BOTTOM_CENTER); + StackPane.setMargin(scrollDownBackground, new Insets(0, 15, 0, 0)); + root.setAlignment(Pos.CENTER); + root.getChildren().addAll(listView, scrollDownBackground, scrollDownBadge); } - public void refreshMessages() { - controller.refreshMessages(); - } - - public static class Controller implements bisq.desktop.common.view.Controller { - private final ChatService chatService; - private final UserIdentityService userIdentityService; - private final UserProfileService userProfileService; - private final ReputationService reputationService; - private final SettingsService settingsService; - private final Consumer mentionUserHandler; - private final Consumer replyHandler; - private final Consumer showChatUserDetailsHandler; - private final Model model; - @Getter - private final View view; - private final ChatNotificationService chatNotificationService; - private final BisqEasyTradeService bisqEasyTradeService; - private final BannedUserService bannedUserService; - private final NetworkService networkService; - private final Optional resendMessageService; - private final BisqEasyService bisqEasyService; - private final MarketPriceService marketPriceService; - private Pin selectedChannelPin, chatMessagesPin, offerOnlySettingsPin; - private Subscription selectedChannelSubscription, focusSubscription, scrollValuePin, scrollBarVisiblePin; - - private Controller(ServiceProvider serviceProvider, - Consumer mentionUserHandler, - Consumer showChatUserDetailsHandler, - Consumer replyHandler, - ChatChannelDomain chatChannelDomain) { - chatService = serviceProvider.getChatService(); - chatNotificationService = chatService.getChatNotificationService(); - userIdentityService = serviceProvider.getUserService().getUserIdentityService(); - userProfileService = serviceProvider.getUserService().getUserProfileService(); - reputationService = serviceProvider.getUserService().getReputationService(); - settingsService = serviceProvider.getSettingsService(); - bisqEasyService = serviceProvider.getBisqEasyService(); - bisqEasyTradeService = serviceProvider.getTradeService().getBisqEasyTradeService(); - bannedUserService = serviceProvider.getUserService().getBannedUserService(); - networkService = serviceProvider.getNetworkService(); - resendMessageService = serviceProvider.getNetworkService().getResendMessageService(); - marketPriceService = serviceProvider.getBondedRolesService().getMarketPriceService(); - this.mentionUserHandler = mentionUserHandler; - this.showChatUserDetailsHandler = showChatUserDetailsHandler; - this.replyHandler = replyHandler; - - model = new Model(userIdentityService, chatChannelDomain); - view = new View(model, this); - } - - @Override - public void onActivate() { - model.getSortedChatMessages().setComparator(ChatMessageListItem::compareTo); - - offerOnlySettingsPin = FxBindings.subscribe(settingsService.getOffersOnly(), offerOnly -> UIThread.run(this::applyPredicate)); - - if (selectedChannelPin != null) { - selectedChannelPin.unbind(); - } - - ChatChannelSelectionService selectionService = chatService.getChatChannelSelectionServices().get(model.getChatChannelDomain()); - - selectedChannelPin = selectionService.getSelectedChannel().addObserver(this::selectedChannelChanged); - - scrollValuePin = EasyBind.subscribe(model.getScrollValue(), scrollValue -> { - if (scrollValue != null) { - applyScrollValue(scrollValue.doubleValue()); - } - }); - - scrollBarVisiblePin = EasyBind.subscribe(model.scrollBarVisible, scrollBarVisible -> { - if (scrollBarVisible != null && scrollBarVisible) { - applyScrollValue(1); - } - }); - - applyScrollValue(1); - } - - @Override - public void onDeactivate() { - if (offerOnlySettingsPin != null) { - offerOnlySettingsPin.unbind(); - } - if (selectedChannelPin != null) { - selectedChannelPin.unbind(); - selectedChannelPin = null; - } - if (chatMessagesPin != null) { - chatMessagesPin.unbind(); - chatMessagesPin = null; - } - if (focusSubscription != null) { - focusSubscription.unsubscribe(); - } - if (selectedChannelSubscription != null) { - selectedChannelSubscription.unsubscribe(); - } - - scrollValuePin.unsubscribe(); - scrollBarVisiblePin.unsubscribe(); - - model.chatMessages.forEach(ChatMessageListItem::dispose); - model.chatMessages.clear(); - } - - private void selectedChannelChanged(ChatChannel channel) { - UIThread.run(() -> { - model.selectedChannel.set(channel); - model.isPublicChannel.set(channel instanceof PublicChatChannel); - - if (chatMessagesPin != null) { - chatMessagesPin.unbind(); - } - - // Clear and call dispose on the current messages when we change the channel. - model.chatMessages.forEach(ChatMessageListItem::dispose); - model.chatMessages.clear(); - - if (channel instanceof BisqEasyOfferbookChannel) { - chatMessagesPin = bindChatMessages((BisqEasyOfferbookChannel) channel); - } else if (channel instanceof BisqEasyOpenTradeChannel) { - chatMessagesPin = bindChatMessages((BisqEasyOpenTradeChannel) channel); - } else if (channel instanceof CommonPublicChatChannel) { - chatMessagesPin = bindChatMessages((CommonPublicChatChannel) channel); - } else if (channel instanceof TwoPartyPrivateChatChannel) { - chatMessagesPin = bindChatMessages((TwoPartyPrivateChatChannel) channel); - } - - if (focusSubscription != null) { - focusSubscription.unsubscribe(); - } - if (selectedChannelSubscription != null) { - selectedChannelSubscription.unsubscribe(); - } - if (channel != null) { - Scene scene = view.getRoot().getScene(); - if (scene != null) { - focusSubscription = EasyBind.subscribe(scene.getWindow().focusedProperty(), - focused -> { - if (focused && model.getSelectedChannel().get() != null) { - chatNotificationService.consume(model.getSelectedChannel().get().getId()); - } - }); - } - - selectedChannelSubscription = EasyBind.subscribe(model.selectedChannel, - selectedChannel -> { - if (selectedChannel != null) { - chatNotificationService.consume(model.getSelectedChannel().get().getId()); - } - }); - } - }); - } - - - /////////////////////////////////////////////////////////////////////////////////////////////////// - // API - called from client - /////////////////////////////////////////////////////////////////////////////////////////////////// - - private void refreshMessages() { - model.chatMessages.setAll(new ArrayList<>(model.chatMessages)); - } - - private void setSearchPredicate(Predicate>> predicate) { - model.setSearchPredicate(Objects.requireNonNullElseGet(predicate, () -> e -> true)); - applyPredicate(); - } - - private void setBisqEasyOfferDirectionOrOwnerFilterPredicate(Predicate>> predicate) { - model.setBisqEasyOfferDirectionOrOwnerFilterPredicate(Objects.requireNonNullElseGet(predicate, () -> e -> true)); - applyPredicate(); - } - - private void setBisqEasyPeerReputationFilterPredicate(Predicate>> predicate) { - model.setBisqEasyPeerReputationFilterPredicate(Objects.requireNonNullElseGet(predicate, () -> e -> true)); - applyPredicate(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////////////// - // UI - delegate to client - /////////////////////////////////////////////////////////////////////////////////////////////////// - - public void onMention(UserProfile userProfile) { - mentionUserHandler.accept(userProfile); - } - - public void onShowChatUserDetails(ChatMessage chatMessage) { - showChatUserDetailsHandler.accept(chatMessage); - } - - public void onReply(ChatMessage chatMessage) { - replyHandler.accept(chatMessage); - } - - - /////////////////////////////////////////////////////////////////////////////////////////////////// - // UI - handler - /////////////////////////////////////////////////////////////////////////////////////////////////// - - public void onTakeOffer(BisqEasyOfferbookMessage bisqEasyOfferbookMessage) { - checkArgument(bisqEasyOfferbookMessage.getBisqEasyOffer().isPresent(), "message must contain offer"); - checkArgument(userIdentityService.getSelectedUserIdentity() != null, - "userIdentityService.getSelectedUserIdentity() must not be null"); - checkArgument(!model.isMyMessage(bisqEasyOfferbookMessage), "tradeChatMessage must not be mine"); - - UserProfile userProfile = userIdentityService.getSelectedUserIdentity().getUserProfile(); - NetworkId takerNetworkId = userProfile.getNetworkId(); - BisqEasyOffer bisqEasyOffer = bisqEasyOfferbookMessage.getBisqEasyOffer().get(); - String tradeId = Trade.createId(bisqEasyOffer.getId(), takerNetworkId.getId()); - if (bisqEasyTradeService.tradeExists(tradeId)) { - new Popup().information(Res.get("chat.message.offer.offerAlreadyTaken.warn")).show(); + @Override + protected void onViewAttached() { + ListViewUtil.findScrollbarAsync(listView, Orientation.VERTICAL, 1000).whenComplete((scrollBar, throwable) -> { + if (throwable != null) { + log.error("Find scrollbar failed", throwable); return; } - - if (!BisqEasyServiceUtil.offerMatchesMinRequiredReputationScore(reputationService, - bisqEasyService, - userIdentityService, - userProfileService, - bisqEasyOffer)) { - if (bisqEasyOffer.getDirection().isSell()) { - long makerAsSellersScore = userProfileService.findUserProfile(bisqEasyOffer.getMakersUserProfileId()) - .map(reputationService::getReputationScore) - .map(ReputationScore::getTotalScore) - .orElse(0L); - long myMinRequiredScore = bisqEasyService.getMinRequiredReputationScore().get(); - new Popup().information(Res.get("chat.message.takeOffer.makersReputationScoreTooLow.warn", - myMinRequiredScore, makerAsSellersScore)).show(); - } else { - long myScoreAsSeller = reputationService.getReputationScore(userIdentityService.getSelectedUserIdentity().getUserProfile()).getTotalScore(); - long offersRequiredScore = OfferOptionUtil.findRequiredTotalReputationScore(bisqEasyOffer).orElse(0L); - new Popup().information(Res.get("chat.message.takeOffer.myReputationScoreTooLow.warn", - offersRequiredScore, myScoreAsSeller)).show(); - } - return; - } - - if (bannedUserService.isUserProfileBanned(bisqEasyOfferbookMessage.getAuthorUserProfileId()) || - bannedUserService.isUserProfileBanned(userProfile)) { - return; - } - - Navigation.navigateTo(NavigationTarget.TAKE_OFFER, new TakeOfferController.InitData(bisqEasyOffer)); - } - - public void onDeleteMessage(ChatMessage chatMessage) { - String authorUserProfileId = chatMessage.getAuthorUserProfileId(); - userIdentityService.findUserIdentity(authorUserProfileId) - .ifPresent(authorUserIdentity -> { - if (authorUserIdentity.equals(userIdentityService.getSelectedUserIdentity())) { - boolean isBisqEasyPublicChatMessageWithOffer = - chatMessage instanceof BisqEasyOfferbookMessage - && ((BisqEasyOfferMessage) chatMessage).hasBisqEasyOffer(); - if (isBisqEasyPublicChatMessageWithOffer) { - new Popup().warning(Res.get("bisqEasy.offerbook.chatMessage.deleteOffer.confirmation")) - .actionButtonText(Res.get("confirmation.yes")) - .onAction(() -> doDeleteMessage(chatMessage, authorUserIdentity)) - .closeButtonText(Res.get("confirmation.no")) - .show(); - } else { - doDeleteMessage(chatMessage, authorUserIdentity); - } - } else { - new Popup().warning(Res.get("chat.message.delete.differentUserProfile.warn")) - .closeButtonText(Res.get("confirmation.no")) - .actionButtonText(Res.get("confirmation.yes")) - .onAction(() -> { - userIdentityService.selectChatUserIdentity(authorUserIdentity); - doDeleteMessage(chatMessage, authorUserIdentity); - }) - .show(); - } - }); - } - - private void doDeleteMessage(ChatMessage chatMessage, UserIdentity userIdentity) { - checkArgument(chatMessage instanceof PublicChatMessage); - - if (chatMessage instanceof BisqEasyOfferbookMessage) { - BisqEasyOfferbookMessage bisqEasyOfferbookMessage = (BisqEasyOfferbookMessage) chatMessage; - chatService.getBisqEasyOfferbookChannelService().deleteChatMessage(bisqEasyOfferbookMessage, userIdentity.getNetworkIdWithKeyPair()) - .whenComplete((result, throwable) -> { - if (throwable != null) { - log.error("We got an error at doDeleteMessage: " + throwable); - } - }); - } else if (chatMessage instanceof CommonPublicChatMessage) { - CommonPublicChatChannelService commonPublicChatChannelService = chatService.getCommonPublicChatChannelServices().get(model.chatChannelDomain); - CommonPublicChatMessage commonPublicChatMessage = (CommonPublicChatMessage) chatMessage; - commonPublicChatChannelService.findChannel(chatMessage) - .ifPresent(channel -> commonPublicChatChannelService.deleteChatMessage(commonPublicChatMessage, userIdentity.getNetworkIdWithKeyPair())); + this.scrollBar = scrollBar; + if (scrollBar.isPresent()) { + scrollBar.get().valueProperty().bindBidirectional(model.getScrollValue()); + model.getScrollBarVisible().bind(scrollBar.get().visibleProperty()); + controller.onScrollToBottom(); + } else { + log.error("scrollBar is empty"); } - } - - public void onOpenPrivateChannel(ChatMessage chatMessage) { - checkArgument(!model.isMyMessage(chatMessage)); - - userProfileService.findUserProfile(chatMessage.getAuthorUserProfileId()) - .ifPresent(this::createAndSelectTwoPartyPrivateChatChannel); - } + }); - public void onSaveEditedMessage(ChatMessage chatMessage, String editedText) { - checkArgument(chatMessage instanceof PublicChatMessage); - checkArgument(model.isMyMessage(chatMessage)); + scrollDownBackground.visibleProperty().bind(model.getShowScrolledDownButton()); + scrollDownBackground.managedProperty().bind(model.getShowScrolledDownButton()); - if (editedText.length() > ChatMessage.MAX_TEXT_LENGTH) { - new Popup().warning(Res.get("validation.tooLong", ChatMessage.MAX_TEXT_LENGTH)).show(); - return; - } - - UserIdentity userIdentity = checkNotNull(userIdentityService.getSelectedUserIdentity()); - if (chatMessage instanceof BisqEasyOfferbookMessage) { - BisqEasyOfferbookMessage bisqEasyOfferbookMessage = (BisqEasyOfferbookMessage) chatMessage; - chatService.getBisqEasyOfferbookChannelService().publishEditedChatMessage(bisqEasyOfferbookMessage, editedText, userIdentity); - } else if (chatMessage instanceof CommonPublicChatMessage) { - CommonPublicChatMessage commonPublicChatMessage = (CommonPublicChatMessage) chatMessage; - chatService.getCommonPublicChatChannelServices().get(model.chatChannelDomain).publishEditedChatMessage(commonPublicChatMessage, editedText, userIdentity); - } - } + scrollDownBadge.textProperty().bind(model.getNumUnReadMessages()); - void onOpenMoreOptions(Node owner, ChatMessage chatMessage, Runnable onClose) { - if (chatMessage.equals(model.selectedChatMessageForMoreOptionsPopup.get())) { + scrollDownBadge.setOpacity(0); + showScrolledDownButtonPin = EasyBind.subscribe(model.getShowScrolledDownButton(), showScrolledDownButton -> { + if (showScrolledDownButton == null) { return; } - model.selectedChatMessageForMoreOptionsPopup.set(chatMessage); - - List items = new ArrayList<>(); - items.add(new BisqPopupMenuItem(Res.get("chat.message.contextMenu.copyMessage"), - () -> onCopyMessage(chatMessage))); - if (!model.isMyMessage(chatMessage)) { - if (chatMessage instanceof PublicChatMessage) { - items.add(new BisqPopupMenuItem(Res.get("chat.message.contextMenu.ignoreUser"), - () -> onIgnoreUser(chatMessage))); - } - items.add(new BisqPopupMenuItem(Res.get("chat.message.contextMenu.reportUser"), - () -> onReportUser(chatMessage))); + if (fadeInScrollDownBadgeTimeline != null) { + fadeInScrollDownBadgeTimeline.stop(); } - - BisqPopupMenu menu = new BisqPopupMenu(items, onClose); - menu.setAlignment(BisqPopup.Alignment.LEFT); - menu.show(owner); - } - - public void onReportUser(ChatMessage chatMessage) { - ChatChannelDomain chatChannelDomain = model.getSelectedChannel().get().getChatChannelDomain(); - if (chatMessage instanceof PrivateChatMessage) { - PrivateChatMessage privateChatMessage = (PrivateChatMessage) chatMessage; - Navigation.navigateTo(NavigationTarget.REPORT_TO_MODERATOR, - new ReportToModeratorWindow.InitData(privateChatMessage.getSenderUserProfile(), chatChannelDomain)); + if (showScrolledDownButton) { + fadeInScrollDownBadge(); } else { - userProfileService.findUserProfile(chatMessage.getAuthorUserProfileId()) - .ifPresent(accusedUserProfile -> Navigation.navigateTo(NavigationTarget.REPORT_TO_MODERATOR, - new ReportToModeratorWindow.InitData(accusedUserProfile, chatChannelDomain))); - } - } - - public void onIgnoreUser(ChatMessage chatMessage) { - new Popup().warning(Res.get("chat.ignoreUser.warn")) - .actionButtonText(Res.get("chat.ignoreUser.confirm")) - .onAction(() -> doIgnoreUser(chatMessage)) - .closeButtonText(Res.get("action.cancel")) - .show(); - } - - public void doIgnoreUser(ChatMessage chatMessage) { - userProfileService.findUserProfile(chatMessage.getAuthorUserProfileId()) - .ifPresent(userProfileService::ignoreUserProfile); - } - - public void onCopyMessage(ChatMessage chatMessage) { - ClipboardUtil.copyToClipboard(chatMessage.getText()); - } - - public void onLeaveChannel() { - ChatChannel chatChannel = model.getSelectedChannel().get(); - checkArgument(chatChannel instanceof PrivateChatChannel, - "Not possible to leave a channel which is not a private chat."); - - new Popup().information(Res.get("chat.leave.info")) - .actionButtonText(Res.get("chat.leave.confirmLeaveChat")) - .onAction(this::doLeaveChannel) - .closeButtonText(Res.get("action.cancel")) - .show(); - } - - public void doLeaveChannel() { - ChatChannel chatChannel = model.getSelectedChannel().get(); - checkArgument(chatChannel instanceof PrivateChatChannel, - "Not possible to leave a channel which is not a private chat."); - - chatService.findChatChannelService(chatChannel) - .filter(service -> service instanceof PrivateChatChannelService) - .map(service -> (PrivateChatChannelService) service).stream() - .findAny() - .ifPresent(service -> { - service.leaveChannel(chatChannel.getId()); - chatService.getChatChannelSelectionServices().get(model.getChatChannelDomain()).maybeSelectFirstChannel(); - }); - } - - - /////////////////////////////////////////////////////////////////////////////////////////////////// - // Scrolling - /////////////////////////////////////////////////////////////////////////////////////////////////// - - private void applyScrollValue(double scrollValue) { - model.scrollValue.set(scrollValue); - model.hasUnreadMessages.set(model.numReadMessages < model.getChatMessages().size()); - boolean isAtBottom = scrollValue == 1d; - model.showScrolledDownButton.set(!isAtBottom && model.scrollBarVisible.get()); - model.autoScrollToBottom = isAtBottom; - if (isAtBottom) { - model.numReadMessages = model.getChatMessages().size(); + scrollDownBadge.setOpacity(0); } - - int numUnReadMessages = model.getChatMessages().size() - model.numReadMessages; - model.numUnReadMessages.set(numUnReadMessages > 0 ? String.valueOf(numUnReadMessages) : ""); - } - - private void maybeScrollDownOnNewItemAdded() { - if (model.autoScrollToBottom) { - // The 100 ms delay is needed as when the item gets added to the listview it updates the scroll property - // to a value < 1. After the render process is done we set it to 1. - UIScheduler.run(() -> applyScrollValue(1)).after(100); + }); + hasUnreadMessagesPin = EasyBind.subscribe(model.getHasUnreadMessages(), hasUnreadMessages -> { + if (hasUnreadMessages) { + scrollDownImageView.setOpacity(1); + scrollDownImageView.setId("scroll-down-green"); + scrollDownTooltip.setText(Res.get("chat.listView.scrollDown.newMessages")); } else { - applyScrollValue(model.scrollValue.get()); + scrollDownImageView.setOpacity(0.5); + scrollDownImageView.setId("scroll-down-white"); + scrollDownTooltip.setText(Res.get("chat.listView.scrollDown")); } - } - - void onScrollToBottom() { - applyScrollValue(1); - } + }); - /////////////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////////////// + scrollDownBadge.setOnMouseClicked(e -> controller.onScrollToBottom()); - private void createAndSelectTwoPartyPrivateChatChannel(UserProfile peer) { - chatService.createAndSelectTwoPartyPrivateChatChannel(model.getChatChannelDomain(), peer) - .ifPresent(channel -> { - if (model.getChatChannelDomain() == ChatChannelDomain.BISQ_EASY_OFFERBOOK) { - Navigation.navigateTo(NavigationTarget.BISQ_EASY_PRIVATE_CHAT); - } - if (model.getChatChannelDomain() == ChatChannelDomain.DISCUSSION) { - Navigation.navigateTo(NavigationTarget.DISCUSSION_PRIVATECHATS); - } - if (model.getChatChannelDomain() == ChatChannelDomain.EVENTS) { - Navigation.navigateTo(NavigationTarget.EVENTS_PRIVATECHATS); - } - if (model.getChatChannelDomain() == ChatChannelDomain.SUPPORT) { - Navigation.navigateTo(NavigationTarget.SUPPORT_PRIVATECHATS); - } - }); - } - - private void applyPredicate() { - boolean offerOnly = settingsService.getOffersOnly().get(); - Predicate>> predicate = item -> { - Optional senderUserProfile = item.getSenderUserProfile(); - if (senderUserProfile.isEmpty()) { - return false; - } - if (bannedUserService.isUserProfileBanned(item.getChatMessage().getAuthorUserProfileId()) || - bannedUserService.isUserProfileBanned(senderUserProfile.get())) { - return false; - } - - boolean offerOnlyPredicate = true; - if (item.getChatMessage() instanceof BisqEasyOfferbookMessage) { - BisqEasyOfferbookMessage bisqEasyOfferbookMessage = (BisqEasyOfferbookMessage) item.getChatMessage(); - offerOnlyPredicate = !offerOnly || bisqEasyOfferbookMessage.hasBisqEasyOffer(); - } - // We do not display the take offer message as it has no text and is used only for sending the offer - // to the peer and signalling the take offer event. - if (item.getChatMessage().getChatMessageType() == ChatMessageType.TAKE_BISQ_EASY_OFFER) { - return false; - } - - return offerOnlyPredicate && - !userProfileService.getIgnoredUserProfileIds().contains(senderUserProfile.get().getId()) && - userProfileService.findUserProfile(senderUserProfile.get().getId()).isPresent(); - }; - model.filteredChatMessages.setPredicate(item -> model.getSearchPredicate().test(item) - && model.getBisqEasyOfferDirectionOrOwnerFilterPredicate().test(item) - && model.getBisqEasyPeerReputationFilterPredicate().test(item) - && predicate.test(item)); - } - - private > Pin bindChatMessages(C channel) { - // We clear and fill the list at channel change. The addObserver triggers the add method for each item, - // but as we have a contains() check there it will not have any effect. - model.chatMessages.clear(); - model.chatMessages.addAll(channel.getChatMessages().stream() - .map(chatMessage -> new ChatMessageListItem<>(chatMessage, - channel, - marketPriceService, - userProfileService, - reputationService, - bisqEasyTradeService, - userIdentityService, - networkService, - resendMessageService)) - .collect(Collectors.toSet())); - maybeScrollDownOnNewItemAdded(); - - return channel.getChatMessages().addObserver(new CollectionObserver<>() { - @Override - public void add(M chatMessage) { - // TODO (low prio) Delaying to the next render frame can cause duplicated items in case we get the channel - // change called 2 times in short interval (should be avoid as well). - // @namloan Could you re-test the performance issues with testing if using UIThread.run makes a difference? - // There have been many changes in the meantime, so maybe the performance issue was fixed by other changes. - UIThread.runOnNextRenderFrame(() -> { - ChatMessageListItem item = new ChatMessageListItem<>(chatMessage, - channel, - marketPriceService, - userProfileService, - reputationService, - bisqEasyTradeService, - userIdentityService, - networkService, - resendMessageService); - // As long as we use runOnNextRenderFrame we need to check to avoid adding duplicates - // The model is updated async in stages, verify that messages belong to the selected channel - if (!model.chatMessages.contains(item) && channel.equals(model.selectedChannel.get())) { - model.chatMessages.add(item); - maybeScrollDownOnNewItemAdded(); - } - }); - } - - @Override - public void remove(Object element) { - if (element instanceof ChatMessage) { - UIThread.runOnNextRenderFrame(() -> { - ChatMessage chatMessage = (ChatMessage) element; - Optional>> toRemove = - model.chatMessages.stream() - .filter(item -> item.getChatMessage().getId().equals(chatMessage.getId())) - .findAny(); - toRemove.ifPresent(item -> { - item.dispose(); - model.chatMessages.remove(item); - }); - }); - } - } - - @Override - public void clear() { - UIThread.runOnNextRenderFrame(() -> { - model.chatMessages.forEach(ChatMessageListItem::dispose); - model.chatMessages.clear(); - }); - } - }); - } - - public String getUserName(String userProfileId) { - return userProfileService.findUserProfile(userProfileId) - .map(UserProfile::getUserName) - .orElse(Res.get("data.na")); - } - - public void onResendMessage(String messageId) { - resendMessageService.ifPresent(service -> service.resendMessage(messageId)); - } - - public boolean canResendMessage(String messageId) { - return resendMessageService.map(service -> service.canResendMessage(messageId)).orElse(false); + if (ChatUtil.isCommonChat(model.getChatChannelDomain()) && model.getIsPublicChannel().get()) { + placeholderTitle.setText(Res.get("chat.messagebox.noChats.placeholder.title")); + placeholderDescription.setText(Res.get("chat.messagebox.noChats.placeholder.description", + model.getSelectedChannel().get().getDisplayString())); + } else { + placeholderTitle.setText(""); + placeholderDescription.setText(""); } } - @Getter - public static class Model implements bisq.desktop.common.view.Model { - private final UserIdentityService userIdentityService; - private final ObjectProperty> selectedChannel = new SimpleObjectProperty<>(); - private final ObservableList>> chatMessages = FXCollections.observableArrayList(); - private final FilteredList>> filteredChatMessages = new FilteredList<>(chatMessages); - private final SortedList>> sortedChatMessages = new SortedList<>(filteredChatMessages); - private final BooleanProperty isPublicChannel = new SimpleBooleanProperty(); - private final ObjectProperty selectedChatMessageForMoreOptionsPopup = new SimpleObjectProperty<>(null); - private final ChatChannelDomain chatChannelDomain; - @Setter - private Predicate>> searchPredicate = e -> true; - @Setter - private Predicate>> BisqEasyOfferDirectionOrOwnerFilterPredicate = e -> true; - @Setter - private Predicate>> BisqEasyPeerReputationFilterPredicate = e -> true; - - private boolean autoScrollToBottom; - private int numReadMessages; - private final BooleanProperty hasUnreadMessages = new SimpleBooleanProperty(); - private final StringProperty numUnReadMessages = new SimpleStringProperty(); - private final BooleanProperty showScrolledDownButton = new SimpleBooleanProperty(); - private final BooleanProperty scrollBarVisible = new SimpleBooleanProperty(); - private final DoubleProperty scrollValue = new SimpleDoubleProperty(); - - private Model(UserIdentityService userIdentityService, - ChatChannelDomain chatChannelDomain) { - this.userIdentityService = userIdentityService; - this.chatChannelDomain = chatChannelDomain; - } - - boolean isMyMessage(ChatMessage chatMessage) { - return chatMessage.isMyMessage(userIdentityService); - } - } - - - @Slf4j - private static class View extends bisq.desktop.common.view.View { - private final ListView>> listView; - private final ImageView scrollDownImageView; - private final Badge scrollDownBadge; - private final BisqTooltip scrollDownTooltip; - private final Label placeholderTitle = new Label(""); - private final Label placeholderDescription = new Label(""); - private final Pane scrollDownBackground; - private Optional scrollBar = Optional.empty(); - private Subscription hasUnreadMessagesPin, showScrolledDownButtonPin; - private Timeline fadeInScrollDownBadgeTimeline; - - private View(Model model, Controller controller) { - super(new StackPane(), model, controller); - - listView = new ListView<>(model.getSortedChatMessages()); - listView.getStyleClass().add("chat-messages-list-view"); - - VBox placeholder = ChatUtil.createEmptyChatPlaceholder(placeholderTitle, placeholderDescription); - listView.setPlaceholder(placeholder); - - listView.setCellFactory(new ChatMessageListCellFactory(controller, model)); - - // https://stackoverflow.com/questions/20621752/javafx-make-listview-not-selectable-via-mouse - listView.setSelectionModel(new NoSelectionModel<>()); - VBox.setVgrow(listView, Priority.ALWAYS); - - scrollDownBackground = new Pane(); - scrollDownBackground.getStyleClass().add("scroll-down-bg"); - - scrollDownImageView = new ImageView(); - scrollDownBadge = new Badge(scrollDownImageView); - scrollDownBadge.setMaxSize(25, 25); - scrollDownBadge.getStyleClass().add("chat-messages-badge"); - scrollDownBadge.setPosition(Pos.BOTTOM_RIGHT); - scrollDownBadge.setBadgeInsets(new Insets(20, 10, -2, 55)); - scrollDownBadge.setCursor(Cursor.HAND); - - scrollDownTooltip = new BisqTooltip(Res.get("chat.listView.scrollDown")); - Tooltip.install(scrollDownBadge, scrollDownTooltip); - - StackPane.setAlignment(scrollDownBadge, Pos.BOTTOM_CENTER); - StackPane.setAlignment(scrollDownBackground, Pos.BOTTOM_CENTER); - StackPane.setMargin(scrollDownBackground, new Insets(0, 15, 0, 0)); - root.setAlignment(Pos.CENTER); - root.getChildren().addAll(listView, scrollDownBackground, scrollDownBadge); - } - - @Override - protected void onViewAttached() { - ListViewUtil.findScrollbarAsync(listView, Orientation.VERTICAL, 1000).whenComplete((scrollBar, throwable) -> { - if (throwable != null) { - log.error("Find scrollbar failed", throwable); - return; - } - this.scrollBar = scrollBar; - if (scrollBar.isPresent()) { - scrollBar.get().valueProperty().bindBidirectional(model.getScrollValue()); - model.scrollBarVisible.bind(scrollBar.get().visibleProperty()); - controller.onScrollToBottom(); - } else { - log.error("scrollBar is empty"); - } - }); - - scrollDownBackground.visibleProperty().bind(model.getShowScrolledDownButton()); - scrollDownBackground.managedProperty().bind(model.getShowScrolledDownButton()); - - scrollDownBadge.textProperty().bind(model.numUnReadMessages); - + @Override + protected void onViewDetached() { + scrollBar.ifPresent(scrollbar -> scrollbar.valueProperty().unbindBidirectional(model.getScrollValue())); + model.getScrollBarVisible().unbind(); + scrollDownBackground.visibleProperty().unbind(); + scrollDownBackground.managedProperty().unbind(); + scrollDownBadge.textProperty().unbind(); + hasUnreadMessagesPin.unsubscribe(); + showScrolledDownButtonPin.unsubscribe(); + + scrollDownBadge.setOnMouseClicked(null); + if (fadeInScrollDownBadgeTimeline != null) { + fadeInScrollDownBadgeTimeline.stop(); + fadeInScrollDownBadgeTimeline = null; scrollDownBadge.setOpacity(0); - showScrolledDownButtonPin = EasyBind.subscribe(model.showScrolledDownButton, showScrolledDownButton -> { - if (showScrolledDownButton == null) { - return; - } - if (fadeInScrollDownBadgeTimeline != null) { - fadeInScrollDownBadgeTimeline.stop(); - } - if (showScrolledDownButton) { - fadeInScrollDownBadge(); - } else { - scrollDownBadge.setOpacity(0); - } - }); - hasUnreadMessagesPin = EasyBind.subscribe(model.hasUnreadMessages, hasUnreadMessages -> { - if (hasUnreadMessages) { - scrollDownImageView.setOpacity(1); - scrollDownImageView.setId("scroll-down-green"); - scrollDownTooltip.setText(Res.get("chat.listView.scrollDown.newMessages")); - } else { - scrollDownImageView.setOpacity(0.5); - scrollDownImageView.setId("scroll-down-white"); - scrollDownTooltip.setText(Res.get("chat.listView.scrollDown")); - } - }); - - scrollDownBadge.setOnMouseClicked(e -> controller.onScrollToBottom()); - - if (ChatUtil.isCommonChat(model.getChatChannelDomain()) && model.isPublicChannel.get()) { - placeholderTitle.setText(Res.get("chat.messagebox.noChats.placeholder.title")); - placeholderDescription.setText(Res.get("chat.messagebox.noChats.placeholder.description", - model.getSelectedChannel().get().getDisplayString())); - } else { - placeholderTitle.setText(""); - placeholderDescription.setText(""); - } } + } - @Override - protected void onViewDetached() { - scrollBar.ifPresent(scrollbar -> scrollbar.valueProperty().unbindBidirectional(model.getScrollValue())); - model.scrollBarVisible.unbind(); - scrollDownBackground.visibleProperty().unbind(); - scrollDownBackground.managedProperty().unbind(); - scrollDownBadge.textProperty().unbind(); - hasUnreadMessagesPin.unsubscribe(); - showScrolledDownButtonPin.unsubscribe(); - - scrollDownBadge.setOnMouseClicked(null); - if (fadeInScrollDownBadgeTimeline != null) { - fadeInScrollDownBadgeTimeline.stop(); - fadeInScrollDownBadgeTimeline = null; - scrollDownBadge.setOpacity(0); - } - } - - private void fadeInScrollDownBadge() { - if (!Transitions.getUseAnimations()) { - scrollDownBadge.setOpacity(1); - return; - } - - fadeInScrollDownBadgeTimeline = new Timeline(); - scrollDownBadge.setOpacity(0); - ObservableList keyFrames = fadeInScrollDownBadgeTimeline.getKeyFrames(); - keyFrames.add(new KeyFrame(Duration.millis(0), - new KeyValue(scrollDownBadge.opacityProperty(), 0, Interpolator.LINEAR) - )); - // Add a delay before starting fade-in to deal with a render delay when adding a - // list item. - keyFrames.add(new KeyFrame(Duration.millis(100), - new KeyValue(scrollDownBadge.opacityProperty(), 0, Interpolator.EASE_OUT) - )); - keyFrames.add(new KeyFrame(Duration.millis(400), - new KeyValue(scrollDownBadge.opacityProperty(), 1, Interpolator.EASE_OUT) - )); - fadeInScrollDownBadgeTimeline.play(); - } + private void fadeInScrollDownBadge() { + if (!Transitions.getUseAnimations()) { + scrollDownBadge.setOpacity(1); + return; + } + + fadeInScrollDownBadgeTimeline = new Timeline(); + scrollDownBadge.setOpacity(0); + ObservableList keyFrames = fadeInScrollDownBadgeTimeline.getKeyFrames(); + keyFrames.add(new KeyFrame(Duration.millis(0), + new KeyValue(scrollDownBadge.opacityProperty(), 0, Interpolator.LINEAR) + )); + // Add a delay before starting fade-in to deal with a render delay when adding a + // list item. + keyFrames.add(new KeyFrame(Duration.millis(100), + new KeyValue(scrollDownBadge.opacityProperty(), 0, Interpolator.EASE_OUT) + )); + keyFrames.add(new KeyFrame(Duration.millis(400), + new KeyValue(scrollDownBadge.opacityProperty(), 1, Interpolator.EASE_OUT) + )); + fadeInScrollDownBadgeTimeline.play(); } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/BubbleMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/BubbleMessageBox.java index 50e3b3fe81..5a60beb51e 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/BubbleMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/BubbleMessageBox.java @@ -26,7 +26,8 @@ import bisq.desktop.components.controls.BisqTooltip; import bisq.desktop.main.content.components.UserProfileIcon; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; import javafx.geometry.Pos; @@ -46,8 +47,8 @@ public abstract class BubbleMessageBox extends MessageBox { protected final ChatMessageListItem> item; protected final ListView>> list; - protected final ChatMessagesListView.Controller controller; - protected final ChatMessagesListView.Model model; + protected final ChatMessagesListController controller; + protected final ChatMessagesListModel model; protected final UserProfileIcon userProfileIcon = new UserProfileIcon(60); protected final HBox reactionsHBox = new HBox(20); protected final VBox quotedMessageVBox, contentVBox; @@ -57,8 +58,8 @@ public abstract class BubbleMessageBox extends MessageBox { public BubbleMessageBox(ChatMessageListItem> item, ListView>> list, - ChatMessagesListView.Controller controller, - ChatMessagesListView.Model model) { + ChatMessagesListController controller, + ChatMessagesListModel model) { this.item = item; this.list = list; this.controller = controller; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/LeaveChatMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/LeaveChatMessageBox.java index c18823d5f1..632c5efcae 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/LeaveChatMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/LeaveChatMessageBox.java @@ -22,13 +22,13 @@ import bisq.desktop.common.utils.ImageUtil; import bisq.desktop.main.content.bisq_easy.open_trades.MyProtocolLogMessageBox; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; import bisq.i18n.Res; import javafx.scene.control.Hyperlink; public final class LeaveChatMessageBox extends MyProtocolLogMessageBox { public LeaveChatMessageBox(ChatMessageListItem> item, - ChatMessagesListView.Controller controller) { + ChatMessagesListController controller) { super(item, controller); Hyperlink hyperlink = new Hyperlink(Res.get("chat.leave")); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MyMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MyMessageBox.java index 63e9a20475..18675da2d8 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MyMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MyMessageBox.java @@ -25,7 +25,8 @@ import bisq.desktop.components.controls.BisqTextArea; import bisq.desktop.components.controls.BisqTooltip; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; import bisq.i18n.Res; import bisq.network.p2p.services.confidential.ack.MessageDeliveryStatus; import de.jensd.fx.fontawesome.AwesomeDude; @@ -54,7 +55,7 @@ public final class MyMessageBox extends BubbleMessageBox { public MyMessageBox(ChatMessageListItem> item, ListView>> list, - ChatMessagesListView.Controller controller, ChatMessagesListView.Model model) { + ChatMessagesListController controller, ChatMessagesListModel model) { super(item, list, controller, model); quotedMessageVBox.setId("chat-message-quote-box-my-msg"); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/PeerMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/PeerMessageBox.java index b82a7e986a..9f6019deba 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/PeerMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/PeerMessageBox.java @@ -25,7 +25,8 @@ import bisq.desktop.components.controls.BisqPopupMenu; import bisq.desktop.components.controls.BisqPopupMenuItem; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListView; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; import bisq.i18n.Res; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; @@ -44,7 +45,7 @@ public class PeerMessageBox extends BubbleMessageBox { public PeerMessageBox(ChatMessageListItem> item, ListView>> list, - ChatMessagesListView.Controller controller, ChatMessagesListView.Model model) { + ChatMessagesListController controller, ChatMessagesListModel model) { super(item, list, controller, model); setUpPeerMessage(); From e66450032d104f6e1eadd55265c08651ad7b9e08 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 17:34:58 +0700 Subject: [PATCH 094/119] Refactor: Move package --- .../content/bisq_easy/offerbook/MyOfferMessageBox.java | 2 +- .../content/bisq_easy/offerbook/PeerOfferMessageBox.java | 2 +- .../bisq_easy/open_trades/PeerProtocolLogMessageBox.java | 2 +- .../list_view/ChatMessageListCellFactory.java | 8 ++++---- .../{ => list_view}/messages/BubbleMessageBox.java | 2 +- .../{ => list_view}/messages/LeaveChatMessageBox.java | 2 +- .../{ => list_view}/messages/MessageBox.java | 2 +- .../{ => list_view}/messages/MyMessageBox.java | 2 +- .../{ => list_view}/messages/PeerMessageBox.java | 4 ++-- 9 files changed, 13 insertions(+), 13 deletions(-) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/{ => list_view}/messages/BubbleMessageBox.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/{ => list_view}/messages/LeaveChatMessageBox.java (95%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/{ => list_view}/messages/MessageBox.java (91%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/{ => list_view}/messages/MyMessageBox.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/{ => list_view}/messages/PeerMessageBox.java (97%) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java index 3890422d92..650c90bcdc 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java @@ -25,7 +25,7 @@ import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; -import bisq.desktop.main.content.components.chat_messages.messages.BubbleMessageBox; +import bisq.desktop.main.content.components.chat_messages.list_view.messages.BubbleMessageBox; import bisq.i18n.Res; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java index e938e2d4bd..d44ac14494 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java @@ -24,7 +24,7 @@ import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; -import bisq.desktop.main.content.components.chat_messages.messages.PeerMessageBox; +import bisq.desktop.main.content.components.chat_messages.list_view.messages.PeerMessageBox; import bisq.i18n.Res; import javafx.geometry.Insets; import javafx.geometry.Pos; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java index 039c9d1f14..5ed3b05852 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java @@ -20,7 +20,7 @@ import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.messages.MessageBox; +import bisq.desktop.main.content.components.chat_messages.list_view.messages.MessageBox; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Label; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java index 0a5bbf7e6b..a16d1bff15 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java @@ -24,10 +24,10 @@ import bisq.desktop.main.content.bisq_easy.open_trades.MyProtocolLogMessageBox; import bisq.desktop.main.content.bisq_easy.open_trades.PeerProtocolLogMessageBox; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.messages.LeaveChatMessageBox; -import bisq.desktop.main.content.components.chat_messages.messages.MessageBox; -import bisq.desktop.main.content.components.chat_messages.messages.MyMessageBox; -import bisq.desktop.main.content.components.chat_messages.messages.PeerMessageBox; +import bisq.desktop.main.content.components.chat_messages.list_view.messages.LeaveChatMessageBox; +import bisq.desktop.main.content.components.chat_messages.list_view.messages.MessageBox; +import bisq.desktop.main.content.components.chat_messages.list_view.messages.MyMessageBox; +import bisq.desktop.main.content.components.chat_messages.list_view.messages.PeerMessageBox; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/BubbleMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/BubbleMessageBox.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/BubbleMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/BubbleMessageBox.java index 5a60beb51e..9b54e21247 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/BubbleMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/BubbleMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages.messages; +package bisq.desktop.main.content.components.chat_messages.list_view.messages; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/LeaveChatMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/LeaveChatMessageBox.java similarity index 95% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/LeaveChatMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/LeaveChatMessageBox.java index 632c5efcae..dca4172e5f 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/LeaveChatMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/LeaveChatMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages.messages; +package bisq.desktop.main.content.components.chat_messages.list_view.messages; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/MessageBox.java similarity index 91% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/MessageBox.java index 00a3a80e57..05a9c31c21 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/MessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages.messages; +package bisq.desktop.main.content.components.chat_messages.list_view.messages; import javafx.scene.layout.VBox; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MyMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/MyMessageBox.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MyMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/MyMessageBox.java index 18675da2d8..3a9c4632b8 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/MyMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/MyMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages.messages; +package bisq.desktop.main.content.components.chat_messages.list_view.messages; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/PeerMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/PeerMessageBox.java similarity index 97% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/PeerMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/PeerMessageBox.java index 9f6019deba..0d57d59193 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/messages/PeerMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/PeerMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages.messages; +package bisq.desktop.main.content.components.chat_messages.list_view.messages; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -49,7 +49,7 @@ public PeerMessageBox(ChatMessageListItem Date: Sun, 31 Mar 2024 17:35:27 +0700 Subject: [PATCH 095/119] Refactor: Rename package --- .../content/bisq_easy/offerbook/MyOfferMessageBox.java | 2 +- .../content/bisq_easy/offerbook/PeerOfferMessageBox.java | 2 +- .../bisq_easy/open_trades/PeerProtocolLogMessageBox.java | 2 +- .../list_view/ChatMessageListCellFactory.java | 8 ++++---- .../{messages => message_box}/BubbleMessageBox.java | 2 +- .../{messages => message_box}/LeaveChatMessageBox.java | 2 +- .../list_view/{messages => message_box}/MessageBox.java | 2 +- .../list_view/{messages => message_box}/MyMessageBox.java | 2 +- .../{messages => message_box}/PeerMessageBox.java | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/{messages => message_box}/BubbleMessageBox.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/{messages => message_box}/LeaveChatMessageBox.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/{messages => message_box}/MessageBox.java (98%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/{messages => message_box}/MyMessageBox.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/{messages => message_box}/PeerMessageBox.java (99%) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java index 650c90bcdc..a1000f4d40 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java @@ -25,7 +25,7 @@ import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; -import bisq.desktop.main.content.components.chat_messages.list_view.messages.BubbleMessageBox; +import bisq.desktop.main.content.components.chat_messages.list_view.message_box.BubbleMessageBox; import bisq.i18n.Res; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java index d44ac14494..fc02899e7a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java @@ -24,7 +24,7 @@ import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; -import bisq.desktop.main.content.components.chat_messages.list_view.messages.PeerMessageBox; +import bisq.desktop.main.content.components.chat_messages.list_view.message_box.PeerMessageBox; import bisq.i18n.Res; import javafx.geometry.Insets; import javafx.geometry.Pos; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java index 5ed3b05852..42152cbf08 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java @@ -20,7 +20,7 @@ import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.messages.MessageBox; +import bisq.desktop.main.content.components.chat_messages.list_view.message_box.MessageBox; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Label; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java index a16d1bff15..b4aca3a0a7 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java @@ -24,10 +24,10 @@ import bisq.desktop.main.content.bisq_easy.open_trades.MyProtocolLogMessageBox; import bisq.desktop.main.content.bisq_easy.open_trades.PeerProtocolLogMessageBox; import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.messages.LeaveChatMessageBox; -import bisq.desktop.main.content.components.chat_messages.list_view.messages.MessageBox; -import bisq.desktop.main.content.components.chat_messages.list_view.messages.MyMessageBox; -import bisq.desktop.main.content.components.chat_messages.list_view.messages.PeerMessageBox; +import bisq.desktop.main.content.components.chat_messages.list_view.message_box.LeaveChatMessageBox; +import bisq.desktop.main.content.components.chat_messages.list_view.message_box.MessageBox; +import bisq.desktop.main.content.components.chat_messages.list_view.message_box.MyMessageBox; +import bisq.desktop.main.content.components.chat_messages.list_view.message_box.PeerMessageBox; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/BubbleMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/BubbleMessageBox.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/BubbleMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/BubbleMessageBox.java index 9b54e21247..aacbab295a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/BubbleMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/BubbleMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages.list_view.messages; +package bisq.desktop.main.content.components.chat_messages.list_view.message_box; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/LeaveChatMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/LeaveChatMessageBox.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/LeaveChatMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/LeaveChatMessageBox.java index dca4172e5f..9bcd4bf5f8 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/LeaveChatMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/LeaveChatMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages.list_view.messages; +package bisq.desktop.main.content.components.chat_messages.list_view.message_box; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/MessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/MessageBox.java similarity index 98% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/MessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/MessageBox.java index 05a9c31c21..70fef22af8 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/MessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/MessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages.list_view.messages; +package bisq.desktop.main.content.components.chat_messages.list_view.message_box; import javafx.scene.layout.VBox; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/MyMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/MyMessageBox.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/MyMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/MyMessageBox.java index 3a9c4632b8..e4dde96355 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/MyMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/MyMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages.list_view.messages; +package bisq.desktop.main.content.components.chat_messages.list_view.message_box; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/PeerMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/PeerMessageBox.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/PeerMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/PeerMessageBox.java index 0d57d59193..a854fc379c 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/messages/PeerMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/PeerMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages.list_view.messages; +package bisq.desktop.main.content.components.chat_messages.list_view.message_box; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; From 809a4de021ee7e33481d56a69f5e37d64ef03f00 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 17:36:15 +0700 Subject: [PATCH 096/119] Refactor: Move class --- .../main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java | 2 +- .../bisq/desktop/main/content/bisq_easy/offerbook/Filters.java | 2 +- .../main/content/bisq_easy/offerbook/MyOfferMessageBox.java | 2 +- .../main/content/bisq_easy/offerbook/PeerOfferMessageBox.java | 2 +- .../content/bisq_easy/open_trades/MyProtocolLogMessageBox.java | 2 +- .../bisq_easy/open_trades/PeerProtocolLogMessageBox.java | 2 +- .../content/components/chat_messages/ChatMessagesComponent.java | 1 + .../chat_messages/list_view/ChatMessageListCellFactory.java | 1 - .../chat_messages/{ => list_view}/ChatMessageListItem.java | 2 +- .../chat_messages/list_view/ChatMessagesListController.java | 1 - .../chat_messages/list_view/ChatMessagesListModel.java | 1 - .../chat_messages/list_view/ChatMessagesListView.java | 1 - .../chat_messages/list_view/message_box/BubbleMessageBox.java | 2 +- .../list_view/message_box/LeaveChatMessageBox.java | 2 +- .../chat_messages/list_view/message_box/MyMessageBox.java | 2 +- .../chat_messages/list_view/message_box/PeerMessageBox.java | 2 +- 16 files changed, 12 insertions(+), 15 deletions(-) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/{ => list_view}/ChatMessageListItem.java (99%) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java index 8a9575ff02..d80971f473 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java @@ -29,7 +29,7 @@ import bisq.desktop.components.table.BisqTableColumn; import bisq.desktop.components.table.BisqTableView; import bisq.desktop.main.content.chat.ChatView; -import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; import bisq.i18n.Res; import javafx.beans.binding.Bindings; import javafx.collections.ListChangeListener; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java index 8458d41261..039828894c 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java @@ -19,7 +19,7 @@ import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; -import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; import lombok.Getter; import java.util.function.Predicate; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java index a1000f4d40..e24847c536 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java @@ -22,7 +22,7 @@ import bisq.desktop.components.containers.Spacer; import bisq.desktop.components.controls.DropdownMenu; import bisq.desktop.components.controls.DropdownMenuItem; -import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; import bisq.desktop.main.content.components.chat_messages.list_view.message_box.BubbleMessageBox; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java index fc02899e7a..835879690c 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java @@ -21,7 +21,7 @@ import bisq.chat.ChatMessage; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; import bisq.desktop.components.containers.Spacer; -import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; import bisq.desktop.main.content.components.chat_messages.list_view.message_box.PeerMessageBox; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java index 29d5168a8d..01a39fc9b9 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java @@ -21,7 +21,7 @@ import bisq.chat.ChatMessage; import bisq.desktop.common.Icons; import bisq.desktop.components.controls.BisqTooltip; -import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; import bisq.network.p2p.services.confidential.ack.MessageDeliveryStatus; import de.jensd.fx.fontawesome.AwesomeDude; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java index 42152cbf08..0d92de7dde 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java @@ -19,7 +19,7 @@ import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; -import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; import bisq.desktop.main.content.components.chat_messages.list_view.message_box.MessageBox; import javafx.geometry.Insets; import javafx.geometry.Pos; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java index 3e5759bdca..17f8b70f90 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java @@ -37,6 +37,7 @@ import bisq.desktop.main.content.components.ChatMentionPopupMenu; import bisq.desktop.main.content.components.CitationBlock; import bisq.desktop.main.content.components.UserProfileSelection; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; import bisq.i18n.Res; import bisq.settings.SettingsService; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java index b4aca3a0a7..a39d03d5ef 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java @@ -23,7 +23,6 @@ import bisq.desktop.main.content.bisq_easy.offerbook.PeerOfferMessageBox; import bisq.desktop.main.content.bisq_easy.open_trades.MyProtocolLogMessageBox; import bisq.desktop.main.content.bisq_easy.open_trades.PeerProtocolLogMessageBox; -import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; import bisq.desktop.main.content.components.chat_messages.list_view.message_box.LeaveChatMessageBox; import bisq.desktop.main.content.components.chat_messages.list_view.message_box.MessageBox; import bisq.desktop.main.content.components.chat_messages.list_view.message_box.MyMessageBox; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageListItem.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListItem.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageListItem.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListItem.java index fe20ae5afc..ddf68aa9aa 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageListItem.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListItem.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages; +package bisq.desktop.main.content.components.chat_messages.list_view; import bisq.bonded_roles.market_price.MarketPriceService; import bisq.chat.ChatChannel; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListController.java index f8bd923396..b8b4c35407 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListController.java @@ -33,7 +33,6 @@ import bisq.desktop.main.content.bisq_easy.BisqEasyServiceUtil; import bisq.desktop.main.content.bisq_easy.take_offer.TakeOfferController; import bisq.desktop.main.content.components.ReportToModeratorWindow; -import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; import bisq.i18n.Res; import bisq.network.NetworkService; import bisq.network.identity.NetworkId; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListModel.java index ce42b9dc43..37a786d6d5 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListModel.java @@ -3,7 +3,6 @@ import bisq.chat.ChatChannel; import bisq.chat.ChatChannelDomain; import bisq.chat.ChatMessage; -import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; import bisq.user.identity.UserIdentityService; import javafx.beans.property.*; import javafx.collections.FXCollections; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListView.java index 940ff000c4..3968a733b2 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListView.java @@ -8,7 +8,6 @@ import bisq.desktop.components.list_view.ListViewUtil; import bisq.desktop.components.list_view.NoSelectionModel; import bisq.desktop.main.content.chat.ChatUtil; -import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; import bisq.i18n.Res; import javafx.animation.Interpolator; import javafx.animation.KeyFrame; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/BubbleMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/BubbleMessageBox.java index aacbab295a..7d587cea49 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/BubbleMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/BubbleMessageBox.java @@ -25,7 +25,7 @@ import bisq.desktop.common.utils.ClipboardUtil; import bisq.desktop.components.controls.BisqTooltip; import bisq.desktop.main.content.components.UserProfileIcon; -import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; import de.jensd.fx.fontawesome.AwesomeIcon; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/LeaveChatMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/LeaveChatMessageBox.java index 9bcd4bf5f8..bf2baa8b2c 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/LeaveChatMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/LeaveChatMessageBox.java @@ -21,7 +21,7 @@ import bisq.chat.ChatMessage; import bisq.desktop.common.utils.ImageUtil; import bisq.desktop.main.content.bisq_easy.open_trades.MyProtocolLogMessageBox; -import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; import bisq.i18n.Res; import javafx.scene.control.Hyperlink; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/MyMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/MyMessageBox.java index e4dde96355..7847bdfea5 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/MyMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/MyMessageBox.java @@ -24,7 +24,7 @@ import bisq.desktop.components.containers.Spacer; import bisq.desktop.components.controls.BisqTextArea; import bisq.desktop.components.controls.BisqTooltip; -import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; import bisq.i18n.Res; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/PeerMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/PeerMessageBox.java index a854fc379c..caae298436 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/PeerMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/PeerMessageBox.java @@ -24,7 +24,7 @@ import bisq.desktop.components.controls.BisqPopup; import bisq.desktop.components.controls.BisqPopupMenu; import bisq.desktop.components.controls.BisqPopupMenuItem; -import bisq.desktop.main.content.components.chat_messages.ChatMessageListItem; +import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; import bisq.i18n.Res; From bcc56fc39997c54ecebc08f5fba70a55be6981ee Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 17:41:44 +0700 Subject: [PATCH 097/119] Refactor: Move package --- .../bisq_easy/offerbook/BisqEasyOfferbookView.java | 2 +- .../main/content/bisq_easy/offerbook/Filters.java | 2 +- .../bisq_easy/offerbook/MyOfferMessageBox.java | 8 ++++---- .../bisq_easy/offerbook/PeerOfferMessageBox.java | 8 ++++---- .../open_trades/MyProtocolLogMessageBox.java | 4 ++-- .../open_trades/PeerProtocolLogMessageBox.java | 4 ++-- .../list_view/ChatMessageListCellFactory.java | 10 +++++----- .../list_view/ChatMessageListItem.java | 2 +- .../list_view/ChatMessagesListController.java | 2 +- .../list_view/ChatMessagesListModel.java | 2 +- .../list_view/ChatMessagesListView.java | 2 +- .../list_view/message_box/BubbleMessageBox.java | 8 ++++---- .../list_view/message_box/LeaveChatMessageBox.java | 6 +++--- .../list_view/message_box/MessageBox.java | 2 +- .../list_view/message_box/MyMessageBox.java | 14 +++++++------- .../list_view/message_box/PeerMessageBox.java | 8 ++++---- .../chat_messages/ChatMessagesComponent.java | 4 ++-- 17 files changed, 44 insertions(+), 44 deletions(-) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/{components/chat_messages => chat}/list_view/ChatMessageListCellFactory.java (93%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/{components/chat_messages => chat}/list_view/ChatMessageListItem.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/{components/chat_messages => chat}/list_view/ChatMessagesListController.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/{components/chat_messages => chat}/list_view/ChatMessagesListModel.java (97%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/{components/chat_messages => chat}/list_view/ChatMessagesListView.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/{components/chat_messages => chat}/list_view/message_box/BubbleMessageBox.java (95%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/{components/chat_messages => chat}/list_view/message_box/LeaveChatMessageBox.java (85%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/{components/chat_messages => chat}/list_view/message_box/MessageBox.java (91%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/{components/chat_messages => chat}/list_view/message_box/MyMessageBox.java (95%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/{components/chat_messages => chat}/list_view/message_box/PeerMessageBox.java (94%) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java index d80971f473..9fe89ad767 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java @@ -29,7 +29,7 @@ import bisq.desktop.components.table.BisqTableColumn; import bisq.desktop.components.table.BisqTableView; import bisq.desktop.main.content.chat.ChatView; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; import bisq.i18n.Res; import javafx.beans.binding.Bindings; import javafx.collections.ListChangeListener; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java index 039828894c..8af389c617 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java @@ -19,7 +19,7 @@ import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; import lombok.Getter; import java.util.function.Predicate; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java index e24847c536..af842c320b 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java @@ -22,10 +22,10 @@ import bisq.desktop.components.containers.Spacer; import bisq.desktop.components.controls.DropdownMenu; import bisq.desktop.components.controls.DropdownMenuItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; -import bisq.desktop.main.content.components.chat_messages.list_view.message_box.BubbleMessageBox; +import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.list_view.ChatMessagesListModel; +import bisq.desktop.main.content.chat.list_view.message_box.BubbleMessageBox; import bisq.i18n.Res; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java index 835879690c..c910409467 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java @@ -21,10 +21,10 @@ import bisq.chat.ChatMessage; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; import bisq.desktop.components.containers.Spacer; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; -import bisq.desktop.main.content.components.chat_messages.list_view.message_box.PeerMessageBox; +import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.list_view.ChatMessagesListModel; +import bisq.desktop.main.content.chat.list_view.message_box.PeerMessageBox; import bisq.i18n.Res; import javafx.geometry.Insets; import javafx.geometry.Pos; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java index 01a39fc9b9..1e253a7376 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java @@ -21,8 +21,8 @@ import bisq.chat.ChatMessage; import bisq.desktop.common.Icons; import bisq.desktop.components.controls.BisqTooltip; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; import bisq.network.p2p.services.confidential.ack.MessageDeliveryStatus; import de.jensd.fx.fontawesome.AwesomeDude; import javafx.geometry.Pos; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java index 0d92de7dde..6576cf0de8 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java @@ -19,8 +19,8 @@ import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.message_box.MessageBox; +import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.list_view.message_box.MessageBox; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Label; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListCellFactory.java similarity index 93% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListCellFactory.java index a39d03d5ef..cdef970661 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListCellFactory.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListCellFactory.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages.list_view; +package bisq.desktop.main.content.chat.list_view; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -23,10 +23,10 @@ import bisq.desktop.main.content.bisq_easy.offerbook.PeerOfferMessageBox; import bisq.desktop.main.content.bisq_easy.open_trades.MyProtocolLogMessageBox; import bisq.desktop.main.content.bisq_easy.open_trades.PeerProtocolLogMessageBox; -import bisq.desktop.main.content.components.chat_messages.list_view.message_box.LeaveChatMessageBox; -import bisq.desktop.main.content.components.chat_messages.list_view.message_box.MessageBox; -import bisq.desktop.main.content.components.chat_messages.list_view.message_box.MyMessageBox; -import bisq.desktop.main.content.components.chat_messages.list_view.message_box.PeerMessageBox; +import bisq.desktop.main.content.chat.list_view.message_box.LeaveChatMessageBox; +import bisq.desktop.main.content.chat.list_view.message_box.MessageBox; +import bisq.desktop.main.content.chat.list_view.message_box.MyMessageBox; +import bisq.desktop.main.content.chat.list_view.message_box.PeerMessageBox; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListItem.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListItem.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java index ddf68aa9aa..942bfe9d41 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessageListItem.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages.list_view; +package bisq.desktop.main.content.chat.list_view; import bisq.bonded_roles.market_price.MarketPriceService; import bisq.chat.ChatChannel; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListController.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListController.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListController.java index b8b4c35407..09d58d0a93 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListController.java @@ -1,4 +1,4 @@ -package bisq.desktop.main.content.components.chat_messages.list_view; +package bisq.desktop.main.content.chat.list_view; import bisq.bisq_easy.BisqEasyService; import bisq.bisq_easy.NavigationTarget; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListModel.java similarity index 97% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListModel.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListModel.java index 37a786d6d5..dfe05e10a4 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListModel.java @@ -1,4 +1,4 @@ -package bisq.desktop.main.content.components.chat_messages.list_view; +package bisq.desktop.main.content.chat.list_view; import bisq.chat.ChatChannel; import bisq.chat.ChatChannelDomain; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListView.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListView.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListView.java index 3968a733b2..21cde6ee49 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/ChatMessagesListView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListView.java @@ -1,4 +1,4 @@ -package bisq.desktop.main.content.components.chat_messages.list_view; +package bisq.desktop.main.content.chat.list_view; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/BubbleMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/BubbleMessageBox.java similarity index 95% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/BubbleMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/BubbleMessageBox.java index 7d587cea49..ab5294032f 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/BubbleMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/BubbleMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages.list_view.message_box; +package bisq.desktop.main.content.chat.list_view.message_box; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -24,10 +24,10 @@ import bisq.desktop.common.Icons; import bisq.desktop.common.utils.ClipboardUtil; import bisq.desktop.components.controls.BisqTooltip; +import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.list_view.ChatMessagesListModel; import bisq.desktop.main.content.components.UserProfileIcon; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; import javafx.geometry.Pos; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/LeaveChatMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/LeaveChatMessageBox.java similarity index 85% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/LeaveChatMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/LeaveChatMessageBox.java index bf2baa8b2c..531a0286d3 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/LeaveChatMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/LeaveChatMessageBox.java @@ -15,14 +15,14 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages.list_view.message_box; +package bisq.desktop.main.content.chat.list_view.message_box; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; import bisq.desktop.common.utils.ImageUtil; import bisq.desktop.main.content.bisq_easy.open_trades.MyProtocolLogMessageBox; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; import bisq.i18n.Res; import javafx.scene.control.Hyperlink; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/MessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/MessageBox.java similarity index 91% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/MessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/MessageBox.java index 70fef22af8..bba6a897d0 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/MessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/MessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages.list_view.message_box; +package bisq.desktop.main.content.chat.list_view.message_box; import javafx.scene.layout.VBox; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/MyMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/MyMessageBox.java similarity index 95% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/MyMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/MyMessageBox.java index 7847bdfea5..8846e2eeaf 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/list_view/message_box/MyMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/MyMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components.chat_messages.list_view.message_box; +package bisq.desktop.main.content.chat.list_view.message_box; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -24,9 +24,9 @@ import bisq.desktop.components.containers.Spacer; import bisq.desktop.components.controls.BisqTextArea; import bisq.desktop.components.controls.BisqTooltip; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; +import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.list_view.ChatMessagesListModel; import bisq.i18n.Res; import bisq.network.p2p.services.confidential.ack.MessageDeliveryStatus; import de.jensd.fx.fontawesome.AwesomeDude; @@ -122,7 +122,7 @@ public MyMessageBox(ChatMessageListItem. */ -package bisq.desktop.main.content.components.chat_messages.list_view.message_box; +package bisq.desktop.main.content.chat.list_view.message_box; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -24,9 +24,9 @@ import bisq.desktop.components.controls.BisqPopup; import bisq.desktop.components.controls.BisqPopupMenu; import bisq.desktop.components.controls.BisqPopupMenuItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListModel; +import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.list_view.ChatMessagesListModel; import bisq.i18n.Res; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java index 17f8b70f90..07fb8e35a9 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java @@ -34,11 +34,11 @@ import bisq.desktop.components.controls.BisqTooltip; import bisq.desktop.components.overlay.Popup; import bisq.desktop.main.content.chat.ChatUtil; +import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; import bisq.desktop.main.content.components.ChatMentionPopupMenu; import bisq.desktop.main.content.components.CitationBlock; import bisq.desktop.main.content.components.UserProfileSelection; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessageListItem; -import bisq.desktop.main.content.components.chat_messages.list_view.ChatMessagesListController; import bisq.i18n.Res; import bisq.settings.SettingsService; import bisq.user.identity.UserIdentity; From 9b32ed83174d0b5a4a2ab1408aa7abe79c31b13a Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 18:06:13 +0700 Subject: [PATCH 098/119] Refactor: Move inner MCV classes out as the component has become too large. Rename methods Cleanups --- .../mediator/MediatorController.java | 8 +- .../BisqEasyOfferbookController.java | 10 +- .../BisqEasyOpenTradesController.java | 2 +- .../BisqEasyPrivateChatsController.java | 2 +- .../main/content/chat/BaseChatController.java | 20 +- .../main/content/chat/ChatController.java | 4 +- .../priv/CommonPrivateChatsController.java | 2 +- .../pub/CommonPublicChatController.java | 2 +- .../chat/list_view/ChatMessageListItem.java | 2 +- .../chat/priv/PrivateChatsController.java | 2 +- .../ChatMessageContainerController.java | 313 ++++++++++ .../ChatMessageContainerModel.java | 45 ++ .../ChatMessageContainerView.java | 208 +++++++ .../chat_messages/ChatMessagesComponent.java | 588 ------------------ 14 files changed, 593 insertions(+), 615 deletions(-) create mode 100644 apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerController.java create mode 100644 apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerModel.java create mode 100644 apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerView.java delete mode 100644 apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorController.java index 54a9ca80cd..4dcc2fa64c 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorController.java @@ -28,7 +28,7 @@ import bisq.desktop.common.observable.FxBindings; import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Controller; -import bisq.desktop.main.content.components.chat_messages.ChatMessagesComponent; +import bisq.desktop.main.content.components.chat_messages.ChatMessageContainerController; import bisq.support.mediation.MediationCase; import bisq.support.mediation.MediationRequest; import bisq.support.mediation.MediatorService; @@ -55,7 +55,7 @@ public class MediatorController implements Controller { protected final ChatService chatService; protected final UserIdentityService userIdentityService; protected final UserProfileService userProfileService; - protected final ChatMessagesComponent chatMessagesComponent; + protected final ChatMessageContainerController chatMessageContainerController; private final BisqEasyOpenTradeChannelService channelService; private final BisqEasyOpenTradeSelectionService selectionService; @@ -75,12 +75,12 @@ public MediatorController(ServiceProvider serviceProvider) { mediatorService = serviceProvider.getSupportService().getMediatorService(); bisqEasyOpenTradeChannelService = chatService.getBisqEasyOpenTradeChannelService(); - chatMessagesComponent = new ChatMessagesComponent(serviceProvider, ChatChannelDomain.BISQ_EASY_OPEN_TRADES, e -> { + chatMessageContainerController = new ChatMessageContainerController(serviceProvider, ChatChannelDomain.BISQ_EASY_OPEN_TRADES, e -> { }); mediationCaseHeader = new MediationCaseHeader(serviceProvider, this::updateFilteredList, this::updateFilteredList); model = new MediatorModel(); - view = new MediatorView(model, this, mediationCaseHeader.getRoot(), chatMessagesComponent.getRoot()); + view = new MediatorView(model, this, mediationCaseHeader.getRoot(), chatMessageContainerController.getView().getRoot()); itemListener = observable -> update(); } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookController.java index a63165eead..1f2005bdc4 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookController.java @@ -106,7 +106,7 @@ public BisqEasyOfferbookModel createAndGetModel(ChatChannelDomain chatChannelDom public BisqEasyOfferbookView createAndGetView() { return new BisqEasyOfferbookView(model, this, - chatMessagesComponent.getRoot(), + chatMessageContainerController.getView().getRoot(), channelSidebar.getRoot()); } @@ -168,9 +168,9 @@ public void onActivate() { if (filter == null) { // By default, show all offers (any direction or owner) model.getSelectedOfferDirectionOrOwnerFilter().set(Filters.OfferDirectionOrOwner.ALL); - chatMessagesComponent.setBisqEasyOfferDirectionOrOwnerFilterPredicate(model.getSelectedOfferDirectionOrOwnerFilter().get().getPredicate()); + chatMessageContainerController.setBisqEasyOfferDirectionOrOwnerFilterPredicate(model.getSelectedOfferDirectionOrOwnerFilter().get().getPredicate()); } else { - chatMessagesComponent.setBisqEasyOfferDirectionOrOwnerFilterPredicate(filter.getPredicate()); + chatMessageContainerController.setBisqEasyOfferDirectionOrOwnerFilterPredicate(filter.getPredicate()); } }); @@ -178,9 +178,9 @@ public void onActivate() { if (filter == null) { // By default, show all offers (with any reputation) model.getSelectedPeerReputationFilter().set(Filters.PeerReputation.ALL); - chatMessagesComponent.setBisqEasyPeerReputationFilterPredicate(model.getSelectedPeerReputationFilter().get().getPredicate()); + chatMessageContainerController.setBisqEasyPeerReputationFilterPredicate(model.getSelectedPeerReputationFilter().get().getPredicate()); } else { - chatMessagesComponent.setBisqEasyPeerReputationFilterPredicate(filter.getPredicate()); + chatMessageContainerController.setBisqEasyPeerReputationFilterPredicate(filter.getPredicate()); } }); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/BisqEasyOpenTradesController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/BisqEasyOpenTradesController.java index fbb8d2dee2..e895d0ab92 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/BisqEasyOpenTradesController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/BisqEasyOpenTradesController.java @@ -92,7 +92,7 @@ public BisqEasyOpenTradesView createAndGetView() { return new BisqEasyOpenTradesView(model, this, tradeDataHeader.getRoot(), - chatMessagesComponent.getRoot(), + chatMessageContainerController.getView().getRoot(), channelSidebar.getRoot(), tradeStateController.getView().getRoot(), openTradesWelcome.getView().getRoot()); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/private_chats/BisqEasyPrivateChatsController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/private_chats/BisqEasyPrivateChatsController.java index 3b882622d3..ea89afa135 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/private_chats/BisqEasyPrivateChatsController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/private_chats/BisqEasyPrivateChatsController.java @@ -44,7 +44,7 @@ public BisqEasyPrivateChatsModel createAndGetModel(ChatChannelDomain chatChannel public BisqEasyPrivateChatsView createAndGetView() { return new BisqEasyPrivateChatsView((BisqEasyPrivateChatsModel) model, this, - chatMessagesComponent.getRoot(), + chatMessageContainerController.getView().getRoot(), channelSidebar.getRoot()); } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/BaseChatController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/BaseChatController.java index f55758408e..ac5c19aa72 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/BaseChatController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/BaseChatController.java @@ -34,7 +34,7 @@ import bisq.desktop.components.controls.BisqIconButton; import bisq.desktop.main.content.chat.sidebar.ChannelSidebar; import bisq.desktop.main.content.chat.sidebar.UserProfileSidebar; -import bisq.desktop.main.content.components.chat_messages.ChatMessagesComponent; +import bisq.desktop.main.content.components.chat_messages.ChatMessageContainerController; import bisq.i18n.Res; import bisq.user.identity.UserIdentityService; import bisq.user.profile.UserProfile; @@ -68,7 +68,7 @@ public abstract class BaseChatController { doCloseSideBar(); - chatMessagesComponent.resetSelectedChatMessage(); + chatMessageContainerController.resetSelectedChatMessage(); }, this::openUserProfileSidebar); @@ -110,12 +110,12 @@ protected void openUserProfileSidebar(UserProfile userProfile) { model.getSelectedChannel(), () -> { doCloseSideBar(); - chatMessagesComponent.resetSelectedChatMessage(); + chatMessageContainerController.resetSelectedChatMessage(); }); model.getSideBarWidth().set(userProfileSidebar.getRoot().getMinWidth()); - userProfileSidebar.setOnSendPrivateMessageHandler(chatMessagesComponent::createAndSelectTwoPartyPrivateChatChannel); - userProfileSidebar.setIgnoreUserStateHandler(chatMessagesComponent::refreshMessages); - userProfileSidebar.setOnMentionUserHandler(chatMessagesComponent::mentionUser); + userProfileSidebar.setOnSendPrivateMessageHandler(chatMessageContainerController::createAndSelectTwoPartyPrivateChatChannel); + userProfileSidebar.setIgnoreUserStateHandler(chatMessageContainerController::refreshMessages); + userProfileSidebar.setOnMentionUserHandler(chatMessageContainerController::mentionUser); model.setChatUserDetails(Optional.of(userProfileSidebar)); model.getChatUserDetailsRoot().set(userProfileSidebar.getRoot()); } @@ -197,7 +197,7 @@ protected void applyPeersIcon(PrivateChatChannel privateChatChannel) { protected void onToggleChannelInfo() { boolean visible = !model.getChannelSidebarVisible().get(); doCloseSideBar(); - chatMessagesComponent.resetSelectedChatMessage(); + chatMessageContainerController.resetSelectedChatMessage(); model.getChannelSidebarVisible().set(visible); model.getSideBarVisible().set(visible); if (visible) { @@ -247,7 +247,7 @@ private void doCloseSideBar() { private void showChannelInfo() { channelSidebar.setChannel(model.getSelectedChannel()); channelSidebar.setOnUndoIgnoreChatUser(() -> { - chatMessagesComponent.refreshMessages(); + chatMessageContainerController.refreshMessages(); channelSidebar.setChannel(model.getSelectedChannel()); }); } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/ChatController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/ChatController.java index 1f4390d3c1..a19357abb7 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/ChatController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/ChatController.java @@ -51,9 +51,9 @@ public void onActivate() { model.getSearchText().set(""); searchTextPin = EasyBind.subscribe(model.getSearchText(), searchText -> { if (searchText == null || searchText.isEmpty()) { - chatMessagesComponent.setSearchPredicate(item -> true); + chatMessageContainerController.setSearchPredicate(item -> true); } else { - chatMessagesComponent.setSearchPredicate(item -> item.match(searchText)); + chatMessageContainerController.setSearchPredicate(item -> item.match(searchText)); } }); } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/common/priv/CommonPrivateChatsController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/common/priv/CommonPrivateChatsController.java index e5079f81af..41c2eb93c4 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/common/priv/CommonPrivateChatsController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/common/priv/CommonPrivateChatsController.java @@ -41,7 +41,7 @@ public CommonPrivateChatsModel createAndGetModel(ChatChannelDomain chatChannelDo public CommonPrivateChatsView createAndGetView() { return new CommonPrivateChatsView((CommonPrivateChatsModel) model, this, - chatMessagesComponent.getRoot(), + chatMessageContainerController.getView().getRoot(), channelSidebar.getRoot()); } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/common/pub/CommonPublicChatController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/common/pub/CommonPublicChatController.java index e74e657d1b..48a8e8195a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/common/pub/CommonPublicChatController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/common/pub/CommonPublicChatController.java @@ -38,6 +38,6 @@ public CommonPublicChatModel createAndGetModel(ChatChannelDomain chatChannelDoma @Override public CommonPublicChatView createAndGetView() { - return new CommonPublicChatView(model, this, chatMessagesComponent.getRoot(), channelSidebar.getRoot()); + return new CommonPublicChatView(model, this, chatMessageContainerController.getView().getRoot(), channelSidebar.getRoot()); } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java index 942bfe9d41..6b6457ab87 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java @@ -67,7 +67,7 @@ import static bisq.chat.ChatMessageType.LEAVE; import static bisq.chat.ChatMessageType.PROTOCOL_LOG_MESSAGE; -import static bisq.desktop.main.content.components.chat_messages.ChatMessagesComponent.View.EDITED_POST_FIX; +import static bisq.desktop.main.content.components.chat_messages.ChatMessageContainerView.EDITED_POST_FIX; @Slf4j @Getter diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/priv/PrivateChatsController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/priv/PrivateChatsController.java index dadda6d4f7..f25188b4e1 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/priv/PrivateChatsController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/priv/PrivateChatsController.java @@ -73,7 +73,7 @@ public void onActivate() { channelsPin = channelService.getChannels().addObserver(this::channelsChanged); openPrivateChatsPin = EasyBind.subscribe(model.getNoOpenChats(), - noOpenChats -> chatMessagesComponent.enableChatDialog(!noOpenChats)); + noOpenChats -> chatMessageContainerController.enableChatDialog(!noOpenChats)); chatNotificationService.getNotConsumedNotifications().forEach(this::handleNotification); changedChatNotificationPin = chatNotificationService.getChangedNotification().addObserver(this::handleNotification); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerController.java new file mode 100644 index 0000000000..e799d04b6f --- /dev/null +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerController.java @@ -0,0 +1,313 @@ +package bisq.desktop.main.content.components.chat_messages; + +import bisq.bisq_easy.NavigationTarget; +import bisq.chat.*; +import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannel; +import bisq.chat.bisqeasy.open_trades.BisqEasyOpenTradeChannel; +import bisq.chat.common.CommonPublicChatChannel; +import bisq.chat.pub.PublicChatChannel; +import bisq.chat.two_party.TwoPartyPrivateChatChannel; +import bisq.common.observable.Pin; +import bisq.desktop.ServiceProvider; +import bisq.desktop.common.threading.UIThread; +import bisq.desktop.common.view.Navigation; +import bisq.desktop.components.overlay.Popup; +import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; +import bisq.desktop.main.content.components.CitationBlock; +import bisq.desktop.main.content.components.UserProfileSelection; +import bisq.i18n.Res; +import bisq.settings.SettingsService; +import bisq.user.identity.UserIdentity; +import bisq.user.identity.UserIdentityService; +import bisq.user.profile.UserProfile; +import bisq.user.profile.UserProfileService; +import lombok.Getter; + +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class ChatMessageContainerController implements bisq.desktop.common.view.Controller { + private final ChatMessageContainerModel model; + @Getter + private final ChatMessageContainerView view; + private final Consumer openUserProfileSidebarHandler; + private final UserIdentityService userIdentityService; + private final CitationBlock citationBlock; + private final ChatMessagesListController chatMessagesListController; + private final UserProfileService userProfileService; + private final SettingsService settingsService; + private final ChatService chatService; + private Pin selectedChannelPin, chatMessagesPin, getUserIdentitiesPin; + + public ChatMessageContainerController(ServiceProvider serviceProvider, + ChatChannelDomain chatChannelDomain, + Consumer openUserProfileSidebarHandler) { + this.openUserProfileSidebarHandler = openUserProfileSidebarHandler; + + chatService = serviceProvider.getChatService(); + settingsService = serviceProvider.getSettingsService(); + userIdentityService = serviceProvider.getUserService().getUserIdentityService(); + userProfileService = serviceProvider.getUserService().getUserProfileService(); + + citationBlock = new CitationBlock(serviceProvider); + + UserProfileSelection userProfileSelection = new UserProfileSelection(serviceProvider); + + chatMessagesListController = new ChatMessagesListController(serviceProvider, + this::mentionUserHandler, + this::showChatUserDetailsHandler, + this::replyHandler, + chatChannelDomain); + + model = new ChatMessageContainerModel(chatChannelDomain, chatService); + view = new ChatMessageContainerView(model, this, + chatMessagesListController.getView().getRoot(), + citationBlock.getRoot(), + userProfileSelection); + } + + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////////////// + + public void resetSelectedChatMessage() { + model.setSelectedChatMessage(null); + } + + public void refreshMessages() { + chatMessagesListController.refreshMessages(); + } + + public void mentionUser(UserProfile userProfile) { + mentionUserHandler(userProfile); + } + + public void createAndSelectTwoPartyPrivateChatChannel(UserProfile peer) { + chatService.createAndSelectTwoPartyPrivateChatChannel(model.getChatChannelDomain(), peer) + .ifPresent(channel -> { + if (model.getChatChannelDomain() == ChatChannelDomain.BISQ_EASY_OFFERBOOK) { + Navigation.navigateTo(NavigationTarget.BISQ_EASY_PRIVATE_CHAT); + } + }); + } + + public void enableChatDialog(boolean isEnabled) { + model.getChatDialogEnabled().set(isEnabled); + } + + public void setSearchPredicate(Predicate>> predicate) { + chatMessagesListController.setSearchPredicate(predicate); + } + + public void setBisqEasyOfferDirectionOrOwnerFilterPredicate(Predicate>> predicate) { + chatMessagesListController.setBisqEasyOfferDirectionOrOwnerFilterPredicate(predicate); + } + + public void setBisqEasyPeerReputationFilterPredicate(Predicate>> predicate) { + chatMessagesListController.setBisqEasyPeerReputationFilterPredicate(predicate); + } + + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // Controller + /////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void onActivate() { + model.getMentionableUsers().setAll(userProfileService.getUserProfiles()); + Optional.ofNullable(model.getSelectedChatMessage()).ifPresent(this::showChatUserDetailsHandler); + + getUserIdentitiesPin = userIdentityService.getUserIdentities().addObserver(() -> UIThread.run(this::applyUserProfileOrChannelChange)); + + if (selectedChannelPin != null) { + selectedChannelPin.unbind(); + } + ChatChannelSelectionService selectionService = chatService.getChatChannelSelectionServices().get(model.getChatChannelDomain()); + selectedChannelPin = selectionService.getSelectedChannel().addObserver(this::selectedChannelChanged); + } + + @Override + public void onDeactivate() { + if (selectedChannelPin != null) { + selectedChannelPin.unbind(); + selectedChannelPin = null; + } + getUserIdentitiesPin.unbind(); + if (chatMessagesPin != null) { + chatMessagesPin.unbind(); + } + + model.getSelectedChannel().set(null); + } + + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // Handlers passed to list view component + /////////////////////////////////////////////////////////////////////////////////////////////////// + + private void replyHandler(ChatMessage chatMessage) { + if (!chatMessage.isMyMessage(userIdentityService)) { + citationBlock.reply(chatMessage); + // To ensure that we trigger an update we set it to null first (and don't use a + // BooleanProperty but ObjectProperty + model.getFocusInputTextField().set(null); + model.getFocusInputTextField().set(true); + } + } + + private void showChatUserDetailsHandler(ChatMessage chatMessage) { + model.setSelectedChatMessage(chatMessage); + userProfileService.findUserProfile(chatMessage.getAuthorUserProfileId()) + .ifPresent(openUserProfileSidebarHandler); + } + + private void mentionUserHandler(UserProfile userProfile) { + String existingText = model.getTextInput().get(); + if (!existingText.isEmpty() && !existingText.endsWith(" ")) { + existingText += " "; + } + model.getTextInput().set(existingText + "@" + userProfile.getUserName() + " "); + } + + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // UI handlers + /////////////////////////////////////////////////////////////////////////////////////////////////// + + void onSendMessage(String text) { + if (text == null || text.isEmpty()) { + return; + } + + if (model.getSelectedChannel().get() instanceof PublicChatChannel) { + List myUserProfilesInChannel = getMyUserProfilesInChannel(); + if (!myUserProfilesInChannel.isEmpty()) { + UserIdentity lastUsedUserProfile = myUserProfilesInChannel.get(0); + if (!lastUsedUserProfile.equals(userIdentityService.getSelectedUserIdentity())) { + new Popup().warning(Res.get("chat.message.send.differentUserProfile.warn")) + .closeButtonText(Res.get("confirmation.no")) + .actionButtonText(Res.get("confirmation.yes")) + .onAction(() -> doSendMessage(text)) + .show(); + return; + } + } + } + + doSendMessage(text); + } + + void onListUserNames(UserProfile user) { + String content = model.getTextInput().get().replaceAll("@[a-zA-Z\\d]*$", "@" + user.getUserName() + " "); + model.getTextInput().set(content); + view.getInputField().positionCaret(content.length()); //todo dont call on view + } + + void onListChannels(ChatChannel chatChannel) { + String channelTitle = chatService.findChatChannelService(chatChannel) + .map(service -> service.getChannelTitle(chatChannel)) + .orElse(""); + String content = model.getTextInput().get().replaceAll("#[a-zA-Z\\d]*$", "#" + channelTitle + " "); + model.getTextInput().set(content); + view.getInputField().positionCaret(content.length()); //todo dont call on view + } + + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////////////// + + private void applyUserProfileOrChannelChange() { + boolean multipleProfiles = userIdentityService.getUserIdentities().size() > 1; + ChatChannel selectedChatChannel = model.getSelectedChannel().get(); + model.getUserProfileSelectionVisible().set(multipleProfiles && selectedChatChannel instanceof PublicChatChannel); + + if (chatMessagesPin != null) { + chatMessagesPin.unbind(); + } + + if (selectedChatChannel != null) { + chatMessagesPin = selectedChatChannel.getChatMessages().addObserver(this::maybeSwitchUserProfile); + } + } + + + protected void selectedChannelChanged(ChatChannel chatChannel) { + UIThread.run(() -> { + model.getSelectedChannel().set(chatChannel); + applyUserProfileOrChannelChange(); + }); + } + + private void doSendMessage(String text) { + if (text.length() > ChatMessage.MAX_TEXT_LENGTH) { + new Popup().warning(Res.get("validation.tooLong", ChatMessage.MAX_TEXT_LENGTH)).show(); + return; + } + + ChatChannel chatChannel = model.getSelectedChannel().get(); + UserIdentity userIdentity = checkNotNull(userIdentityService.getSelectedUserIdentity(), "user identity must not be null"); + Optional citation = citationBlock.getCitation(); + + if (citation.isPresent() && citation.get().getText().length() > Citation.MAX_TEXT_LENGTH) { + new Popup().warning(Res.get("validation.tooLong", Citation.MAX_TEXT_LENGTH)).show(); + return; + } + + if (chatChannel instanceof BisqEasyOfferbookChannel) { + String dontShowAgainId = "sendMsgOfferOnlyWarn"; + if (settingsService.getOffersOnly().get()) { + new Popup().information(Res.get("chat.message.send.offerOnly.warn")) + .actionButtonText(Res.get("confirmation.yes")) + .onAction(() -> settingsService.getOffersOnly().set(false)) + .closeButtonText(Res.get("confirmation.no")) + .dontShowAgainId(dontShowAgainId) + .show(); + } + chatService.getBisqEasyOfferbookChannelService().publishChatMessage(text, citation, (BisqEasyOfferbookChannel) chatChannel, userIdentity); + } else if (chatChannel instanceof BisqEasyOpenTradeChannel) { + if (settingsService.getTradeRulesConfirmed().get() || ((BisqEasyOpenTradeChannel) chatChannel).isMediator()) { + chatService.getBisqEasyOpenTradeChannelService().sendTextMessage(text, citation, (BisqEasyOpenTradeChannel) chatChannel); + } else { + new Popup().information(Res.get("bisqEasy.tradeGuide.notConfirmed.warn")) + .actionButtonText(Res.get("bisqEasy.tradeGuide.open")) + .onAction(() -> Navigation.navigateTo(NavigationTarget.BISQ_EASY_GUIDE)) + .show(); + } + } else if (chatChannel instanceof CommonPublicChatChannel) { + chatService.getCommonPublicChatChannelServices().get(model.getChatChannelDomain()).publishChatMessage(text, citation, (CommonPublicChatChannel) chatChannel, userIdentity); + } else if (chatChannel instanceof TwoPartyPrivateChatChannel) { + chatService.getTwoPartyPrivateChatChannelServices().get(model.getChatChannelDomain()).sendTextMessage(text, citation, (TwoPartyPrivateChatChannel) chatChannel); + } + + citationBlock.close(); + } + + private void maybeSwitchUserProfile() { + if (model.getUserProfileSelectionVisible().get()) { + List myUserProfilesInChannel = getMyUserProfilesInChannel(); + if (!myUserProfilesInChannel.isEmpty()) { + userIdentityService.selectChatUserIdentity(myUserProfilesInChannel.get(0)); + } + } + } + + private List getMyUserProfilesInChannel() { + return model.getSelectedChannel().get().getChatMessages().stream() + .sorted(Comparator.comparing(ChatMessage::getDate).reversed()) + .map(ChatMessage::getAuthorUserProfileId) + .map(userIdentityService::findUserIdentity) + .filter(Optional::isPresent) + .map(Optional::get) + .distinct() + .collect(Collectors.toList()); + } +} diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerModel.java new file mode 100644 index 0000000000..6de5ff832c --- /dev/null +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerModel.java @@ -0,0 +1,45 @@ +package bisq.desktop.main.content.components.chat_messages; + +import bisq.chat.ChatChannel; +import bisq.chat.ChatChannelDomain; +import bisq.chat.ChatMessage; +import bisq.chat.ChatService; +import bisq.user.profile.UserProfile; +import javafx.beans.property.*; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import lombok.Getter; + +import javax.annotation.Nullable; + +@Getter +public class ChatMessageContainerModel implements bisq.desktop.common.view.Model { + private final BooleanProperty chatDialogEnabled = new SimpleBooleanProperty(true); + + private final ObjectProperty> selectedChannel = new SimpleObjectProperty<>(); + private final StringProperty textInput = new SimpleStringProperty(""); + private final BooleanProperty userProfileSelectionVisible = new SimpleBooleanProperty(); + private final ObjectProperty focusInputTextField = new SimpleObjectProperty<>(); + private final ObservableList mentionableUsers = FXCollections.observableArrayList(); + // TODO mentionableChatChannels not filled with data + private final ObservableList> mentionableChatChannels = FXCollections.observableArrayList(); + private final ChatChannelDomain chatChannelDomain; + private final ChatService chatService; + @Nullable + private ChatMessage selectedChatMessage; + + public ChatMessageContainerModel(ChatChannelDomain chatChannelDomain, ChatService chatService) { + this.chatChannelDomain = chatChannelDomain; + this.chatService = chatService; + } + + String getChannelTitle(ChatChannel chatChannel) { + return chatService.findChatChannelService(chatChannel) + .map(service -> service.getChannelTitle(chatChannel)) + .orElse(""); + } + + public void setSelectedChatMessage(@Nullable ChatMessage selectedChatMessage) { + this.selectedChatMessage = selectedChatMessage; + } +} diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerView.java new file mode 100644 index 0000000000..ca21863965 --- /dev/null +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerView.java @@ -0,0 +1,208 @@ +package bisq.desktop.main.content.components.chat_messages; + +import bisq.chat.ChatChannel; +import bisq.common.util.StringUtils; +import bisq.desktop.common.utils.ImageUtil; +import bisq.desktop.components.controls.BisqTextArea; +import bisq.desktop.components.controls.BisqTooltip; +import bisq.desktop.main.content.chat.ChatUtil; +import bisq.desktop.main.content.components.ChatMentionPopupMenu; +import bisq.desktop.main.content.components.UserProfileSelection; +import bisq.i18n.Res; +import bisq.user.profile.UserProfile; +import javafx.beans.binding.Bindings; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.input.KeyCode; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import javafx.util.StringConverter; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.fxmisc.easybind.EasyBind; +import org.fxmisc.easybind.Subscription; + +@Slf4j +public class ChatMessageContainerView extends bisq.desktop.common.view.View { + private final static double CHAT_BOX_MAX_WIDTH = 1200; + public final static String EDITED_POST_FIX = " " + Res.get("chat.message.wasEdited"); + @Getter + private final BisqTextArea inputField = new BisqTextArea(); //todo remove accessor + private final Button sendButton = new Button(); + private final Pane messagesListView; + private final VBox emptyMessageList; + private ChatMentionPopupMenu userMentionPopup; + private ChatMentionPopupMenu> channelMentionPopup; + private Pane userProfileSelectionRoot; + private Subscription focusInputTextFieldPin; + + public ChatMessageContainerView(ChatMessageContainerModel model, + ChatMessageContainerController controller, + Pane messagesListView, + Pane quotedMessageBlock, + UserProfileSelection userProfileSelection) { + super(new VBox(), model, controller); + + this.messagesListView = messagesListView; + VBox.setVgrow(messagesListView, Priority.ALWAYS); + + emptyMessageList = ChatUtil.createEmptyChatPlaceholder( + new Label(Res.get("chat.private.messagebox.noChats.placeholder.title")), + new Label(Res.get("chat.private.messagebox.noChats.placeholder.description"))); + + VBox bottomBarContainer = createAndGetBottomBar(userProfileSelection); + + quotedMessageBlock.setMaxWidth(CHAT_BOX_MAX_WIDTH); + + root.setAlignment(Pos.CENTER); + root.getChildren().addAll(messagesListView, emptyMessageList, quotedMessageBlock, bottomBarContainer); + } + + @Override + protected void onViewAttached() { + userProfileSelectionRoot.visibleProperty().bind(model.getUserProfileSelectionVisible()); + userProfileSelectionRoot.managedProperty().bind(model.getUserProfileSelectionVisible()); + inputField.textProperty().bindBidirectional(model.getTextInput()); + userMentionPopup.filterProperty().bind(Bindings.createStringBinding( + () -> StringUtils.deriveWordStartingWith(inputField.getText(), '@'), + inputField.textProperty() + )); + channelMentionPopup.filterProperty().bind(Bindings.createStringBinding( + () -> StringUtils.deriveWordStartingWith(inputField.getText(), '#'), + inputField.textProperty() + )); + + inputField.setOnKeyPressed(event -> { + if (event.getCode() == KeyCode.ENTER) { + event.consume(); + if (event.isShiftDown()) { + inputField.appendText(System.getProperty("line.separator")); + } else if (!inputField.getText().isEmpty()) { + controller.onSendMessage(inputField.getText().trim()); + inputField.clear(); + } + } + }); + sendButton.setOnAction(event -> { + controller.onSendMessage(inputField.getText().trim()); + inputField.clear(); + }); + + userMentionPopup.setItems(model.getMentionableUsers()); + channelMentionPopup.setItems(model.getMentionableChatChannels()); + + createChatDialogEnabledSubscription(); + + focusInputTextFieldPin = EasyBind.subscribe(model.getFocusInputTextField(), focusInputTextField -> { + if (focusInputTextField != null && focusInputTextField) { + inputField.requestFocus(); + } + }); + } + + @Override + protected void onViewDetached() { + userProfileSelectionRoot.visibleProperty().unbind(); + userProfileSelectionRoot.managedProperty().unbind(); + inputField.textProperty().unbindBidirectional(model.getTextInput()); + userMentionPopup.filterProperty().unbind(); + channelMentionPopup.filterProperty().unbind(); + focusInputTextFieldPin.unsubscribe(); + removeChatDialogEnabledSubscription(); + + inputField.setOnKeyPressed(null); + sendButton.setOnAction(null); + } + + private VBox createAndGetBottomBar(UserProfileSelection userProfileSelection) { + setUpUserProfileSelection(userProfileSelection); + HBox sendMessageBox = createAndGetSendMessageBox(); + + HBox bottomBar = new HBox(10); + bottomBar.getChildren().addAll(userProfileSelectionRoot, sendMessageBox); + bottomBar.setMaxWidth(CHAT_BOX_MAX_WIDTH); + bottomBar.setPadding(new Insets(14, 20, 14, 20)); + bottomBar.setAlignment(Pos.BOTTOM_CENTER); + + VBox bottomBarContainer = new VBox(bottomBar); + bottomBarContainer.setAlignment(Pos.CENTER); + return bottomBarContainer; + } + + private HBox createAndGetSendMessageBox() { + inputField.setPromptText(Res.get("chat.message.input.prompt")); + inputField.getStyleClass().addAll("chat-input-field", "medium-text"); + inputField.setPadding(new Insets(5, 0, 5, 5)); + HBox.setMargin(inputField, new Insets(0, 0, 1.5, 0)); + HBox.setHgrow(inputField, Priority.ALWAYS); + setUpInputFieldAtMentions(); + + sendButton.setGraphic(ImageUtil.getImageViewById("chat-send")); + sendButton.setId("chat-messages-send-button"); + HBox.setMargin(sendButton, new Insets(0, 0, 5, 0)); + sendButton.setMinWidth(30); + sendButton.setMaxWidth(30); + sendButton.setTooltip(new BisqTooltip(Res.get("chat.message.input.send"), true)); + + HBox sendMessageBox = new HBox(inputField, sendButton); + sendMessageBox.getStyleClass().add("chat-send-message-box"); + sendMessageBox.setAlignment(Pos.BOTTOM_CENTER); + HBox.setHgrow(sendMessageBox, Priority.ALWAYS); + return sendMessageBox; + } + + private void setUpInputFieldAtMentions() { + userMentionPopup = new ChatMentionPopupMenu<>(inputField); + userMentionPopup.setItemDisplayConverter(UserProfile::getUserName); + userMentionPopup.setSelectionHandler(controller::onListUserNames); + + channelMentionPopup = new ChatMentionPopupMenu<>(inputField); + channelMentionPopup.setItemDisplayConverter(model::getChannelTitle); + channelMentionPopup.setSelectionHandler(controller::onListChannels); + } + + private void setUpUserProfileSelection(UserProfileSelection userProfileSelection) { + userProfileSelection.setMaxComboBoxWidth(165); + userProfileSelection.setConverter(new StringConverter<>() { + @Override + public String toString(UserProfileSelection.ListItem item) { + return item != null ? StringUtils.truncate(item.getUserIdentity().getUserName(), 10) : ""; + } + + @Override + public UserProfileSelection.ListItem fromString(String string) { + return null; + } + }); + userProfileSelectionRoot = userProfileSelection.getRoot(); + userProfileSelectionRoot.setMaxHeight(44); + userProfileSelectionRoot.setMaxWidth(165); + userProfileSelectionRoot.setMinWidth(165); + userProfileSelectionRoot.setId("chat-user-profile-bg"); + HBox.setMargin(userProfileSelectionRoot, new Insets(0, -20, 0, -8)); + } + + private void createChatDialogEnabledSubscription() { + inputField.disableProperty().bind(model.getChatDialogEnabled().not()); + sendButton.disableProperty().bind(model.getChatDialogEnabled().not()); + emptyMessageList.visibleProperty().bind(model.getChatDialogEnabled().not()); + emptyMessageList.managedProperty().bind(model.getChatDialogEnabled().not()); + messagesListView.visibleProperty().bind(model.getChatDialogEnabled()); + messagesListView.managedProperty().bind(model.getChatDialogEnabled()); + userProfileSelectionRoot.disableProperty().bind(model.getChatDialogEnabled().not()); + } + + private void removeChatDialogEnabledSubscription() { + inputField.disableProperty().unbind(); + sendButton.disableProperty().unbind(); + emptyMessageList.visibleProperty().unbind(); + emptyMessageList.managedProperty().unbind(); + messagesListView.visibleProperty().unbind(); + messagesListView.managedProperty().unbind(); + userProfileSelectionRoot.disableProperty().unbind(); + } +} diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java deleted file mode 100644 index 07fb8e35a9..0000000000 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessagesComponent.java +++ /dev/null @@ -1,588 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.content.components.chat_messages; - -import bisq.bisq_easy.NavigationTarget; -import bisq.chat.*; -import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannel; -import bisq.chat.bisqeasy.open_trades.BisqEasyOpenTradeChannel; -import bisq.chat.common.CommonPublicChatChannel; -import bisq.chat.pub.PublicChatChannel; -import bisq.chat.two_party.TwoPartyPrivateChatChannel; -import bisq.common.observable.Pin; -import bisq.common.util.StringUtils; -import bisq.desktop.ServiceProvider; -import bisq.desktop.common.threading.UIThread; -import bisq.desktop.common.utils.ImageUtil; -import bisq.desktop.common.view.Navigation; -import bisq.desktop.components.controls.BisqTextArea; -import bisq.desktop.components.controls.BisqTooltip; -import bisq.desktop.components.overlay.Popup; -import bisq.desktop.main.content.chat.ChatUtil; -import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; -import bisq.desktop.main.content.components.ChatMentionPopupMenu; -import bisq.desktop.main.content.components.CitationBlock; -import bisq.desktop.main.content.components.UserProfileSelection; -import bisq.i18n.Res; -import bisq.settings.SettingsService; -import bisq.user.identity.UserIdentity; -import bisq.user.identity.UserIdentityService; -import bisq.user.profile.UserProfile; -import bisq.user.profile.UserProfileService; -import javafx.beans.binding.Bindings; -import javafx.beans.property.*; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.geometry.Insets; -import javafx.geometry.Pos; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.input.KeyCode; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; -import javafx.scene.layout.Priority; -import javafx.scene.layout.VBox; -import javafx.util.StringConverter; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.fxmisc.easybind.EasyBind; -import org.fxmisc.easybind.Subscription; - -import javax.annotation.Nullable; -import java.util.Comparator; -import java.util.List; -import java.util.Optional; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import static com.google.common.base.Preconditions.checkNotNull; - -@Slf4j -public class ChatMessagesComponent { - private final Controller controller; - - public ChatMessagesComponent(ServiceProvider serviceProvider, - ChatChannelDomain chatChannelDomain, - Consumer openUserProfileSidebarHandler) { - controller = new Controller(serviceProvider, - chatChannelDomain, - openUserProfileSidebarHandler); - } - - public VBox getRoot() { - return controller.view.getRoot(); - } - - public void mentionUser(UserProfile userProfile) { - controller.mentionUserHandler(userProfile); - } - - public void setSearchPredicate(Predicate>> predicate) { - controller.chatMessagesListController.setSearchPredicate(predicate); - } - - public void setBisqEasyOfferDirectionOrOwnerFilterPredicate(Predicate>> predicate) { - controller.chatMessagesListController.setBisqEasyOfferDirectionOrOwnerFilterPredicate(predicate); - } - - public void setBisqEasyPeerReputationFilterPredicate(Predicate>> predicate) { - controller.chatMessagesListController.setBisqEasyPeerReputationFilterPredicate(predicate); - } - - public void resetSelectedChatMessage() { - controller.model.selectedChatMessage = null; - } - - public void createAndSelectTwoPartyPrivateChatChannel(UserProfile peer) { - controller.createAndSelectTwoPartyPrivateChatChannel(peer); - } - - public void refreshMessages() { - controller.chatMessagesListController.refreshMessages(); - } - - public void enableChatDialog(boolean isEnabled) { - controller.enableChatDialog(isEnabled); - } - - private static class Controller implements bisq.desktop.common.view.Controller { - private final Model model; - @Getter - private final View view; - private final Consumer openUserProfileSidebarHandler; - private final UserIdentityService userIdentityService; - private final CitationBlock citationBlock; - private final ChatMessagesListController chatMessagesListController; - private final UserProfileService userProfileService; - private final SettingsService settingsService; - private final ChatService chatService; - - private Pin selectedChannelPin, chatMessagesPin, getUserIdentitiesPin; - - private Controller(ServiceProvider serviceProvider, - ChatChannelDomain chatChannelDomain, - Consumer openUserProfileSidebarHandler) { - this.openUserProfileSidebarHandler = openUserProfileSidebarHandler; - - chatService = serviceProvider.getChatService(); - settingsService = serviceProvider.getSettingsService(); - userIdentityService = serviceProvider.getUserService().getUserIdentityService(); - userProfileService = serviceProvider.getUserService().getUserProfileService(); - - citationBlock = new CitationBlock(serviceProvider); - - UserProfileSelection userProfileSelection = new UserProfileSelection(serviceProvider); - - chatMessagesListController = new ChatMessagesListController(serviceProvider, - this::mentionUserHandler, - this::showChatUserDetailsHandler, - this::replyHandler, - chatChannelDomain); - - model = new Model(chatChannelDomain, chatService); - view = new View(model, this, - chatMessagesListController.getView().getRoot(), - citationBlock.getRoot(), - userProfileSelection); - } - - @Override - public void onActivate() { - model.mentionableUsers.setAll(userProfileService.getUserProfiles()); - Optional.ofNullable(model.selectedChatMessage).ifPresent(this::showChatUserDetailsHandler); - - getUserIdentitiesPin = userIdentityService.getUserIdentities().addObserver(() -> UIThread.run(this::applyUserProfileOrChannelChange)); - - if (selectedChannelPin != null) { - selectedChannelPin.unbind(); - } - ChatChannelSelectionService selectionService = chatService.getChatChannelSelectionServices().get(model.getChatChannelDomain()); - selectedChannelPin = selectionService.getSelectedChannel().addObserver(this::selectedChannelChanged); - } - - @Override - public void onDeactivate() { - if (selectedChannelPin != null) { - selectedChannelPin.unbind(); - selectedChannelPin = null; - } - getUserIdentitiesPin.unbind(); - if (chatMessagesPin != null) { - chatMessagesPin.unbind(); - } - - model.selectedChannel.set(null); - } - - protected void selectedChannelChanged(ChatChannel chatChannel) { - UIThread.run(() -> { - model.selectedChannel.set(chatChannel); - applyUserProfileOrChannelChange(); - }); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - // From method calls on component - /////////////////////////////////////////////////////////////////////////////////////////////////// - - private void createAndSelectTwoPartyPrivateChatChannel(UserProfile peer) { - chatService.createAndSelectTwoPartyPrivateChatChannel(model.getChatChannelDomain(), peer) - .ifPresent(channel -> { - if (model.getChatChannelDomain() == ChatChannelDomain.BISQ_EASY_OFFERBOOK) { - Navigation.navigateTo(NavigationTarget.BISQ_EASY_PRIVATE_CHAT); - } - }); - } - - - /////////////////////////////////////////////////////////////////////////////////////////////////// - // Handlers passed to list view component - /////////////////////////////////////////////////////////////////////////////////////////////////// - - private void replyHandler(ChatMessage chatMessage) { - if (!chatMessage.isMyMessage(userIdentityService)) { - citationBlock.reply(chatMessage); - // To ensure that we trigger an update we set it to null first (and don't use a - // BooleanProperty but ObjectProperty - model.getFocusInputTextField().set(null); - model.getFocusInputTextField().set(true); - } - } - - private void showChatUserDetailsHandler(ChatMessage chatMessage) { - model.selectedChatMessage = chatMessage; - userProfileService.findUserProfile(chatMessage.getAuthorUserProfileId()) - .ifPresent(openUserProfileSidebarHandler); - } - - private void mentionUserHandler(UserProfile userProfile) { - String existingText = model.getTextInput().get(); - if (!existingText.isEmpty() && !existingText.endsWith(" ")) { - existingText += " "; - } - model.getTextInput().set(existingText + "@" + userProfile.getUserName() + " "); - } - - - private void listUserNamesHandler(UserProfile user) { - String content = model.getTextInput().get().replaceAll("@[a-zA-Z\\d]*$", "@" + user.getUserName() + " "); - model.getTextInput().set(content); - view.inputField.positionCaret(content.length()); - } - - private void listChannelsHandler(ChatChannel chatChannel) { - String channelTitle = chatService.findChatChannelService(chatChannel) - .map(service -> service.getChannelTitle(chatChannel)) - .orElse(""); - String content = model.getTextInput().get().replaceAll("#[a-zA-Z\\d]*$", "#" + channelTitle + " "); - model.getTextInput().set(content); - view.inputField.positionCaret(content.length()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////////////// - // Change handlers from service or model - /////////////////////////////////////////////////////////////////////////////////////////////////// - - private void applyUserProfileOrChannelChange() { - boolean multipleProfiles = userIdentityService.getUserIdentities().size() > 1; - ChatChannel selectedChatChannel = model.selectedChannel.get(); - model.userProfileSelectionVisible.set(multipleProfiles && selectedChatChannel instanceof PublicChatChannel); - - if (chatMessagesPin != null) { - chatMessagesPin.unbind(); - } - - if (selectedChatChannel != null) { - chatMessagesPin = selectedChatChannel.getChatMessages().addObserver(this::maybeSwitchUserProfile); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////////////// - // UI handlers - /////////////////////////////////////////////////////////////////////////////////////////////////// - - private void onSendMessage(String text) { - if (text == null || text.isEmpty()) { - return; - } - - if (model.selectedChannel.get() instanceof PublicChatChannel) { - List myUserProfilesInChannel = getMyUserProfilesInChannel(); - if (!myUserProfilesInChannel.isEmpty()) { - UserIdentity lastUsedUserProfile = myUserProfilesInChannel.get(0); - if (!lastUsedUserProfile.equals(userIdentityService.getSelectedUserIdentity())) { - new Popup().warning(Res.get("chat.message.send.differentUserProfile.warn")) - .closeButtonText(Res.get("confirmation.no")) - .actionButtonText(Res.get("confirmation.yes")) - .onAction(() -> doSendMessage(text)) - .show(); - return; - } - } - } - - doSendMessage(text); - } - - - /////////////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////////////// - - private void doSendMessage(String text) { - if (text.length() > ChatMessage.MAX_TEXT_LENGTH) { - new Popup().warning(Res.get("validation.tooLong", ChatMessage.MAX_TEXT_LENGTH)).show(); - return; - } - - ChatChannel chatChannel = model.selectedChannel.get(); - UserIdentity userIdentity = checkNotNull(userIdentityService.getSelectedUserIdentity(), "user identity must not be null"); - Optional citation = citationBlock.getCitation(); - - if (citation.isPresent() && citation.get().getText().length() > Citation.MAX_TEXT_LENGTH) { - new Popup().warning(Res.get("validation.tooLong", Citation.MAX_TEXT_LENGTH)).show(); - return; - } - - if (chatChannel instanceof BisqEasyOfferbookChannel) { - String dontShowAgainId = "sendMsgOfferOnlyWarn"; - if (settingsService.getOffersOnly().get()) { - new Popup().information(Res.get("chat.message.send.offerOnly.warn")) - .actionButtonText(Res.get("confirmation.yes")) - .onAction(() -> settingsService.getOffersOnly().set(false)) - .closeButtonText(Res.get("confirmation.no")) - .dontShowAgainId(dontShowAgainId) - .show(); - } - chatService.getBisqEasyOfferbookChannelService().publishChatMessage(text, citation, (BisqEasyOfferbookChannel) chatChannel, userIdentity); - } else if (chatChannel instanceof BisqEasyOpenTradeChannel) { - if (settingsService.getTradeRulesConfirmed().get() || ((BisqEasyOpenTradeChannel) chatChannel).isMediator()) { - chatService.getBisqEasyOpenTradeChannelService().sendTextMessage(text, citation, (BisqEasyOpenTradeChannel) chatChannel); - } else { - new Popup().information(Res.get("bisqEasy.tradeGuide.notConfirmed.warn")) - .actionButtonText(Res.get("bisqEasy.tradeGuide.open")) - .onAction(() -> Navigation.navigateTo(NavigationTarget.BISQ_EASY_GUIDE)) - .show(); - } - } else if (chatChannel instanceof CommonPublicChatChannel) { - chatService.getCommonPublicChatChannelServices().get(model.chatChannelDomain).publishChatMessage(text, citation, (CommonPublicChatChannel) chatChannel, userIdentity); - } else if (chatChannel instanceof TwoPartyPrivateChatChannel) { - chatService.getTwoPartyPrivateChatChannelServices().get(model.chatChannelDomain).sendTextMessage(text, citation, (TwoPartyPrivateChatChannel) chatChannel); - } - - citationBlock.close(); - } - - private void maybeSwitchUserProfile() { - if (model.userProfileSelectionVisible.get()) { - List myUserProfilesInChannel = getMyUserProfilesInChannel(); - if (!myUserProfilesInChannel.isEmpty()) { - userIdentityService.selectChatUserIdentity(myUserProfilesInChannel.get(0)); - } - } - } - - private List getMyUserProfilesInChannel() { - return model.selectedChannel.get().getChatMessages().stream() - .sorted(Comparator.comparing(ChatMessage::getDate).reversed()) - .map(ChatMessage::getAuthorUserProfileId) - .map(userIdentityService::findUserIdentity) - .filter(Optional::isPresent) - .map(Optional::get) - .distinct() - .collect(Collectors.toList()); - } - - private void enableChatDialog(boolean isEnabled) { - model.getChatDialogEnabled().set(isEnabled); - } - } - - @Getter - private static class Model implements bisq.desktop.common.view.Model { - private final BooleanProperty chatDialogEnabled = new SimpleBooleanProperty(true); - - private final ObjectProperty> selectedChannel = new SimpleObjectProperty<>(); - private final StringProperty textInput = new SimpleStringProperty(""); - private final BooleanProperty userProfileSelectionVisible = new SimpleBooleanProperty(); - private final ObjectProperty focusInputTextField = new SimpleObjectProperty<>(); - private final ObservableList mentionableUsers = FXCollections.observableArrayList(); - // TODO mentionableChatChannels not filled with data - private final ObservableList> mentionableChatChannels = FXCollections.observableArrayList(); - private final ChatChannelDomain chatChannelDomain; - private final ChatService chatService; - @Nullable - private ChatMessage selectedChatMessage; - - private Model(ChatChannelDomain chatChannelDomain, ChatService chatService) { - this.chatChannelDomain = chatChannelDomain; - this.chatService = chatService; - } - - String getChannelTitle(ChatChannel chatChannel) { - return chatService.findChatChannelService(chatChannel) - .map(service -> service.getChannelTitle(chatChannel)) - .orElse(""); - } - } - - @Slf4j - public static class View extends bisq.desktop.common.view.View { - private final static double CHAT_BOX_MAX_WIDTH = 1200; - public final static String EDITED_POST_FIX = " " + Res.get("chat.message.wasEdited"); - - private final BisqTextArea inputField = new BisqTextArea(); - private final Button sendButton = new Button(); - private final Pane messagesListView; - private final VBox emptyMessageList; - private ChatMentionPopupMenu userMentionPopup; - private ChatMentionPopupMenu> channelMentionPopup; - private Pane userProfileSelectionRoot; - private Subscription focusInputTextFieldPin; - - private View(Model model, - Controller controller, - Pane messagesListView, - Pane quotedMessageBlock, - UserProfileSelection userProfileSelection) { - super(new VBox(), model, controller); - - this.messagesListView = messagesListView; - VBox.setVgrow(messagesListView, Priority.ALWAYS); - - emptyMessageList = ChatUtil.createEmptyChatPlaceholder( - new Label(Res.get("chat.private.messagebox.noChats.placeholder.title")), - new Label(Res.get("chat.private.messagebox.noChats.placeholder.description"))); - - VBox bottomBarContainer = createAndGetBottomBar(userProfileSelection); - - quotedMessageBlock.setMaxWidth(CHAT_BOX_MAX_WIDTH); - - root.setAlignment(Pos.CENTER); - root.getChildren().addAll(messagesListView, emptyMessageList, quotedMessageBlock, bottomBarContainer); - } - - @Override - protected void onViewAttached() { - userProfileSelectionRoot.visibleProperty().bind(model.userProfileSelectionVisible); - userProfileSelectionRoot.managedProperty().bind(model.userProfileSelectionVisible); - inputField.textProperty().bindBidirectional(model.getTextInput()); - userMentionPopup.filterProperty().bind(Bindings.createStringBinding( - () -> StringUtils.deriveWordStartingWith(inputField.getText(), '@'), - inputField.textProperty() - )); - channelMentionPopup.filterProperty().bind(Bindings.createStringBinding( - () -> StringUtils.deriveWordStartingWith(inputField.getText(), '#'), - inputField.textProperty() - )); - - inputField.setOnKeyPressed(event -> { - if (event.getCode() == KeyCode.ENTER) { - event.consume(); - if (event.isShiftDown()) { - inputField.appendText(System.getProperty("line.separator")); - } else if (!inputField.getText().isEmpty()) { - controller.onSendMessage(inputField.getText().trim()); - inputField.clear(); - } - } - }); - sendButton.setOnAction(event -> { - controller.onSendMessage(inputField.getText().trim()); - inputField.clear(); - }); - - userMentionPopup.setItems(model.mentionableUsers); - channelMentionPopup.setItems(model.mentionableChatChannels); - - createChatDialogEnabledSubscription(); - - focusInputTextFieldPin = EasyBind.subscribe(model.getFocusInputTextField(), focusInputTextField -> { - if (focusInputTextField != null && focusInputTextField) { - inputField.requestFocus(); - } - }); - } - - @Override - protected void onViewDetached() { - userProfileSelectionRoot.visibleProperty().unbind(); - userProfileSelectionRoot.managedProperty().unbind(); - inputField.textProperty().unbindBidirectional(model.getTextInput()); - userMentionPopup.filterProperty().unbind(); - channelMentionPopup.filterProperty().unbind(); - focusInputTextFieldPin.unsubscribe(); - removeChatDialogEnabledSubscription(); - - inputField.setOnKeyPressed(null); - sendButton.setOnAction(null); - } - - private VBox createAndGetBottomBar(UserProfileSelection userProfileSelection) { - setUpUserProfileSelection(userProfileSelection); - HBox sendMessageBox = createAndGetSendMessageBox(); - - HBox bottomBar = new HBox(10); - bottomBar.getChildren().addAll(userProfileSelectionRoot, sendMessageBox); - bottomBar.setMaxWidth(CHAT_BOX_MAX_WIDTH); - bottomBar.setPadding(new Insets(14, 20, 14, 20)); - bottomBar.setAlignment(Pos.BOTTOM_CENTER); - - VBox bottomBarContainer = new VBox(bottomBar); - bottomBarContainer.setAlignment(Pos.CENTER); - return bottomBarContainer; - } - - private HBox createAndGetSendMessageBox() { - inputField.setPromptText(Res.get("chat.message.input.prompt")); - inputField.getStyleClass().addAll("chat-input-field", "medium-text"); - inputField.setPadding(new Insets(5, 0, 5, 5)); - HBox.setMargin(inputField, new Insets(0, 0, 1.5, 0)); - HBox.setHgrow(inputField, Priority.ALWAYS); - setUpInputFieldAtMentions(); - - sendButton.setGraphic(ImageUtil.getImageViewById("chat-send")); - sendButton.setId("chat-messages-send-button"); - HBox.setMargin(sendButton, new Insets(0, 0, 5, 0)); - sendButton.setMinWidth(30); - sendButton.setMaxWidth(30); - sendButton.setTooltip(new BisqTooltip(Res.get("chat.message.input.send"), true)); - - HBox sendMessageBox = new HBox(inputField, sendButton); - sendMessageBox.getStyleClass().add("chat-send-message-box"); - sendMessageBox.setAlignment(Pos.BOTTOM_CENTER); - HBox.setHgrow(sendMessageBox, Priority.ALWAYS); - return sendMessageBox; - } - - private void setUpInputFieldAtMentions() { - userMentionPopup = new ChatMentionPopupMenu<>(inputField); - userMentionPopup.setItemDisplayConverter(UserProfile::getUserName); - userMentionPopup.setSelectionHandler(controller::listUserNamesHandler); - - channelMentionPopup = new ChatMentionPopupMenu<>(inputField); - channelMentionPopup.setItemDisplayConverter(model::getChannelTitle); - channelMentionPopup.setSelectionHandler(controller::listChannelsHandler); - } - - private void setUpUserProfileSelection(UserProfileSelection userProfileSelection) { - userProfileSelection.setMaxComboBoxWidth(165); - userProfileSelection.setConverter(new StringConverter<>() { - @Override - public String toString(UserProfileSelection.ListItem item) { - return item != null ? StringUtils.truncate(item.getUserIdentity().getUserName(), 10) : ""; - } - - @Override - public UserProfileSelection.ListItem fromString(String string) { - return null; - } - }); - userProfileSelectionRoot = userProfileSelection.getRoot(); - userProfileSelectionRoot.setMaxHeight(44); - userProfileSelectionRoot.setMaxWidth(165); - userProfileSelectionRoot.setMinWidth(165); - userProfileSelectionRoot.setId("chat-user-profile-bg"); - HBox.setMargin(userProfileSelectionRoot, new Insets(0, -20, 0, -8)); - } - - private void createChatDialogEnabledSubscription() { - inputField.disableProperty().bind(model.getChatDialogEnabled().not()); - sendButton.disableProperty().bind(model.getChatDialogEnabled().not()); - emptyMessageList.visibleProperty().bind(model.getChatDialogEnabled().not()); - emptyMessageList.managedProperty().bind(model.getChatDialogEnabled().not()); - messagesListView.visibleProperty().bind(model.getChatDialogEnabled()); - messagesListView.managedProperty().bind(model.getChatDialogEnabled()); - userProfileSelectionRoot.disableProperty().bind(model.getChatDialogEnabled().not()); - } - - private void removeChatDialogEnabledSubscription() { - inputField.disableProperty().unbind(); - sendButton.disableProperty().unbind(); - emptyMessageList.visibleProperty().unbind(); - emptyMessageList.managedProperty().unbind(); - messagesListView.visibleProperty().unbind(); - messagesListView.managedProperty().unbind(); - userProfileSelectionRoot.disableProperty().unbind(); - } - } -} From fee7c22f2e753499b6d3ced23c4dc86a29a968af Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 18:06:43 +0700 Subject: [PATCH 099/119] Refactor: Move package --- .../content/authorized_role/mediator/MediatorController.java | 2 +- .../java/bisq/desktop/main/content/chat/BaseChatController.java | 2 +- .../chat_messages/ChatMessageContainerController.java | 2 +- .../chat_messages/ChatMessageContainerModel.java | 2 +- .../chat_messages/ChatMessageContainerView.java | 2 +- .../main/content/chat/list_view/ChatMessageListItem.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/{components => chat}/chat_messages/ChatMessageContainerController.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/{components => chat}/chat_messages/ChatMessageContainerModel.java (96%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/{components => chat}/chat_messages/ChatMessageContainerView.java (99%) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorController.java index 4dcc2fa64c..123061ccc5 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorController.java @@ -28,7 +28,7 @@ import bisq.desktop.common.observable.FxBindings; import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Controller; -import bisq.desktop.main.content.components.chat_messages.ChatMessageContainerController; +import bisq.desktop.main.content.chat.chat_messages.ChatMessageContainerController; import bisq.support.mediation.MediationCase; import bisq.support.mediation.MediationRequest; import bisq.support.mediation.MediatorService; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/BaseChatController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/BaseChatController.java index ac5c19aa72..c1b7e21981 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/BaseChatController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/BaseChatController.java @@ -32,9 +32,9 @@ import bisq.desktop.common.view.NavigationController; import bisq.desktop.components.cathash.CatHash; import bisq.desktop.components.controls.BisqIconButton; +import bisq.desktop.main.content.chat.chat_messages.ChatMessageContainerController; import bisq.desktop.main.content.chat.sidebar.ChannelSidebar; import bisq.desktop.main.content.chat.sidebar.UserProfileSidebar; -import bisq.desktop.main.content.components.chat_messages.ChatMessageContainerController; import bisq.i18n.Res; import bisq.user.identity.UserIdentityService; import bisq.user.profile.UserProfile; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerController.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerController.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerController.java index e799d04b6f..11f7e38078 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerController.java @@ -1,4 +1,4 @@ -package bisq.desktop.main.content.components.chat_messages; +package bisq.desktop.main.content.chat.chat_messages; import bisq.bisq_easy.NavigationTarget; import bisq.chat.*; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerModel.java similarity index 96% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerModel.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerModel.java index 6de5ff832c..42ef9d1967 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerModel.java @@ -1,4 +1,4 @@ -package bisq.desktop.main.content.components.chat_messages; +package bisq.desktop.main.content.chat.chat_messages; import bisq.chat.ChatChannel; import bisq.chat.ChatChannelDomain; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerView.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerView.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerView.java index ca21863965..4611dbf178 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/chat_messages/ChatMessageContainerView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerView.java @@ -1,4 +1,4 @@ -package bisq.desktop.main.content.components.chat_messages; +package bisq.desktop.main.content.chat.chat_messages; import bisq.chat.ChatChannel; import bisq.common.util.StringUtils; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java index 6b6457ab87..e3b62bf9b3 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java @@ -67,7 +67,7 @@ import static bisq.chat.ChatMessageType.LEAVE; import static bisq.chat.ChatMessageType.PROTOCOL_LOG_MESSAGE; -import static bisq.desktop.main.content.components.chat_messages.ChatMessageContainerView.EDITED_POST_FIX; +import static bisq.desktop.main.content.chat.chat_messages.ChatMessageContainerView.EDITED_POST_FIX; @Slf4j @Getter From 42932958fdbe60ef1a4b58bc81c884d81d3941f1 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 18:07:30 +0700 Subject: [PATCH 100/119] Refactor: Rename package --- .../content/authorized_role/mediator/MediatorController.java | 2 +- .../java/bisq/desktop/main/content/chat/BaseChatController.java | 2 +- .../main/content/chat/list_view/ChatMessageListItem.java | 2 +- .../ChatMessageContainerController.java | 2 +- .../ChatMessageContainerModel.java | 2 +- .../ChatMessageContainerView.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/{chat_messages => message_container}/ChatMessageContainerController.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/{chat_messages => message_container}/ChatMessageContainerModel.java (97%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/{chat_messages => message_container}/ChatMessageContainerView.java (99%) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorController.java index 123061ccc5..36129d7b04 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/authorized_role/mediator/MediatorController.java @@ -28,7 +28,7 @@ import bisq.desktop.common.observable.FxBindings; import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Controller; -import bisq.desktop.main.content.chat.chat_messages.ChatMessageContainerController; +import bisq.desktop.main.content.chat.message_container.ChatMessageContainerController; import bisq.support.mediation.MediationCase; import bisq.support.mediation.MediationRequest; import bisq.support.mediation.MediatorService; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/BaseChatController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/BaseChatController.java index c1b7e21981..ef767dd0ab 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/BaseChatController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/BaseChatController.java @@ -32,7 +32,7 @@ import bisq.desktop.common.view.NavigationController; import bisq.desktop.components.cathash.CatHash; import bisq.desktop.components.controls.BisqIconButton; -import bisq.desktop.main.content.chat.chat_messages.ChatMessageContainerController; +import bisq.desktop.main.content.chat.message_container.ChatMessageContainerController; import bisq.desktop.main.content.chat.sidebar.ChannelSidebar; import bisq.desktop.main.content.chat.sidebar.UserProfileSidebar; import bisq.i18n.Res; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java index e3b62bf9b3..d6149825e6 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java @@ -67,7 +67,7 @@ import static bisq.chat.ChatMessageType.LEAVE; import static bisq.chat.ChatMessageType.PROTOCOL_LOG_MESSAGE; -import static bisq.desktop.main.content.chat.chat_messages.ChatMessageContainerView.EDITED_POST_FIX; +import static bisq.desktop.main.content.chat.message_container.ChatMessageContainerView.EDITED_POST_FIX; @Slf4j @Getter diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerController.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java index 11f7e38078..5c78471dc4 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java @@ -1,4 +1,4 @@ -package bisq.desktop.main.content.chat.chat_messages; +package bisq.desktop.main.content.chat.message_container; import bisq.bisq_easy.NavigationTarget; import bisq.chat.*; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerModel.java similarity index 97% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerModel.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerModel.java index 42ef9d1967..7a4b9cccef 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerModel.java @@ -1,4 +1,4 @@ -package bisq.desktop.main.content.chat.chat_messages; +package bisq.desktop.main.content.chat.message_container; import bisq.chat.ChatChannel; import bisq.chat.ChatChannelDomain; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerView.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java index 4611dbf178..878ff0f0cc 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/chat_messages/ChatMessageContainerView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java @@ -1,4 +1,4 @@ -package bisq.desktop.main.content.chat.chat_messages; +package bisq.desktop.main.content.chat.message_container; import bisq.chat.ChatChannel; import bisq.common.util.StringUtils; From 8e938288b9c2d2903ce9b3cec03a92eba669f07a Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 18:07:47 +0700 Subject: [PATCH 101/119] Refactor: Move package --- .../bisq_easy/offerbook/BisqEasyOfferbookView.java | 2 +- .../main/content/bisq_easy/offerbook/Filters.java | 2 +- .../content/bisq_easy/offerbook/MyOfferMessageBox.java | 8 ++++---- .../bisq_easy/offerbook/PeerOfferMessageBox.java | 8 ++++---- .../bisq_easy/open_trades/MyProtocolLogMessageBox.java | 4 ++-- .../open_trades/PeerProtocolLogMessageBox.java | 4 ++-- .../ChatMessageContainerController.java | 4 ++-- .../list_view/ChatMessageListCellFactory.java | 10 +++++----- .../list_view/ChatMessageListItem.java | 2 +- .../list_view/ChatMessagesListController.java | 2 +- .../list_view/ChatMessagesListModel.java | 2 +- .../list_view/ChatMessagesListView.java | 2 +- .../list_view/message_box/BubbleMessageBox.java | 8 ++++---- .../list_view/message_box/LeaveChatMessageBox.java | 6 +++--- .../list_view/message_box/MessageBox.java | 2 +- .../list_view/message_box/MyMessageBox.java | 8 ++++---- .../list_view/message_box/PeerMessageBox.java | 8 ++++---- 17 files changed, 41 insertions(+), 41 deletions(-) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/{ => message_container}/list_view/ChatMessageListCellFactory.java (93%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/{ => message_container}/list_view/ChatMessageListItem.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/{ => message_container}/list_view/ChatMessagesListController.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/{ => message_container}/list_view/ChatMessagesListModel.java (97%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/{ => message_container}/list_view/ChatMessagesListView.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/{ => message_container}/list_view/message_box/BubbleMessageBox.java (96%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/{ => message_container}/list_view/message_box/LeaveChatMessageBox.java (86%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/{ => message_container}/list_view/message_box/MessageBox.java (91%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/{ => message_container}/list_view/message_box/MyMessageBox.java (96%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/{ => message_container}/list_view/message_box/PeerMessageBox.java (94%) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java index 9fe89ad767..51653799bb 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java @@ -29,7 +29,7 @@ import bisq.desktop.components.table.BisqTableColumn; import bisq.desktop.components.table.BisqTableView; import bisq.desktop.main.content.chat.ChatView; -import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; import bisq.i18n.Res; import javafx.beans.binding.Bindings; import javafx.collections.ListChangeListener; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java index 8af389c617..49e5499911 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java @@ -19,7 +19,7 @@ import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; -import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; import lombok.Getter; import java.util.function.Predicate; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java index af842c320b..343bafae4d 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java @@ -22,10 +22,10 @@ import bisq.desktop.components.containers.Spacer; import bisq.desktop.components.controls.DropdownMenu; import bisq.desktop.components.controls.DropdownMenuItem; -import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; -import bisq.desktop.main.content.chat.list_view.ChatMessagesListModel; -import bisq.desktop.main.content.chat.list_view.message_box.BubbleMessageBox; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListModel; +import bisq.desktop.main.content.chat.message_container.list_view.message_box.BubbleMessageBox; import bisq.i18n.Res; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java index c910409467..3f19d779f0 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java @@ -21,10 +21,10 @@ import bisq.chat.ChatMessage; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; import bisq.desktop.components.containers.Spacer; -import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; -import bisq.desktop.main.content.chat.list_view.ChatMessagesListModel; -import bisq.desktop.main.content.chat.list_view.message_box.PeerMessageBox; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListModel; +import bisq.desktop.main.content.chat.message_container.list_view.message_box.PeerMessageBox; import bisq.i18n.Res; import javafx.geometry.Insets; import javafx.geometry.Pos; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java index 1e253a7376..c7242e6c3b 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java @@ -21,8 +21,8 @@ import bisq.chat.ChatMessage; import bisq.desktop.common.Icons; import bisq.desktop.components.controls.BisqTooltip; -import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListController; import bisq.network.p2p.services.confidential.ack.MessageDeliveryStatus; import de.jensd.fx.fontawesome.AwesomeDude; import javafx.geometry.Pos; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java index 6576cf0de8..74a0bb7931 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java @@ -19,8 +19,8 @@ import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; -import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.list_view.message_box.MessageBox; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list_view.message_box.MessageBox; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Label; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java index 5c78471dc4..d030da765e 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java @@ -12,8 +12,8 @@ import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Navigation; import bisq.desktop.components.overlay.Popup; -import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListController; import bisq.desktop.main.content.components.CitationBlock; import bisq.desktop.main.content.components.UserProfileSelection; import bisq.i18n.Res; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListCellFactory.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessageListCellFactory.java similarity index 93% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListCellFactory.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessageListCellFactory.java index cdef970661..d9e4afcbdc 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListCellFactory.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessageListCellFactory.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.chat.list_view; +package bisq.desktop.main.content.chat.message_container.list_view; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -23,10 +23,10 @@ import bisq.desktop.main.content.bisq_easy.offerbook.PeerOfferMessageBox; import bisq.desktop.main.content.bisq_easy.open_trades.MyProtocolLogMessageBox; import bisq.desktop.main.content.bisq_easy.open_trades.PeerProtocolLogMessageBox; -import bisq.desktop.main.content.chat.list_view.message_box.LeaveChatMessageBox; -import bisq.desktop.main.content.chat.list_view.message_box.MessageBox; -import bisq.desktop.main.content.chat.list_view.message_box.MyMessageBox; -import bisq.desktop.main.content.chat.list_view.message_box.PeerMessageBox; +import bisq.desktop.main.content.chat.message_container.list_view.message_box.LeaveChatMessageBox; +import bisq.desktop.main.content.chat.message_container.list_view.message_box.MessageBox; +import bisq.desktop.main.content.chat.message_container.list_view.message_box.MyMessageBox; +import bisq.desktop.main.content.chat.message_container.list_view.message_box.PeerMessageBox; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessageListItem.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessageListItem.java index d6149825e6..f0f401da71 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessageListItem.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessageListItem.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.chat.list_view; +package bisq.desktop.main.content.chat.message_container.list_view; import bisq.bonded_roles.market_price.MarketPriceService; import bisq.chat.ChatChannel; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListController.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListController.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListController.java index 09d58d0a93..ec02d7d704 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListController.java @@ -1,4 +1,4 @@ -package bisq.desktop.main.content.chat.list_view; +package bisq.desktop.main.content.chat.message_container.list_view; import bisq.bisq_easy.BisqEasyService; import bisq.bisq_easy.NavigationTarget; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListModel.java similarity index 97% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListModel.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListModel.java index dfe05e10a4..eaf2a478f4 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListModel.java @@ -1,4 +1,4 @@ -package bisq.desktop.main.content.chat.list_view; +package bisq.desktop.main.content.chat.message_container.list_view; import bisq.chat.ChatChannel; import bisq.chat.ChatChannelDomain; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListView.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListView.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListView.java index 21cde6ee49..e8c404bf9a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/ChatMessagesListView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListView.java @@ -1,4 +1,4 @@ -package bisq.desktop.main.content.chat.list_view; +package bisq.desktop.main.content.chat.message_container.list_view; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/BubbleMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/BubbleMessageBox.java similarity index 96% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/BubbleMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/BubbleMessageBox.java index ab5294032f..978f2568be 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/BubbleMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/BubbleMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.chat.list_view.message_box; +package bisq.desktop.main.content.chat.message_container.list_view.message_box; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -24,9 +24,9 @@ import bisq.desktop.common.Icons; import bisq.desktop.common.utils.ClipboardUtil; import bisq.desktop.components.controls.BisqTooltip; -import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; -import bisq.desktop.main.content.chat.list_view.ChatMessagesListModel; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListModel; import bisq.desktop.main.content.components.UserProfileIcon; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/LeaveChatMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/LeaveChatMessageBox.java similarity index 86% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/LeaveChatMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/LeaveChatMessageBox.java index 531a0286d3..6f61b9edb4 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/LeaveChatMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/LeaveChatMessageBox.java @@ -15,14 +15,14 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.chat.list_view.message_box; +package bisq.desktop.main.content.chat.message_container.list_view.message_box; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; import bisq.desktop.common.utils.ImageUtil; import bisq.desktop.main.content.bisq_easy.open_trades.MyProtocolLogMessageBox; -import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListController; import bisq.i18n.Res; import javafx.scene.control.Hyperlink; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/MessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/MessageBox.java similarity index 91% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/MessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/MessageBox.java index bba6a897d0..a0f382319f 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/MessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/MessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.chat.list_view.message_box; +package bisq.desktop.main.content.chat.message_container.list_view.message_box; import javafx.scene.layout.VBox; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/MyMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/MyMessageBox.java similarity index 96% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/MyMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/MyMessageBox.java index 8846e2eeaf..9df5e134ef 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/MyMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/MyMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.chat.list_view.message_box; +package bisq.desktop.main.content.chat.message_container.list_view.message_box; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -24,9 +24,9 @@ import bisq.desktop.components.containers.Spacer; import bisq.desktop.components.controls.BisqTextArea; import bisq.desktop.components.controls.BisqTooltip; -import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; -import bisq.desktop.main.content.chat.list_view.ChatMessagesListModel; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListModel; import bisq.i18n.Res; import bisq.network.p2p.services.confidential.ack.MessageDeliveryStatus; import de.jensd.fx.fontawesome.AwesomeDude; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/PeerMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/PeerMessageBox.java similarity index 94% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/PeerMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/PeerMessageBox.java index 0d13e2bbd9..18a42cfc9b 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/list_view/message_box/PeerMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/PeerMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.chat.list_view.message_box; +package bisq.desktop.main.content.chat.message_container.list_view.message_box; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -24,9 +24,9 @@ import bisq.desktop.components.controls.BisqPopup; import bisq.desktop.components.controls.BisqPopupMenu; import bisq.desktop.components.controls.BisqPopupMenuItem; -import bisq.desktop.main.content.chat.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.list_view.ChatMessagesListController; -import bisq.desktop.main.content.chat.list_view.ChatMessagesListModel; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListModel; import bisq.i18n.Res; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; From 0895af5186f455c1b4de5c59449205bcb7f53ab1 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 18:08:27 +0700 Subject: [PATCH 102/119] Refactor: Rename package --- .../bisq_easy/offerbook/BisqEasyOfferbookView.java | 2 +- .../main/content/bisq_easy/offerbook/Filters.java | 2 +- .../content/bisq_easy/offerbook/MyOfferMessageBox.java | 8 ++++---- .../bisq_easy/offerbook/PeerOfferMessageBox.java | 8 ++++---- .../bisq_easy/open_trades/MyProtocolLogMessageBox.java | 4 ++-- .../open_trades/PeerProtocolLogMessageBox.java | 4 ++-- .../ChatMessageContainerController.java | 4 ++-- .../ChatMessageListCellFactory.java | 10 +++++----- .../{list_view => list}/ChatMessageListItem.java | 2 +- .../ChatMessagesListController.java | 2 +- .../{list_view => list}/ChatMessagesListModel.java | 2 +- .../{list_view => list}/ChatMessagesListView.java | 2 +- .../message_box/BubbleMessageBox.java | 8 ++++---- .../message_box/LeaveChatMessageBox.java | 6 +++--- .../{list_view => list}/message_box/MessageBox.java | 2 +- .../{list_view => list}/message_box/MyMessageBox.java | 8 ++++---- .../message_box/PeerMessageBox.java | 8 ++++---- 17 files changed, 41 insertions(+), 41 deletions(-) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/{list_view => list}/ChatMessageListCellFactory.java (93%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/{list_view => list}/ChatMessageListItem.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/{list_view => list}/ChatMessagesListController.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/{list_view => list}/ChatMessagesListModel.java (97%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/{list_view => list}/ChatMessagesListView.java (99%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/{list_view => list}/message_box/BubbleMessageBox.java (96%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/{list_view => list}/message_box/LeaveChatMessageBox.java (86%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/{list_view => list}/message_box/MessageBox.java (91%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/{list_view => list}/message_box/MyMessageBox.java (96%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/{list_view => list}/message_box/PeerMessageBox.java (94%) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java index 51653799bb..3f415c2606 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/BisqEasyOfferbookView.java @@ -29,7 +29,7 @@ import bisq.desktop.components.table.BisqTableColumn; import bisq.desktop.components.table.BisqTableView; import bisq.desktop.main.content.chat.ChatView; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list.ChatMessageListItem; import bisq.i18n.Res; import javafx.beans.binding.Bindings; import javafx.collections.ListChangeListener; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java index 49e5499911..bf0e5cb3f2 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/Filters.java @@ -19,7 +19,7 @@ import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list.ChatMessageListItem; import lombok.Getter; import java.util.function.Predicate; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java index 343bafae4d..2b3d270119 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/MyOfferMessageBox.java @@ -22,10 +22,10 @@ import bisq.desktop.components.containers.Spacer; import bisq.desktop.components.controls.DropdownMenu; import bisq.desktop.components.controls.DropdownMenuItem; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListController; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListModel; -import bisq.desktop.main.content.chat.message_container.list_view.message_box.BubbleMessageBox; +import bisq.desktop.main.content.chat.message_container.list.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListController; +import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListModel; +import bisq.desktop.main.content.chat.message_container.list.message_box.BubbleMessageBox; import bisq.i18n.Res; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java index 3f19d779f0..f4cdcbb480 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/offerbook/PeerOfferMessageBox.java @@ -21,10 +21,10 @@ import bisq.chat.ChatMessage; import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage; import bisq.desktop.components.containers.Spacer; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListController; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListModel; -import bisq.desktop.main.content.chat.message_container.list_view.message_box.PeerMessageBox; +import bisq.desktop.main.content.chat.message_container.list.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListController; +import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListModel; +import bisq.desktop.main.content.chat.message_container.list.message_box.PeerMessageBox; import bisq.i18n.Res; import javafx.geometry.Insets; import javafx.geometry.Pos; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java index c7242e6c3b..ccc53c5761 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/MyProtocolLogMessageBox.java @@ -21,8 +21,8 @@ import bisq.chat.ChatMessage; import bisq.desktop.common.Icons; import bisq.desktop.components.controls.BisqTooltip; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.message_container.list.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListController; import bisq.network.p2p.services.confidential.ack.MessageDeliveryStatus; import de.jensd.fx.fontawesome.AwesomeDude; import javafx.geometry.Pos; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java index 74a0bb7931..2172ba3c13 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/PeerProtocolLogMessageBox.java @@ -19,8 +19,8 @@ import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.message_container.list_view.message_box.MessageBox; +import bisq.desktop.main.content.chat.message_container.list.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list.message_box.MessageBox; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Label; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java index d030da765e..fa13c4088f 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java @@ -12,8 +12,8 @@ import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Navigation; import bisq.desktop.components.overlay.Popup; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.message_container.list.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListController; import bisq.desktop.main.content.components.CitationBlock; import bisq.desktop.main.content.components.UserProfileSelection; import bisq.i18n.Res; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessageListCellFactory.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessageListCellFactory.java similarity index 93% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessageListCellFactory.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessageListCellFactory.java index d9e4afcbdc..8ff20a43d7 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessageListCellFactory.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessageListCellFactory.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.chat.message_container.list_view; +package bisq.desktop.main.content.chat.message_container.list; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -23,10 +23,10 @@ import bisq.desktop.main.content.bisq_easy.offerbook.PeerOfferMessageBox; import bisq.desktop.main.content.bisq_easy.open_trades.MyProtocolLogMessageBox; import bisq.desktop.main.content.bisq_easy.open_trades.PeerProtocolLogMessageBox; -import bisq.desktop.main.content.chat.message_container.list_view.message_box.LeaveChatMessageBox; -import bisq.desktop.main.content.chat.message_container.list_view.message_box.MessageBox; -import bisq.desktop.main.content.chat.message_container.list_view.message_box.MyMessageBox; -import bisq.desktop.main.content.chat.message_container.list_view.message_box.PeerMessageBox; +import bisq.desktop.main.content.chat.message_container.list.message_box.LeaveChatMessageBox; +import bisq.desktop.main.content.chat.message_container.list.message_box.MessageBox; +import bisq.desktop.main.content.chat.message_container.list.message_box.MyMessageBox; +import bisq.desktop.main.content.chat.message_container.list.message_box.PeerMessageBox; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessageListItem.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessageListItem.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessageListItem.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessageListItem.java index f0f401da71..b98c96849d 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessageListItem.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessageListItem.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.chat.message_container.list_view; +package bisq.desktop.main.content.chat.message_container.list; import bisq.bonded_roles.market_price.MarketPriceService; import bisq.chat.ChatChannel; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListController.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java index ec02d7d704..e2195a3ae8 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java @@ -1,4 +1,4 @@ -package bisq.desktop.main.content.chat.message_container.list_view; +package bisq.desktop.main.content.chat.message_container.list; import bisq.bisq_easy.BisqEasyService; import bisq.bisq_easy.NavigationTarget; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListModel.java similarity index 97% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListModel.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListModel.java index eaf2a478f4..2286bac28c 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListModel.java @@ -1,4 +1,4 @@ -package bisq.desktop.main.content.chat.message_container.list_view; +package bisq.desktop.main.content.chat.message_container.list; import bisq.chat.ChatChannel; import bisq.chat.ChatChannelDomain; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListView.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListView.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListView.java index e8c404bf9a..9f4d1ccb98 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/ChatMessagesListView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListView.java @@ -1,4 +1,4 @@ -package bisq.desktop.main.content.chat.message_container.list_view; +package bisq.desktop.main.content.chat.message_container.list; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/BubbleMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/BubbleMessageBox.java similarity index 96% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/BubbleMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/BubbleMessageBox.java index 978f2568be..bed346ae3b 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/BubbleMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/BubbleMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.chat.message_container.list_view.message_box; +package bisq.desktop.main.content.chat.message_container.list.message_box; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -24,9 +24,9 @@ import bisq.desktop.common.Icons; import bisq.desktop.common.utils.ClipboardUtil; import bisq.desktop.components.controls.BisqTooltip; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListController; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListModel; +import bisq.desktop.main.content.chat.message_container.list.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListController; +import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListModel; import bisq.desktop.main.content.components.UserProfileIcon; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/LeaveChatMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/LeaveChatMessageBox.java similarity index 86% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/LeaveChatMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/LeaveChatMessageBox.java index 6f61b9edb4..94aa9d0824 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/LeaveChatMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/LeaveChatMessageBox.java @@ -15,14 +15,14 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.chat.message_container.list_view.message_box; +package bisq.desktop.main.content.chat.message_container.list.message_box; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; import bisq.desktop.common.utils.ImageUtil; import bisq.desktop.main.content.bisq_easy.open_trades.MyProtocolLogMessageBox; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListController; +import bisq.desktop.main.content.chat.message_container.list.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListController; import bisq.i18n.Res; import javafx.scene.control.Hyperlink; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/MessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/MessageBox.java similarity index 91% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/MessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/MessageBox.java index a0f382319f..ede85e582e 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/MessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/MessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.chat.message_container.list_view.message_box; +package bisq.desktop.main.content.chat.message_container.list.message_box; import javafx.scene.layout.VBox; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/MyMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/MyMessageBox.java similarity index 96% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/MyMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/MyMessageBox.java index 9df5e134ef..5c0f974691 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/MyMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/MyMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.chat.message_container.list_view.message_box; +package bisq.desktop.main.content.chat.message_container.list.message_box; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -24,9 +24,9 @@ import bisq.desktop.components.containers.Spacer; import bisq.desktop.components.controls.BisqTextArea; import bisq.desktop.components.controls.BisqTooltip; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListController; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListModel; +import bisq.desktop.main.content.chat.message_container.list.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListController; +import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListModel; import bisq.i18n.Res; import bisq.network.p2p.services.confidential.ack.MessageDeliveryStatus; import de.jensd.fx.fontawesome.AwesomeDude; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/PeerMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/PeerMessageBox.java similarity index 94% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/PeerMessageBox.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/PeerMessageBox.java index 18a42cfc9b..0bde387ac4 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list_view/message_box/PeerMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/PeerMessageBox.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.chat.message_container.list_view.message_box; +package bisq.desktop.main.content.chat.message_container.list.message_box; import bisq.chat.ChatChannel; import bisq.chat.ChatMessage; @@ -24,9 +24,9 @@ import bisq.desktop.components.controls.BisqPopup; import bisq.desktop.components.controls.BisqPopupMenu; import bisq.desktop.components.controls.BisqPopupMenuItem; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessageListItem; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListController; -import bisq.desktop.main.content.chat.message_container.list_view.ChatMessagesListModel; +import bisq.desktop.main.content.chat.message_container.list.ChatMessageListItem; +import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListController; +import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListModel; import bisq.i18n.Res; import de.jensd.fx.fontawesome.AwesomeIcon; import javafx.geometry.Insets; From f5be19d8a5869c498f74f0fcbeb8332fd0b49513 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 18:52:03 +0700 Subject: [PATCH 103/119] Use run instead of runOnNextRenderFrame --- .../list/ChatMessagesListController.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java index e2195a3ae8..77635b264d 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java @@ -500,6 +500,7 @@ void onScrollToBottom() { applyScrollValue(1); } + /////////////////////////////////////////////////////////////////////////////////////////////////// // Private /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -575,11 +576,7 @@ private > Pin bindChatMessages(C return channel.getChatMessages().addObserver(new CollectionObserver<>() { @Override public void add(M chatMessage) { - // TODO (low prio) Delaying to the next render frame can cause duplicated items in case we get the channel - // change called 2 times in short interval (should be avoid as well). - // @namloan Could you re-test the performance issues with testing if using UIThread.run makes a difference? - // There have been many changes in the meantime, so maybe the performance issue was fixed by other changes. - UIThread.runOnNextRenderFrame(() -> { + UIThread.run(() -> { ChatMessageListItem item = new ChatMessageListItem<>(chatMessage, channel, marketPriceService, @@ -601,7 +598,7 @@ public void add(M chatMessage) { @Override public void remove(Object element) { if (element instanceof ChatMessage) { - UIThread.runOnNextRenderFrame(() -> { + UIThread.run(() -> { ChatMessage chatMessage = (ChatMessage) element; Optional>> toRemove = model.getChatMessages().stream() @@ -617,7 +614,7 @@ public void remove(Object element) { @Override public void clear() { - UIThread.runOnNextRenderFrame(() -> { + UIThread.run(() -> { model.getChatMessages().forEach(ChatMessageListItem::dispose); model.getChatMessages().clear(); }); From 2fc9cc31cf8d3f64ec0a810cd76aa4fa2b246fb0 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Sun, 31 Mar 2024 19:06:21 +0700 Subject: [PATCH 104/119] Add chatMessageIds set for lookup at add to avoid creating item renderer to check if item already exists. --- .../list/ChatMessagesListController.java | 26 +++++++++++++------ .../list/ChatMessagesListModel.java | 3 +++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java index 77635b264d..b91b605dd0 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java @@ -169,6 +169,7 @@ public void onDeactivate() { model.getChatMessages().forEach(ChatMessageListItem::dispose); model.getChatMessages().clear(); + model.getChatMessageIds().clear(); } private void selectedChannelChanged(ChatChannel channel) { @@ -183,6 +184,7 @@ private void selectedChannelChanged(ChatChannel channel) // Clear and call dispose on the current messages when we change the channel. model.getChatMessages().forEach(ChatMessageListItem::dispose); model.getChatMessages().clear(); + model.getChatMessageIds().clear(); if (channel instanceof BisqEasyOfferbookChannel) { chatMessagesPin = bindChatMessages((BisqEasyOfferbookChannel) channel); @@ -228,6 +230,10 @@ private void selectedChannelChanged(ChatChannel channel) public void refreshMessages() { model.getChatMessages().setAll(new ArrayList<>(model.getChatMessages())); + model.getChatMessageIds().clear(); + model.getChatMessageIds().addAll(model.getChatMessages().stream() + .map(e -> e.getChatMessage().getId()) + .collect(Collectors.toSet())); } public void setSearchPredicate(Predicate>> predicate) { @@ -559,8 +565,7 @@ private void applyPredicate() { private > Pin bindChatMessages(C channel) { // We clear and fill the list at channel change. The addObserver triggers the add method for each item, // but as we have a contains() check there it will not have any effect. - model.getChatMessages().clear(); - model.getChatMessages().addAll(channel.getChatMessages().stream() + model.getChatMessages().setAll(channel.getChatMessages().stream() .map(chatMessage -> new ChatMessageListItem<>(chatMessage, channel, marketPriceService, @@ -571,12 +576,19 @@ private > Pin bindChatMessages(C networkService, resendMessageService)) .collect(Collectors.toSet())); + model.getChatMessageIds().clear(); + model.getChatMessageIds().addAll(model.getChatMessages().stream() + .map(e -> e.getChatMessage().getId()) + .collect(Collectors.toSet())); maybeScrollDownOnNewItemAdded(); return channel.getChatMessages().addObserver(new CollectionObserver<>() { @Override public void add(M chatMessage) { UIThread.run(() -> { + if (model.getChatMessageIds().contains(chatMessage.getId())) { + return; + } ChatMessageListItem item = new ChatMessageListItem<>(chatMessage, channel, marketPriceService, @@ -586,12 +598,8 @@ public void add(M chatMessage) { userIdentityService, networkService, resendMessageService); - // As long as we use runOnNextRenderFrame we need to check to avoid adding duplicates - // The model is updated async in stages, verify that messages belong to the selected channel - if (!model.getChatMessages().contains(item) && channel.equals(model.getSelectedChannel().get())) { - model.getChatMessages().add(item); - maybeScrollDownOnNewItemAdded(); - } + model.getChatMessages().add(item); + maybeScrollDownOnNewItemAdded(); }); } @@ -607,6 +615,7 @@ public void remove(Object element) { toRemove.ifPresent(item -> { item.dispose(); model.getChatMessages().remove(item); + model.getChatMessageIds().remove(item.getChatMessage().getId()); }); }); } @@ -617,6 +626,7 @@ public void clear() { UIThread.run(() -> { model.getChatMessages().forEach(ChatMessageListItem::dispose); model.getChatMessages().clear(); + model.getChatMessageIds().clear(); }); } }); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListModel.java index 2286bac28c..e12566bd6a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListModel.java @@ -12,6 +12,8 @@ import lombok.Getter; import lombok.Setter; +import java.util.HashSet; +import java.util.Set; import java.util.function.Predicate; @Getter @@ -21,6 +23,7 @@ public class ChatMessagesListModel implements bisq.desktop.common.view.Model { private final ObservableList>> chatMessages = FXCollections.observableArrayList(); private final FilteredList>> filteredChatMessages = new FilteredList<>(chatMessages); private final SortedList>> sortedChatMessages = new SortedList<>(filteredChatMessages); + private final Set chatMessageIds = new HashSet<>(); private final BooleanProperty isPublicChannel = new SimpleBooleanProperty(); private final ObjectProperty selectedChatMessageForMoreOptionsPopup = new SimpleObjectProperty<>(null); private final ChatChannelDomain chatChannelDomain; From eb295ca1300d4506a1d995b4e47d450bdcccef5a Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Sun, 31 Mar 2024 14:07:03 +0200 Subject: [PATCH 105/119] Only send system notification for real time messages While online, for each message we (1) show the badge and (2) send a system notification. The latter should be avoided when a message was received before the user went offline. This prevents a flood of system notifications at start-up. Resolves #1546 --- .../desktop_app/DesktopApplicationService.java | 2 +- chat/src/main/java/bisq/chat/ChatService.java | 2 +- .../notifications/ChatNotificationService.java | 16 +++++++++++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/desktop/desktop-app/src/main/java/bisq/desktop_app/DesktopApplicationService.java b/apps/desktop/desktop-app/src/main/java/bisq/desktop_app/DesktopApplicationService.java index 9fcbbb0ffc..c9df551786 100644 --- a/apps/desktop/desktop-app/src/main/java/bisq/desktop_app/DesktopApplicationService.java +++ b/apps/desktop/desktop-app/src/main/java/bisq/desktop_app/DesktopApplicationService.java @@ -243,7 +243,7 @@ public CompletableFuture initialize() { .thenCompose(result -> settingsService.initialize()) .thenCompose(result -> offerService.initialize()) .thenCompose(result -> chatService.initialize()) - .thenCompose(result -> sendNotificationService.initialize()) // We initialize after chatService to avoid flooding the notification center + .thenCompose(result -> sendNotificationService.initialize()) .thenCompose(result -> supportService.initialize()) .thenCompose(result -> tradeService.initialize()) .thenCompose(result -> updaterService.initialize()) diff --git a/chat/src/main/java/bisq/chat/ChatService.java b/chat/src/main/java/bisq/chat/ChatService.java index b55f51a221..af7236c33a 100644 --- a/chat/src/main/java/bisq/chat/ChatService.java +++ b/chat/src/main/java/bisq/chat/ChatService.java @@ -84,7 +84,7 @@ public ChatService(PersistenceService persistenceService, userIdentityService, userProfileService); - //BISQ_EASY + // BISQ_EASY bisqEasyOfferbookChannelService = new BisqEasyOfferbookChannelService(persistenceService, networkService, userService); diff --git a/chat/src/main/java/bisq/chat/notifications/ChatNotificationService.java b/chat/src/main/java/bisq/chat/notifications/ChatNotificationService.java index 9222e1ad94..364aa4e509 100644 --- a/chat/src/main/java/bisq/chat/notifications/ChatNotificationService.java +++ b/chat/src/main/java/bisq/chat/notifications/ChatNotificationService.java @@ -75,6 +75,7 @@ public class ChatNotificationService implements PersistenceClient changedNotification = new Observable<>(); private final Map chatMessagesByChannelIdPins = new ConcurrentHashMap<>(); + private final long startUpDateTime = System.currentTimeMillis(); public ChatNotificationService(PersistenceService persistenceService, ChatService chatService, @@ -347,7 +348,7 @@ private void onMessageAdded(ChatChannel chatChannel, if (shouldSendNotification) { addNotification(chatNotification); - sendNotificationService.send(chatNotification); + maybeSendSystemNotification(chatNotification); } else { consumeNotification(chatNotification); } @@ -380,4 +381,17 @@ private ChatNotification createNotification(String id, C chatMessage, senderUserProfile); } + + private void maybeSendSystemNotification(ChatNotification chatNotification) { + if (isReceivedAfterStartUp(chatNotification)) { + log.debug("Sending system notification since message was received after start-up time"); + sendNotificationService.send(chatNotification); + } else { + log.debug("Avoiding system notification for notifications sent while user was offline"); + } + } + + private boolean isReceivedAfterStartUp(ChatNotification chatNotification) { + return chatNotification.getDate() > startUpDateTime; + } } From e5640135f645fbd3176369c69654b0852d8bf318 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 1 Apr 2024 00:08:08 +0700 Subject: [PATCH 106/119] Add CustomStackPane allowing to get notified when layoutChildren method is completed. Used to trigger update of the scroll state. --- .../list/ChatMessageListCellFactory.java | 11 +++++----- .../list/ChatMessagesListController.java | 13 +++++++++++- .../list/ChatMessagesListModel.java | 2 ++ .../list/ChatMessagesListView.java | 21 +++++++++++++++++-- .../list/message_box/BubbleMessageBox.java | 13 +++++++----- .../list/message_box/MyMessageBox.java | 2 ++ .../list/message_box/PeerMessageBox.java | 4 +++- 7 files changed, 51 insertions(+), 15 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessageListCellFactory.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessageListCellFactory.java index 8ff20a43d7..7393cfabe6 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessageListCellFactory.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessageListCellFactory.java @@ -34,9 +34,11 @@ import javafx.scene.control.ListView; import javafx.scene.layout.HBox; import javafx.util.Callback; +import lombok.extern.slf4j.Slf4j; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; +@Slf4j final class ChatMessageListCellFactory implements Callback>>, ListCell>>> { @@ -57,13 +59,13 @@ public ChatMessageListCellFactory(ChatMessagesListController controller, ChatMes private final static String STYLE_CLASS_WITH_SCROLLBAR_FULL_WIDTH = "chat-message-list-cell-w-scrollbar-full-width"; private final static String STYLE_CLASS_WITH_SCROLLBAR_MAX_WIDTH = "chat-message-list-cell-w-scrollbar-max-width"; - private final HBox cellHBox; + private final HBox cellHBox = new HBox(); private Subscription listWidthPropertyPin; private MessageBox messageBox; { - cellHBox = new HBox(); cellHBox.setPadding(new Insets(15, 0, 15, 0)); + setAlignment(Pos.CENTER); } @Override @@ -78,6 +80,7 @@ public void updateItem(final ChatMessageListItem updateMessageStyle()); setGraphic(cellHBox); - setAlignment(Pos.CENTER); } private void cleanup() { @@ -93,9 +95,6 @@ private void cleanup() { messageBox.cleanup(); } - cellHBox.setOnMouseEntered(null); - cellHBox.setOnMouseExited(null); - if (listWidthPropertyPin != null) { listWidthPropertyPin.unsubscribe(); } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java index b91b605dd0..8b7821e0e5 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java @@ -88,7 +88,8 @@ public class ChatMessagesListController implements bisq.desktop.common.view.Cont private final BisqEasyService bisqEasyService; private final MarketPriceService marketPriceService; private Pin selectedChannelPin, chatMessagesPin, offerOnlySettingsPin; - private Subscription selectedChannelSubscription, focusSubscription, scrollValuePin, scrollBarVisiblePin; + private Subscription selectedChannelSubscription, focusSubscription, scrollValuePin, scrollBarVisiblePin, + layoutChildrenDonePin; public ChatMessagesListController(ServiceProvider serviceProvider, Consumer mentionUserHandler, @@ -141,6 +142,10 @@ public void onActivate() { } }); + layoutChildrenDonePin = EasyBind.subscribe(model.getLayoutChildrenDone(), layoutChildrenDone -> { + UIThread.runOnNextRenderFrame(this::handleScrollValueChanged); + }); + applyScrollValue(1); } @@ -164,6 +169,7 @@ public void onDeactivate() { selectedChannelSubscription.unsubscribe(); } + layoutChildrenDonePin.unsubscribe(); scrollValuePin.unsubscribe(); scrollBarVisiblePin.unsubscribe(); @@ -480,6 +486,11 @@ public void doLeaveChannel() { private void applyScrollValue(double scrollValue) { model.getScrollValue().set(scrollValue); + handleScrollValueChanged(); + } + + private void handleScrollValueChanged() { + double scrollValue = model.getScrollValue().get(); model.getHasUnreadMessages().set(model.getNumReadMessages() < model.getChatMessages().size()); boolean isAtBottom = scrollValue == 1d; model.getShowScrolledDownButton().set(!isAtBottom && model.getScrollBarVisible().get()); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListModel.java index e12566bd6a..e3b6eb09f2 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListModel.java @@ -24,6 +24,8 @@ public class ChatMessagesListModel implements bisq.desktop.common.view.Model { private final FilteredList>> filteredChatMessages = new FilteredList<>(chatMessages); private final SortedList>> sortedChatMessages = new SortedList<>(filteredChatMessages); private final Set chatMessageIds = new HashSet<>(); + private final BooleanProperty layoutChildrenDone = new SimpleBooleanProperty(); + private final BooleanProperty isPublicChannel = new SimpleBooleanProperty(); private final ObjectProperty selectedChatMessageForMoreOptionsPopup = new SimpleObjectProperty<>(null); private final ChatChannelDomain chatChannelDomain; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListView.java index 9f4d1ccb98..d20fa54e84 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListView.java @@ -13,6 +13,8 @@ import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; import javafx.collections.ObservableList; import javafx.geometry.Insets; import javafx.geometry.Orientation; @@ -28,6 +30,7 @@ import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.util.Duration; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; @@ -35,7 +38,19 @@ import java.util.Optional; @Slf4j -public class ChatMessagesListView extends bisq.desktop.common.view.View { +public class ChatMessagesListView extends bisq.desktop.common.view.View { + @Getter + public static class CustomStackPane extends StackPane { + private final BooleanProperty layoutChildrenDone = new SimpleBooleanProperty(); + + @Override + protected void layoutChildren() { + layoutChildrenDone.set(false); + super.layoutChildren(); + layoutChildrenDone.set(true); + } + } + private final ListView>> listView; private final ImageView scrollDownImageView; private final Badge scrollDownBadge; @@ -48,7 +63,7 @@ public class ChatMessagesListView extends bisq.desktop.common.view.View(model.getSortedChatMessages()); listView.getStyleClass().add("chat-messages-list-view"); @@ -130,6 +145,7 @@ protected void onViewAttached() { scrollDownTooltip.setText(Res.get("chat.listView.scrollDown")); } }); + model.getLayoutChildrenDone().bind(root.getLayoutChildrenDone()); scrollDownBadge.setOnMouseClicked(e -> controller.onScrollToBottom()); @@ -150,6 +166,7 @@ protected void onViewDetached() { scrollDownBackground.visibleProperty().unbind(); scrollDownBackground.managedProperty().unbind(); scrollDownBadge.textProperty().unbind(); + model.getLayoutChildrenDone().unbind(); hasUnreadMessagesPin.unsubscribe(); showScrolledDownButtonPin.unsubscribe(); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/BubbleMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/BubbleMessageBox.java index bed346ae3b..51b33f99c7 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/BubbleMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/BubbleMessageBox.java @@ -38,9 +38,11 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; +import lombok.extern.slf4j.Slf4j; import java.util.Optional; +@Slf4j public abstract class BubbleMessageBox extends MessageBox { protected static final double CHAT_MESSAGE_BOX_MAX_WIDTH = 630; protected static final double OFFER_MESSAGE_USER_ICON_SIZE = 70; @@ -117,10 +119,6 @@ protected void setUpReactions() { protected void addReactionsHandlers() { } - protected void hideReactionsBox() { - reactionsHBox.setVisible(false); - } - private void addOnMouseEventHandlers() { setOnMouseEntered(e -> { if (model.getSelectedChatMessageForMoreOptionsPopup().get() != null) { @@ -132,13 +130,18 @@ private void addOnMouseEventHandlers() { setOnMouseExited(e -> { if (model.getSelectedChatMessageForMoreOptionsPopup().get() == null) { - hideReactionsBox(); dateTime.setVisible(false); reactionsHBox.setVisible(false); } }); } + @Override + public void cleanup() { + setOnMouseEntered(null); + setOnMouseExited(null); + } + private Label createAndGetSupportedLanguagesLabel() { Label label = new Label(); if (item.isBisqEasyPublicChatMessageWithOffer()) { diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/MyMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/MyMessageBox.java index 5c0f974691..6602a547ed 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/MyMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/MyMessageBox.java @@ -235,6 +235,8 @@ private void onCloseEditMessage() { @Override public void cleanup() { + super.cleanup(); + message.maxWidthProperty().unbind(); editInputField.maxWidthProperty().unbind(); deliveryState.getTooltip().textProperty().unbind(); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/PeerMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/PeerMessageBox.java index 0bde387ac4..c0c3a5443a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/PeerMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/PeerMessageBox.java @@ -79,7 +79,7 @@ protected void setUpReactions() { protected void addReactionsHandlers() { ChatMessage chatMessage = item.getChatMessage(); moreOptionsIcon.setOnMouseClicked(e -> onOpenMoreOptions(pmIcon, chatMessage, () -> { - hideReactionsBox(); + reactionsHBox.setVisible(false); model.getSelectedChatMessageForMoreOptionsPopup().set(null); })); replyIcon.setOnMouseClicked(e -> controller.onReply(chatMessage)); @@ -134,6 +134,8 @@ private void onOpenMoreOptions(Node owner, ChatMessage chatMessage, Runnable onC @Override public void cleanup() { + super.cleanup(); + message.maxWidthProperty().unbind(); userName.setOnMouseClicked(null); From c5ca638bd77d7b51465325f4d5b6c1048206eae1 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 1 Apr 2024 00:21:52 +0700 Subject: [PATCH 107/119] setAutoScrollToBottom to true when changing channels --- .../message_container/list/ChatMessagesListController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java index 8b7821e0e5..0987af3a7e 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java @@ -143,7 +143,7 @@ public void onActivate() { }); layoutChildrenDonePin = EasyBind.subscribe(model.getLayoutChildrenDone(), layoutChildrenDone -> { - UIThread.runOnNextRenderFrame(this::handleScrollValueChanged); + handleScrollValueChanged(); }); applyScrollValue(1); @@ -191,6 +191,7 @@ private void selectedChannelChanged(ChatChannel channel) model.getChatMessages().forEach(ChatMessageListItem::dispose); model.getChatMessages().clear(); model.getChatMessageIds().clear(); + model.setAutoScrollToBottom(true); if (channel instanceof BisqEasyOfferbookChannel) { chatMessagesPin = bindChatMessages((BisqEasyOfferbookChannel) channel); From fe4aa5c159d4e21203134251ce2180df8ac9ae30 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 1 Apr 2024 01:06:56 +0700 Subject: [PATCH 108/119] Only send notification if app is not in focus --- .../src/main/java/bisq/desktop/DesktopController.java | 6 ++++++ .../bisq/chat/notifications/ChatNotificationService.java | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/DesktopController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/DesktopController.java index a152796331..5458a5ba7a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/DesktopController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/DesktopController.java @@ -18,6 +18,7 @@ package bisq.desktop; import bisq.bisq_easy.NavigationTarget; +import bisq.chat.notifications.ChatNotificationService; import bisq.common.observable.Observable; import bisq.desktop.common.Browser; import bisq.desktop.common.Transitions; @@ -44,6 +45,7 @@ import javafx.stage.Screen; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.fxmisc.easybind.EasyBind; import java.util.Optional; @@ -67,6 +69,7 @@ public class DesktopController extends NavigationController { protected final Runnable onActivatedHandler; private SplashController splashController; private final UserIdentityService userIdentityService; + private final ChatNotificationService chatNotificationService; private final ServiceProvider serviceProvider; private PreventStandbyModeService preventStandbyModeService; @@ -85,6 +88,7 @@ public DesktopController(Observable applicationServiceState, settingsService = serviceProvider.getSettingsService(); userIdentityService = serviceProvider.getUserService().getUserIdentityService(); + chatNotificationService = serviceProvider.getChatService().getChatNotificationService(); } public void init() { @@ -107,6 +111,8 @@ public void init() { view.showStage(); new OverlayController(serviceProvider, viewRoot); + + EasyBind.subscribe(viewRoot.getScene().getWindow().focusedProperty(), chatNotificationService::setApplicationFocussed); } private void setInitialScreenSize() { diff --git a/chat/src/main/java/bisq/chat/notifications/ChatNotificationService.java b/chat/src/main/java/bisq/chat/notifications/ChatNotificationService.java index 364aa4e509..0f3410273a 100644 --- a/chat/src/main/java/bisq/chat/notifications/ChatNotificationService.java +++ b/chat/src/main/java/bisq/chat/notifications/ChatNotificationService.java @@ -43,6 +43,7 @@ import bisq.user.profile.UserProfile; import bisq.user.profile.UserProfileService; import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.util.Map; @@ -76,6 +77,8 @@ public class ChatNotificationService implements PersistenceClient changedNotification = new Observable<>(); private final Map chatMessagesByChannelIdPins = new ConcurrentHashMap<>(); private final long startUpDateTime = System.currentTimeMillis(); + @Setter + private boolean isApplicationFocussed; public ChatNotificationService(PersistenceService persistenceService, ChatService chatService, @@ -383,11 +386,8 @@ private ChatNotification createNotification(String id, C } private void maybeSendSystemNotification(ChatNotification chatNotification) { - if (isReceivedAfterStartUp(chatNotification)) { - log.debug("Sending system notification since message was received after start-up time"); + if (!isApplicationFocussed && isReceivedAfterStartUp(chatNotification)) { sendNotificationService.send(chatNotification); - } else { - log.debug("Avoiding system notification for notifications sent while user was offline"); } } From c27693585081344c290c4bd4ec09a1a5de41a639 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Mon, 1 Apr 2024 17:19:59 +0700 Subject: [PATCH 109/119] Add version --- .../java/bisq/desktop/splash/SplashController.java | 2 +- .../main/java/bisq/desktop/splash/SplashModel.java | 6 ++++++ .../main/java/bisq/desktop/splash/SplashView.java | 14 ++++++++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/splash/SplashController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/splash/SplashController.java index 6232ae547a..4a9a22da02 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/splash/SplashController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/splash/SplashController.java @@ -44,7 +44,7 @@ public SplashController(Observable applicationServiceState, ServiceProvid this.applicationServiceState = applicationServiceState; this.serviceProvider = serviceProvider; networkService = serviceProvider.getNetworkService(); - model = new SplashModel(); + model = new SplashModel(serviceProvider.getConfig().getVersion()); view = new SplashView(model, this); } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/splash/SplashModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/splash/SplashModel.java index 6dd0e7dfff..4ed1ecb133 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/splash/SplashModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/splash/SplashModel.java @@ -17,6 +17,7 @@ package bisq.desktop.splash; +import bisq.common.util.Version; import bisq.desktop.common.view.Model; import bisq.desktop.splash.temp.BootstrapStateDisplay; import javafx.beans.property.DoubleProperty; @@ -31,6 +32,11 @@ @Getter public class SplashModel implements Model { private final StringProperty applicationServiceState = new SimpleStringProperty(); + private final String version; private final DoubleProperty progress = new SimpleDoubleProperty(); private final List bootstrapStateDisplays = new ArrayList<>(); + + public SplashModel(Version version) { + this.version = "v" + version.getVersionAsString(); + } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/splash/SplashView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/splash/SplashView.java index 65a06cbca7..57c02ce49d 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/splash/SplashView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/splash/SplashView.java @@ -24,6 +24,7 @@ import javafx.scene.control.ProgressBar; import javafx.scene.image.ImageView; import javafx.scene.layout.GridPane; +import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.text.TextAlignment; import lombok.extern.slf4j.Slf4j; @@ -33,6 +34,7 @@ public class SplashView extends View { public static final int WIDTH = 535; private final ProgressBar progressBar; private final Label applicationServiceState; + private final Label version; public SplashView(SplashModel model, SplashController controller) { super(new VBox(), model, controller); @@ -43,6 +45,14 @@ public SplashView(SplashModel model, SplashController controller) { ImageView logo = new ImageView(); logo.setId("logo-splash"); + version = new Label(model.getVersion()); + version.setOpacity(0.5); + version.getStyleClass().addAll("text-fill-grey-dimmed", "medium-text"); + + StackPane logoAndVersion = new StackPane(logo, version); + logoAndVersion.setAlignment(Pos.CENTER); + StackPane.setMargin(version, new Insets(-25, 0, 0, 200)); + applicationServiceState = new Label(""); applicationServiceState.getStyleClass().add("splash-application-state"); applicationServiceState.setTextAlignment(TextAlignment.CENTER); @@ -52,9 +62,9 @@ public SplashView(SplashModel model, SplashController controller) { progressBar.setMaxHeight(3); progressBar.setMinWidth(WIDTH); - VBox.setMargin(logo, new Insets(-52, 0, 83, 0)); + VBox.setMargin(logoAndVersion, new Insets(-52, 0, 83, 0)); VBox.setMargin(progressBar, new Insets(16, 0, 16, 0)); - root.getChildren().addAll(logo, applicationServiceState, progressBar); + root.getChildren().addAll(logoAndVersion, applicationServiceState, progressBar); } @Override From 5729f68fb6a3af5e729a60c61ef2ae8b4d50d614 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Tue, 2 Apr 2024 16:47:41 +0700 Subject: [PATCH 110/119] Refactor: Fix incorrect MVC pattern usage. Controller does not call view directly but sets state in model and view binds to that model state. Remove Getter from inputField in view Add field in model and subscriber in view to handle caretPosition --- .../ChatMessageContainerController.java | 4 ++-- .../ChatMessageContainerModel.java | 1 + .../message_container/ChatMessageContainerView.java | 13 +++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java index fa13c4088f..88ef39b216 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java @@ -208,7 +208,7 @@ void onSendMessage(String text) { void onListUserNames(UserProfile user) { String content = model.getTextInput().get().replaceAll("@[a-zA-Z\\d]*$", "@" + user.getUserName() + " "); model.getTextInput().set(content); - view.getInputField().positionCaret(content.length()); //todo dont call on view + model.getCaretPosition().set(content.length()); } void onListChannels(ChatChannel chatChannel) { @@ -217,7 +217,7 @@ void onListChannels(ChatChannel chatChannel) { .orElse(""); String content = model.getTextInput().get().replaceAll("#[a-zA-Z\\d]*$", "#" + channelTitle + " "); model.getTextInput().set(content); - view.getInputField().positionCaret(content.length()); //todo dont call on view + model.getCaretPosition().set(content.length()); } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerModel.java index 7a4b9cccef..9ff2d22fec 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerModel.java @@ -27,6 +27,7 @@ public class ChatMessageContainerModel implements bisq.desktop.common.view.Model private final ChatService chatService; @Nullable private ChatMessage selectedChatMessage; + private final IntegerProperty caretPosition = new SimpleIntegerProperty(); public ChatMessageContainerModel(ChatChannelDomain chatChannelDomain, ChatService chatService) { this.chatChannelDomain = chatChannelDomain; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java index 878ff0f0cc..cda82c4493 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java @@ -21,7 +21,6 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; import javafx.util.StringConverter; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; @@ -30,15 +29,14 @@ public class ChatMessageContainerView extends bisq.desktop.common.view.View { private final static double CHAT_BOX_MAX_WIDTH = 1200; public final static String EDITED_POST_FIX = " " + Res.get("chat.message.wasEdited"); - @Getter - private final BisqTextArea inputField = new BisqTextArea(); //todo remove accessor + private final BisqTextArea inputField = new BisqTextArea(); private final Button sendButton = new Button(); private final Pane messagesListView; private final VBox emptyMessageList; private ChatMentionPopupMenu userMentionPopup; private ChatMentionPopupMenu> channelMentionPopup; private Pane userProfileSelectionRoot; - private Subscription focusInputTextFieldPin; + private Subscription focusInputTextFieldPin, caretPositionPin; public ChatMessageContainerView(ChatMessageContainerModel model, ChatMessageContainerController controller, @@ -76,6 +74,12 @@ protected void onViewAttached() { inputField.textProperty() )); + caretPositionPin = EasyBind.subscribe(model.getCaretPosition(), position -> { + if (position != null) { + inputField.positionCaret(position.intValue()); + } + }); + inputField.setOnKeyPressed(event -> { if (event.getCode() == KeyCode.ENTER) { event.consume(); @@ -112,6 +116,7 @@ protected void onViewDetached() { userMentionPopup.filterProperty().unbind(); channelMentionPopup.filterProperty().unbind(); focusInputTextFieldPin.unsubscribe(); + caretPositionPin.unsubscribe(); removeChatDialogEnabledSubscription(); inputField.setOnKeyPressed(null); From fed950646d9d29a08e077d61f71872da33fa8afa Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Tue, 2 Apr 2024 16:50:14 +0700 Subject: [PATCH 111/119] Remove unused code. We do not support channel selection via typing # --- .../ChatMessageContainerModel.java | 15 +++------------ .../ChatMessageContainerView.java | 12 ------------ 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerModel.java index 9ff2d22fec..64aefbf3ac 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerModel.java @@ -14,17 +14,14 @@ @Getter public class ChatMessageContainerModel implements bisq.desktop.common.view.Model { - private final BooleanProperty chatDialogEnabled = new SimpleBooleanProperty(true); - + private final ChatChannelDomain chatChannelDomain; + private final ChatService chatService; private final ObjectProperty> selectedChannel = new SimpleObjectProperty<>(); private final StringProperty textInput = new SimpleStringProperty(""); private final BooleanProperty userProfileSelectionVisible = new SimpleBooleanProperty(); private final ObjectProperty focusInputTextField = new SimpleObjectProperty<>(); private final ObservableList mentionableUsers = FXCollections.observableArrayList(); - // TODO mentionableChatChannels not filled with data - private final ObservableList> mentionableChatChannels = FXCollections.observableArrayList(); - private final ChatChannelDomain chatChannelDomain; - private final ChatService chatService; + private final BooleanProperty chatDialogEnabled = new SimpleBooleanProperty(true); @Nullable private ChatMessage selectedChatMessage; private final IntegerProperty caretPosition = new SimpleIntegerProperty(); @@ -34,12 +31,6 @@ public ChatMessageContainerModel(ChatChannelDomain chatChannelDomain, ChatServic this.chatService = chatService; } - String getChannelTitle(ChatChannel chatChannel) { - return chatService.findChatChannelService(chatChannel) - .map(service -> service.getChannelTitle(chatChannel)) - .orElse(""); - } - public void setSelectedChatMessage(@Nullable ChatMessage selectedChatMessage) { this.selectedChatMessage = selectedChatMessage; } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java index cda82c4493..6d68f3d5e3 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java @@ -1,6 +1,5 @@ package bisq.desktop.main.content.chat.message_container; -import bisq.chat.ChatChannel; import bisq.common.util.StringUtils; import bisq.desktop.common.utils.ImageUtil; import bisq.desktop.components.controls.BisqTextArea; @@ -34,7 +33,6 @@ public class ChatMessageContainerView extends bisq.desktop.common.view.View userMentionPopup; - private ChatMentionPopupMenu> channelMentionPopup; private Pane userProfileSelectionRoot; private Subscription focusInputTextFieldPin, caretPositionPin; @@ -69,10 +67,6 @@ protected void onViewAttached() { () -> StringUtils.deriveWordStartingWith(inputField.getText(), '@'), inputField.textProperty() )); - channelMentionPopup.filterProperty().bind(Bindings.createStringBinding( - () -> StringUtils.deriveWordStartingWith(inputField.getText(), '#'), - inputField.textProperty() - )); caretPositionPin = EasyBind.subscribe(model.getCaretPosition(), position -> { if (position != null) { @@ -97,7 +91,6 @@ protected void onViewAttached() { }); userMentionPopup.setItems(model.getMentionableUsers()); - channelMentionPopup.setItems(model.getMentionableChatChannels()); createChatDialogEnabledSubscription(); @@ -114,7 +107,6 @@ protected void onViewDetached() { userProfileSelectionRoot.managedProperty().unbind(); inputField.textProperty().unbindBidirectional(model.getTextInput()); userMentionPopup.filterProperty().unbind(); - channelMentionPopup.filterProperty().unbind(); focusInputTextFieldPin.unsubscribe(); caretPositionPin.unsubscribe(); removeChatDialogEnabledSubscription(); @@ -164,10 +156,6 @@ private void setUpInputFieldAtMentions() { userMentionPopup = new ChatMentionPopupMenu<>(inputField); userMentionPopup.setItemDisplayConverter(UserProfile::getUserName); userMentionPopup.setSelectionHandler(controller::onListUserNames); - - channelMentionPopup = new ChatMentionPopupMenu<>(inputField); - channelMentionPopup.setItemDisplayConverter(model::getChannelTitle); - channelMentionPopup.setSelectionHandler(controller::onListChannels); } private void setUpUserProfileSelection(UserProfileSelection userProfileSelection) { From bd7197041af2cf58e09c704ff3188165916981b2 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Tue, 2 Apr 2024 16:51:58 +0700 Subject: [PATCH 112/119] Cleanups Use Setter annotation, remove unused field --- .../ChatMessageContainerController.java | 2 +- .../ChatMessageContainerModel.java | 13 ++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java index 88ef39b216..c11f5cd86e 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java @@ -66,7 +66,7 @@ public ChatMessageContainerController(ServiceProvider serviceProvider, this::replyHandler, chatChannelDomain); - model = new ChatMessageContainerModel(chatChannelDomain, chatService); + model = new ChatMessageContainerModel(chatChannelDomain); view = new ChatMessageContainerView(model, this, chatMessagesListController.getView().getRoot(), citationBlock.getRoot(), diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerModel.java index 64aefbf3ac..e050fcf9d6 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerModel.java @@ -3,35 +3,30 @@ import bisq.chat.ChatChannel; import bisq.chat.ChatChannelDomain; import bisq.chat.ChatMessage; -import bisq.chat.ChatService; import bisq.user.profile.UserProfile; import javafx.beans.property.*; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import lombok.Getter; +import lombok.Setter; import javax.annotation.Nullable; @Getter public class ChatMessageContainerModel implements bisq.desktop.common.view.Model { private final ChatChannelDomain chatChannelDomain; - private final ChatService chatService; private final ObjectProperty> selectedChannel = new SimpleObjectProperty<>(); private final StringProperty textInput = new SimpleStringProperty(""); private final BooleanProperty userProfileSelectionVisible = new SimpleBooleanProperty(); private final ObjectProperty focusInputTextField = new SimpleObjectProperty<>(); private final ObservableList mentionableUsers = FXCollections.observableArrayList(); private final BooleanProperty chatDialogEnabled = new SimpleBooleanProperty(true); + private final IntegerProperty caretPosition = new SimpleIntegerProperty(); @Nullable + @Setter private ChatMessage selectedChatMessage; - private final IntegerProperty caretPosition = new SimpleIntegerProperty(); - public ChatMessageContainerModel(ChatChannelDomain chatChannelDomain, ChatService chatService) { + public ChatMessageContainerModel(ChatChannelDomain chatChannelDomain) { this.chatChannelDomain = chatChannelDomain; - this.chatService = chatService; - } - - public void setSelectedChatMessage(@Nullable ChatMessage selectedChatMessage) { - this.selectedChatMessage = selectedChatMessage; } } From b11cb4c418f369d2bca52996ab1616838cd345a9 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Tue, 2 Apr 2024 16:54:31 +0700 Subject: [PATCH 113/119] Refactor: Move component classes only used by ChatMessageContainer classes into same package --- .../chat/message_container/ChatMessageContainerController.java | 2 +- .../chat/message_container/ChatMessageContainerView.java | 2 +- .../message_container}/components/ChatMentionPopupMenu.java | 2 +- .../{ => chat/message_container}/components/CitationBlock.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/{ => chat/message_container}/components/ChatMentionPopupMenu.java (96%) rename apps/desktop/desktop/src/main/java/bisq/desktop/main/content/{ => chat/message_container}/components/CitationBlock.java (99%) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java index c11f5cd86e..6a249fb408 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java @@ -12,9 +12,9 @@ import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.view.Navigation; import bisq.desktop.components.overlay.Popup; +import bisq.desktop.main.content.chat.message_container.components.CitationBlock; import bisq.desktop.main.content.chat.message_container.list.ChatMessageListItem; import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListController; -import bisq.desktop.main.content.components.CitationBlock; import bisq.desktop.main.content.components.UserProfileSelection; import bisq.i18n.Res; import bisq.settings.SettingsService; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java index 6d68f3d5e3..9b5996b65f 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java @@ -5,7 +5,7 @@ import bisq.desktop.components.controls.BisqTextArea; import bisq.desktop.components.controls.BisqTooltip; import bisq.desktop.main.content.chat.ChatUtil; -import bisq.desktop.main.content.components.ChatMentionPopupMenu; +import bisq.desktop.main.content.chat.message_container.components.ChatMentionPopupMenu; import bisq.desktop.main.content.components.UserProfileSelection; import bisq.i18n.Res; import bisq.user.profile.UserProfile; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/ChatMentionPopupMenu.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/components/ChatMentionPopupMenu.java similarity index 96% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/ChatMentionPopupMenu.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/components/ChatMentionPopupMenu.java index d3da1be4e6..6c487b9ee1 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/ChatMentionPopupMenu.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/components/ChatMentionPopupMenu.java @@ -1,4 +1,4 @@ -package bisq.desktop.main.content.components; +package bisq.desktop.main.content.chat.message_container.components; import bisq.common.util.StringUtils; import bisq.desktop.components.controls.BisqPopup; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/CitationBlock.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/components/CitationBlock.java similarity index 99% rename from apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/CitationBlock.java rename to apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/components/CitationBlock.java index e5d26e8c36..276710019d 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/components/CitationBlock.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/components/CitationBlock.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop.main.content.components; +package bisq.desktop.main.content.chat.message_container.components; import bisq.chat.ChatMessage; import bisq.chat.ChatService; From 67a6eff4c1c1d912246aeb661101244fc989b2f1 Mon Sep 17 00:00:00 2001 From: axpoems <145597137+axpoems@users.noreply.github.com> Date: Tue, 2 Apr 2024 22:07:51 +0200 Subject: [PATCH 114/119] Fix alignment of direction box in create offer wizard --- .../trade_wizard/direction/TradeWizardDirectionView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/direction/TradeWizardDirectionView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/direction/TradeWizardDirectionView.java index 76c001d144..53442740e3 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/direction/TradeWizardDirectionView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/direction/TradeWizardDirectionView.java @@ -73,7 +73,7 @@ public TradeWizardDirectionView(TradeWizardDirectionModel model, TradeWizardDire sellButton = sellPair.getSecond(); HBox directionBox = new HBox(25, buyBox, sellBox); - directionBox.setAlignment(Pos.CENTER); + directionBox.setAlignment(Pos.BASELINE_CENTER); VBox.setMargin(headlineLabel, new Insets(-20, 0, 0, 0)); VBox.setMargin(directionBox, new Insets(10, 0, 0, 0)); From eb1ff977d0673e59b976c35b7f1761ba5274d7a2 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 3 Apr 2024 13:17:01 +0700 Subject: [PATCH 115/119] Use ListView in ChatMentionPopupMenu Limit height Add sorting Var. refactorings --- .../components/controls/BisqPopup.java | 10 +- .../ChatMessageContainerController.java | 11 +- .../ChatMessageContainerView.java | 21 ++- .../components/ChatMentionPopupMenu.java | 149 ++++++++++++------ .../desktop/src/main/resources/css/chat.css | 18 ++- 5 files changed, 130 insertions(+), 79 deletions(-) diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/controls/BisqPopup.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/controls/BisqPopup.java index a07f48dc4b..081d6b0b8a 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/controls/BisqPopup.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/components/controls/BisqPopup.java @@ -27,6 +27,10 @@ import lombok.Setter; public class BisqPopup extends PopupControl { + public enum Alignment { + LEFT, RIGHT + } + @Getter private final StackPane root = new StackPane(); @@ -43,7 +47,7 @@ public BisqPopup() { setAutoHide(true); } - public final void show(Node owner) { + public void show(Node owner) { Bounds bounds = owner.localToScreen(owner.getBoundsInLocal()); double anchorX = 0; if (alignment == Alignment.RIGHT) { @@ -56,10 +60,6 @@ public final void show(Node owner) { super.show(owner, anchorX, bounds.getMinY()); } - public enum Alignment { - LEFT, RIGHT - } - @Override protected Skin createDefaultSkin() { return new BisqPopupSkin(this); diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java index 6a249fb408..073b66599d 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerController.java @@ -205,21 +205,12 @@ void onSendMessage(String text) { doSendMessage(text); } - void onListUserNames(UserProfile user) { + void onUserProfileSelected(UserProfile user) { String content = model.getTextInput().get().replaceAll("@[a-zA-Z\\d]*$", "@" + user.getUserName() + " "); model.getTextInput().set(content); model.getCaretPosition().set(content.length()); } - void onListChannels(ChatChannel chatChannel) { - String channelTitle = chatService.findChatChannelService(chatChannel) - .map(service -> service.getChannelTitle(chatChannel)) - .orElse(""); - String content = model.getTextInput().get().replaceAll("#[a-zA-Z\\d]*$", "#" + channelTitle + " "); - model.getTextInput().set(content); - model.getCaretPosition().set(content.length()); - } - /////////////////////////////////////////////////////////////////////////////////////////////////// // Private diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java index 9b5996b65f..6e43cab967 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/ChatMessageContainerView.java @@ -8,8 +8,6 @@ import bisq.desktop.main.content.chat.message_container.components.ChatMentionPopupMenu; import bisq.desktop.main.content.components.UserProfileSelection; import bisq.i18n.Res; -import bisq.user.profile.UserProfile; -import javafx.beans.binding.Bindings; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button; @@ -24,6 +22,8 @@ import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; +import java.util.stream.Collectors; + @Slf4j public class ChatMessageContainerView extends bisq.desktop.common.view.View { private final static double CHAT_BOX_MAX_WIDTH = 1200; @@ -32,7 +32,7 @@ public class ChatMessageContainerView extends bisq.desktop.common.view.View userMentionPopup; + private ChatMentionPopupMenu userMentionPopup; private Pane userProfileSelectionRoot; private Subscription focusInputTextFieldPin, caretPositionPin; @@ -63,10 +63,6 @@ protected void onViewAttached() { userProfileSelectionRoot.visibleProperty().bind(model.getUserProfileSelectionVisible()); userProfileSelectionRoot.managedProperty().bind(model.getUserProfileSelectionVisible()); inputField.textProperty().bindBidirectional(model.getTextInput()); - userMentionPopup.filterProperty().bind(Bindings.createStringBinding( - () -> StringUtils.deriveWordStartingWith(inputField.getText(), '@'), - inputField.textProperty() - )); caretPositionPin = EasyBind.subscribe(model.getCaretPosition(), position -> { if (position != null) { @@ -90,7 +86,9 @@ protected void onViewAttached() { inputField.clear(); }); - userMentionPopup.setItems(model.getMentionableUsers()); + userMentionPopup.getObservableList().setAll(model.getMentionableUsers().stream() + .map(ChatMentionPopupMenu.ListItem::new) + .collect(Collectors.toList())); createChatDialogEnabledSubscription(); @@ -99,6 +97,7 @@ protected void onViewAttached() { inputField.requestFocus(); } }); + userMentionPopup.init(); } @Override @@ -106,13 +105,13 @@ protected void onViewDetached() { userProfileSelectionRoot.visibleProperty().unbind(); userProfileSelectionRoot.managedProperty().unbind(); inputField.textProperty().unbindBidirectional(model.getTextInput()); - userMentionPopup.filterProperty().unbind(); focusInputTextFieldPin.unsubscribe(); caretPositionPin.unsubscribe(); removeChatDialogEnabledSubscription(); inputField.setOnKeyPressed(null); sendButton.setOnAction(null); + userMentionPopup.cleanup(); } private VBox createAndGetBottomBar(UserProfileSelection userProfileSelection) { @@ -153,9 +152,7 @@ private HBox createAndGetSendMessageBox() { } private void setUpInputFieldAtMentions() { - userMentionPopup = new ChatMentionPopupMenu<>(inputField); - userMentionPopup.setItemDisplayConverter(UserProfile::getUserName); - userMentionPopup.setSelectionHandler(controller::onListUserNames); + userMentionPopup = new ChatMentionPopupMenu(inputField, controller::onUserProfileSelected); } private void setUpUserProfileSelection(UserProfileSelection userProfileSelection) { diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/components/ChatMentionPopupMenu.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/components/ChatMentionPopupMenu.java index 6c487b9ee1..4148dfa37f 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/components/ChatMentionPopupMenu.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/components/ChatMentionPopupMenu.java @@ -2,73 +2,128 @@ import bisq.common.util.StringUtils; import bisq.desktop.components.controls.BisqPopup; +import bisq.desktop.components.controls.BisqTextArea; +import bisq.user.profile.UserProfile; +import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; -import javafx.scene.Node; +import javafx.beans.value.ChangeListener; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import javafx.collections.transformation.SortedList; import javafx.scene.control.Button; -import javafx.scene.layout.VBox; -import lombok.Setter; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.util.Callback; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; -import java.util.Collection; -import java.util.List; import java.util.function.Consumer; -import java.util.stream.Collectors; -public class ChatMentionPopupMenu extends BisqPopup { - @Setter - private ToStringConverter itemDisplayConverter; +@Slf4j +public class ChatMentionPopupMenu extends BisqPopup { + private final BisqTextArea inputField; + private final Consumer userProfileSelectedHandler; + private final StringProperty filter = new SimpleStringProperty(); + @Getter + private final ObservableList observableList = FXCollections.observableArrayList(); + private final FilteredList filteredList = new FilteredList<>(observableList); + private final SortedList sortedList = new SortedList<>(filteredList); + private final ListView listView = new ListView<>(sortedList); + private final ChangeListener filterChangeListener; - @Setter - private Consumer selectionHandler; + public ChatMentionPopupMenu(BisqTextArea inputField, Consumer userProfileSelectedHandler) { + super(); + this.inputField = inputField; + this.userProfileSelectedHandler = userProfileSelectedHandler; - @Setter - private Collection items; + sortedList.setComparator(ListItem::compareTo); + listView.getStyleClass().add("chat-mention-list-view"); + listView.setPrefWidth(600); + listView.setCellFactory(getCellFactory()); - private final StringProperty filter = new SimpleStringProperty(null); - - public ChatMentionPopupMenu(Node owner) { - getStyleClass().add("chat-mention-popup-menu"); setAlignment(Alignment.LEFT); - setContentNode(new VBox()); - - filterProperty().addListener((ev, prev, current) -> { - if (current != null) { - updateItems(current); + setContentNode(listView); - if (prev == null) { - show(owner); + filterChangeListener = (observableValue, oldValue, newValue) -> { + if (newValue != null) { + filteredList.setPredicate(item -> item.matchUserName(newValue)); + listView.setPrefHeight(Math.min(600, 20 + filteredList.size() * ListItem.CELL_HEIGHT)); + if (oldValue == null) { + show(inputField); } } else { hide(); } - }); + }; } - public StringProperty filterProperty() { - return filter; + public void init() { + filter.addListener(filterChangeListener); + filter.bind(Bindings.createStringBinding( + () -> StringUtils.deriveWordStartingWith(inputField.getText(), '@'), + inputField.textProperty())); } - private void updateItems(String filter) { - List