Compare commits

...

13 commits

Author SHA1 Message Date
woodser
f053a274a4 bump version to 1.0.17
Some checks are pending
CI / build (macos-13) (push) Waiting to run
CI / build (ubuntu-22.04) (push) Waiting to run
CI / build (windows-latest) (push) Waiting to run
Codacy Coverage Reporter / Publish coverage (push) Waiting to run
CodeQL / Analyze (push) Waiting to run
2024-12-21 09:19:18 -05:00
woodser
fdee044023 fix occasional miscolored buttons to remove or edit my offer 2024-12-21 09:17:06 -05:00
woodser
42ede83ca2 'show all' resets default currency to create new offer 2024-12-21 09:16:57 -05:00
woodser
5444d96832 reverse order of funds > confirmations and memo columns 2024-12-21 09:16:48 -05:00
woodser
7340ca9c21 allow scheduling funds from split output tx 2024-12-21 09:16:40 -05:00
woodser
542441d9d2 increase contrast of filter toggles and remove bottom highlight 2024-12-21 09:16:28 -05:00
woodser
c5ef60ce5c fix ui to set security deposit pct w/o deposit 2024-12-21 09:16:28 -05:00
woodser
389c5dddac fix no deposit filter applied to sell tab 2024-12-21 09:16:28 -05:00
woodser
7240b5f222 document changing download url for network deployment 2024-12-21 08:45:16 -05:00
woodser
34e0c4b71f remove bitcoin donation address from readme 2024-12-20 09:36:57 -05:00
woodser
aab4d0207e update links to typescript client and tests 2024-12-20 06:43:14 -05:00
woodser
1a51b171a0 bump version to 1.0.16 2024-12-19 16:21:44 -05:00
woodser
a557d90e5d fix password prompt on startup by referencing lock@2x.png 2024-12-19 16:16:22 -05:00
17 changed files with 114 additions and 92 deletions

View file

@ -73,18 +73,9 @@ To incentivize development and reward contributors, we adopt a simple bounty sys
To bring Haveno to life, we need resources. If you have the possibility, please consider [becoming a sponsor](https://haveno.exchange/sponsors/) or donating to the project: To bring Haveno to life, we need resources. If you have the possibility, please consider [becoming a sponsor](https://haveno.exchange/sponsors/) or donating to the project:
### Monero
<p> <p>
<img src="https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_monero.png" alt="Donate Monero" width="115" height="115"><br> <img src="https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_monero.png" alt="Donate Monero" width="115" height="115"><br>
<code>42sjokkT9FmiWPqVzrWPFE5NCJXwt96bkBozHf4vgLR9hXyJDqKHEHKVscAARuD7in5wV1meEcSTJTanCTDzidTe2cFXS1F</code> <code>42sjokkT9FmiWPqVzrWPFE5NCJXwt96bkBozHf4vgLR9hXyJDqKHEHKVscAARuD7in5wV1meEcSTJTanCTDzidTe2cFXS1F</code>
</p> </p>
If you are using a wallet that supports OpenAlias (like the 'official' CLI and GUI wallets), you can simply put `fund@haveno.exchange` as the "receiver" address. If you are using a wallet that supports OpenAlias (like the 'official' CLI and GUI wallets), you can simply put `fund@haveno.exchange` as the "receiver" address.
### Bitcoin
<p>
<img src="https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_bitcoin.png" alt="Donate Bitcoin" width="115" height="115"><br>
<code>1AKq3CE1yBAnxGmHXbNFfNYStcByNDc5gQ</code>
</p>

View file

@ -610,7 +610,7 @@ configure(project(':desktop')) {
apply plugin: 'com.github.johnrengelman.shadow' apply plugin: 'com.github.johnrengelman.shadow'
apply from: 'package/package.gradle' apply from: 'package/package.gradle'
version = '1.0.15-SNAPSHOT' version = '1.0.17-SNAPSHOT'
jar.manifest.attributes( jar.manifest.attributes(
"Implementation-Title": project.name, "Implementation-Title": project.name,

View file

@ -28,7 +28,7 @@ import static com.google.common.base.Preconditions.checkArgument;
public class Version { public class Version {
// The application versions // The application versions
// We use semantic versioning with major, minor and patch // We use semantic versioning with major, minor and patch
public static final String VERSION = "1.0.15"; public static final String VERSION = "1.0.17";
/** /**
* Holds a list of the tagged resource files for optimizing the getData requests. * Holds a list of the tagged resource files for optimizing the getData requests.

View file

@ -115,7 +115,6 @@ import lombok.Getter;
import monero.common.MoneroRpcConnection; import monero.common.MoneroRpcConnection;
import monero.daemon.model.MoneroKeyImageSpentStatus; import monero.daemon.model.MoneroKeyImageSpentStatus;
import monero.daemon.model.MoneroTx; import monero.daemon.model.MoneroTx;
import monero.wallet.model.MoneroIncomingTransfer;
import monero.wallet.model.MoneroOutputQuery; import monero.wallet.model.MoneroOutputQuery;
import monero.wallet.model.MoneroOutputWallet; import monero.wallet.model.MoneroOutputWallet;
import monero.wallet.model.MoneroTransferQuery; import monero.wallet.model.MoneroTransferQuery;
@ -1159,23 +1158,17 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
private void scheduleWithEarliestTxs(List<OpenOffer> openOffers, OpenOffer openOffer) { private void scheduleWithEarliestTxs(List<OpenOffer> openOffers, OpenOffer openOffer) {
// check for sufficient balance - scheduled offers amount
BigInteger offerReserveAmount = openOffer.getOffer().getAmountNeeded();
if (xmrWalletService.getBalance().subtract(getScheduledAmount(openOffers)).compareTo(offerReserveAmount) < 0) {
throw new RuntimeException("Not enough money in Haveno wallet");
}
// get earliest available or pending txs with sufficient spendable amount // get earliest available or pending txs with sufficient spendable amount
BigInteger offerReserveAmount = openOffer.getOffer().getAmountNeeded();
BigInteger scheduledAmount = BigInteger.ZERO; BigInteger scheduledAmount = BigInteger.ZERO;
Set<MoneroTxWallet> scheduledTxs = new HashSet<MoneroTxWallet>(); Set<MoneroTxWallet> scheduledTxs = new HashSet<MoneroTxWallet>();
for (MoneroTxWallet tx : xmrWalletService.getTxs()) { for (MoneroTxWallet tx : xmrWalletService.getTxs()) {
// get spendable amount // get unscheduled spendable amount
BigInteger spendableAmount = getSpendableAmount(tx); BigInteger spendableAmount = getUnscheduledSpendableAmount(tx, openOffers);
// skip if no spendable amount or already scheduled // skip if no spendable amount
if (spendableAmount.equals(BigInteger.ZERO)) continue; if (spendableAmount.equals(BigInteger.ZERO)) continue;
if (isTxScheduledByOtherOffer(openOffers, openOffer, tx.getHash())) continue;
// schedule tx // schedule tx
scheduledAmount = scheduledAmount.add(spendableAmount); scheduledAmount = scheduledAmount.add(spendableAmount);
@ -1184,7 +1177,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
// break if sufficient funds // break if sufficient funds
if (scheduledAmount.compareTo(offerReserveAmount) >= 0) break; if (scheduledAmount.compareTo(offerReserveAmount) >= 0) break;
} }
if (scheduledAmount.compareTo(offerReserveAmount) < 0) throw new RuntimeException("Not enough funds to schedule offer"); if (scheduledAmount.compareTo(offerReserveAmount) < 0) throw new RuntimeException("Not enough funds to create offer");
// schedule txs // schedule txs
openOffer.setScheduledTxHashes(scheduledTxs.stream().map(tx -> tx.getHash()).collect(Collectors.toList())); openOffer.setScheduledTxHashes(scheduledTxs.stream().map(tx -> tx.getHash()).collect(Collectors.toList()));
@ -1192,6 +1185,30 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
openOffer.setState(OpenOffer.State.PENDING); openOffer.setState(OpenOffer.State.PENDING);
} }
private BigInteger getUnscheduledSpendableAmount(MoneroTxWallet tx, List<OpenOffer> openOffers) {
if (isScheduledWithUnknownAmount(tx, openOffers)) return BigInteger.ZERO;
return getSpendableAmount(tx).subtract(getSplitAmount(tx, openOffers)).max(BigInteger.ZERO);
}
private boolean isScheduledWithUnknownAmount(MoneroTxWallet tx, List<OpenOffer> openOffers) {
for (OpenOffer openOffer : openOffers) {
if (openOffer.getScheduledTxHashes() == null) continue;
if (openOffer.getScheduledTxHashes().contains(tx.getHash()) && !tx.getHash().equals(openOffer.getSplitOutputTxHash())) {
return true;
}
}
return false;
}
private BigInteger getSplitAmount(MoneroTxWallet tx, List<OpenOffer> openOffers) {
for (OpenOffer openOffer : openOffers) {
if (openOffer.getSplitOutputTxHash() == null) continue;
if (!openOffer.getSplitOutputTxHash().equals(tx.getHash())) continue;
return openOffer.getOffer().getAmountNeeded();
}
return BigInteger.ZERO;
}
private BigInteger getSpendableAmount(MoneroTxWallet tx) { private BigInteger getSpendableAmount(MoneroTxWallet tx) {
// compute spendable amount from outputs if confirmed // compute spendable amount from outputs if confirmed
@ -1220,23 +1237,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
return getSpendableAmount(tx).compareTo(BigInteger.ZERO) > 0; return getSpendableAmount(tx).compareTo(BigInteger.ZERO) > 0;
} }
private BigInteger getScheduledAmount(List<OpenOffer> openOffers) {
BigInteger scheduledAmount = BigInteger.ZERO;
for (OpenOffer openOffer : openOffers) {
if (openOffer.getState() != OpenOffer.State.PENDING) continue;
if (openOffer.getScheduledTxHashes() == null) continue;
List<MoneroTxWallet> fundingTxs = xmrWalletService.getTxs(openOffer.getScheduledTxHashes());
for (MoneroTxWallet fundingTx : fundingTxs) {
if (fundingTx.getIncomingTransfers() != null) {
for (MoneroIncomingTransfer transfer : fundingTx.getIncomingTransfers()) {
if (transfer.getAccountIndex() == 0) scheduledAmount = scheduledAmount.add(transfer.getAmount());
}
}
}
}
return scheduledAmount;
}
private boolean isTxScheduledByOtherOffer(List<OpenOffer> openOffers, OpenOffer openOffer, String txHash) { private boolean isTxScheduledByOtherOffer(List<OpenOffer> openOffers, OpenOffer openOffer, String txHash) {
for (OpenOffer otherOffer : openOffers) { for (OpenOffer otherOffer : openOffers) {
if (otherOffer == openOffer) continue; if (otherOffer == openOffer) continue;

View file

@ -60,6 +60,6 @@
</content_rating> </content_rating>
<releases> <releases>
<release version="1.0.15" date="2024-12-19"/> <release version="1.0.17" date="2024-12-21"/>
</releases> </releases>
</component> </component>

View file

@ -5,10 +5,10 @@
<!-- See: https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html --> <!-- See: https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -->
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0.15</string> <string>1.0.17</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0.15</string> <string>1.0.17</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>Haveno</string> <string>Haveno</string>

View file

@ -200,7 +200,7 @@ public class HavenoAppMain extends HavenoExecutable {
// Add an icon to the dialog // Add an icon to the dialog
Stage stage = (Stage) getDialogPane().getScene().getWindow(); Stage stage = (Stage) getDialogPane().getScene().getWindow();
stage.getIcons().add(ImageUtil.getImageByPath("lock.png")); stage.getIcons().add(ImageUtil.getImageByPath("lock@2x.png"));
// Create the password field // Create the password field
PasswordField passwordField = new PasswordField(); PasswordField passwordField = new PasswordField();

View file

@ -38,8 +38,8 @@
<TableColumn fx:id="transactionColumn" minWidth="180"/> <TableColumn fx:id="transactionColumn" minWidth="180"/>
<TableColumn fx:id="amountColumn" minWidth="110" maxWidth="110"/> <TableColumn fx:id="amountColumn" minWidth="110" maxWidth="110"/>
<TableColumn fx:id="txFeeColumn" minWidth="110" maxWidth="110"/> <TableColumn fx:id="txFeeColumn" minWidth="110" maxWidth="110"/>
<TableColumn fx:id="confidenceColumn" minWidth="60" maxWidth="130"/>
<TableColumn fx:id="memoColumn" minWidth="40"/> <TableColumn fx:id="memoColumn" minWidth="40"/>
<TableColumn fx:id="confidenceColumn" minWidth="120" maxWidth="130"/>
<TableColumn fx:id="revertTxColumn" sortable="false" minWidth="110" maxWidth="110" visible="false"/> <TableColumn fx:id="revertTxColumn" sortable="false" minWidth="110" maxWidth="110" visible="false"/>
</columns> </columns>
</TableView> </TableView>

View file

@ -70,7 +70,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
@FXML @FXML
TableView<TransactionsListItem> tableView; TableView<TransactionsListItem> tableView;
@FXML @FXML
TableColumn<TransactionsListItem, TransactionsListItem> dateColumn, detailsColumn, addressColumn, transactionColumn, amountColumn, txFeeColumn, memoColumn, confidenceColumn, revertTxColumn; TableColumn<TransactionsListItem, TransactionsListItem> dateColumn, detailsColumn, addressColumn, transactionColumn, amountColumn, txFeeColumn, confidenceColumn, memoColumn, revertTxColumn;
@FXML @FXML
Label numItems; Label numItems;
@FXML @FXML
@ -133,8 +133,8 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
transactionColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.txId", Res.getBaseCurrencyCode()))); transactionColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.txId", Res.getBaseCurrencyCode())));
amountColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode()))); amountColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode())));
txFeeColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.txFee", Res.getBaseCurrencyCode()))); txFeeColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.txFee", Res.getBaseCurrencyCode())));
memoColumn.setGraphic(new AutoTooltipLabel(Res.get("funds.tx.memo")));
confidenceColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.confirmations", Res.getBaseCurrencyCode()))); confidenceColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.confirmations", Res.getBaseCurrencyCode())));
memoColumn.setGraphic(new AutoTooltipLabel(Res.get("funds.tx.memo")));
revertTxColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.revert", Res.getBaseCurrencyCode()))); revertTxColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.revert", Res.getBaseCurrencyCode())));
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_FLEX_LAST_COLUMN); tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_FLEX_LAST_COLUMN);
@ -146,8 +146,8 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
setTransactionColumnCellFactory(); setTransactionColumnCellFactory();
setAmountColumnCellFactory(); setAmountColumnCellFactory();
setTxFeeColumnCellFactory(); setTxFeeColumnCellFactory();
setMemoColumnCellFactory();
setConfidenceColumnCellFactory(); setConfidenceColumnCellFactory();
setMemoColumnCellFactory();
setRevertTxColumnCellFactory(); setRevertTxColumnCellFactory();
dateColumn.setComparator(Comparator.comparing(TransactionsListItem::getDate)); dateColumn.setComparator(Comparator.comparing(TransactionsListItem::getDate));
@ -221,8 +221,8 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
columns[3] = item.getTxId(); columns[3] = item.getTxId();
columns[4] = item.getAmountStr(); columns[4] = item.getAmountStr();
columns[5] = item.getTxFeeStr(); columns[5] = item.getTxFeeStr();
columns[6] = item.getMemo() == null ? "" : item.getMemo(); columns[6] = String.valueOf(item.getNumConfirmations());
columns[7] = String.valueOf(item.getNumConfirmations()); columns[7] = item.getMemo() == null ? "" : item.getMemo();
return columns; return columns;
}; };

View file

@ -56,6 +56,7 @@ import haveno.core.util.coin.CoinUtil;
import haveno.core.util.validation.AmountValidator4Decimals; import haveno.core.util.validation.AmountValidator4Decimals;
import haveno.core.util.validation.AmountValidator8Decimals; import haveno.core.util.validation.AmountValidator8Decimals;
import haveno.core.util.validation.InputValidator; import haveno.core.util.validation.InputValidator;
import haveno.core.util.validation.InputValidator.ValidationResult;
import haveno.core.util.validation.MonetaryValidator; import haveno.core.util.validation.MonetaryValidator;
import haveno.core.xmr.wallet.Restrictions; import haveno.core.xmr.wallet.Restrictions;
import haveno.desktop.Navigation; import haveno.desktop.Navigation;
@ -490,6 +491,8 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
xmrValidator.setMaxTradeLimit(BigInteger.valueOf(dataModel.getMaxTradeLimit())); xmrValidator.setMaxTradeLimit(BigInteger.valueOf(dataModel.getMaxTradeLimit()));
if (amount.get() != null) amountValidationResult.set(isXmrInputValid(amount.get())); if (amount.get() != null) amountValidationResult.set(isXmrInputValid(amount.get()));
updateSecurityDeposit(); updateSecurityDeposit();
setSecurityDepositToModel();
onFocusOutSecurityDepositTextField(true, false); // refresh security deposit field
applyMakerFee(); applyMakerFee();
dataModel.calculateTotalToPay(); dataModel.calculateTotalToPay();
updateButtonDisableState(); updateButtonDisableState();
@ -769,7 +772,8 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
} }
} }
} }
// We want to trigger a recalculation of the volume
// trigger recalculation of the volume
UserThread.execute(() -> { UserThread.execute(() -> {
onFocusOutVolumeTextField(true, false); onFocusOutVolumeTextField(true, false);
onFocusOutMinAmountTextField(true, false); onFocusOutMinAmountTextField(true, false);
@ -815,6 +819,11 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
} }
maybeShowMakeOfferToUnsignedAccountWarning(); maybeShowMakeOfferToUnsignedAccountWarning();
// trigger recalculation of the security deposit
UserThread.execute(() -> {
onFocusOutSecurityDepositTextField(true, false);
});
} }
} }
@ -944,11 +953,16 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
if (marketPriceMargin.get() == null && amount.get() != null && volume.get() != null) { if (marketPriceMargin.get() == null && amount.get() != null && volume.get() != null) {
updateMarketPriceToManual(); updateMarketPriceToManual();
} }
// trigger recalculation of security deposit
UserThread.execute(() -> {
onFocusOutSecurityDepositTextField(true, false);
});
} }
} }
void onFocusOutSecurityDepositTextField(boolean oldValue, boolean newValue) { void onFocusOutSecurityDepositTextField(boolean oldValue, boolean newValue) {
if (oldValue && !newValue) { if (oldValue && !newValue && !isMinSecurityDeposit.get()) {
InputValidator.ValidationResult result = securityDepositValidator.validate(securityDeposit.get()); InputValidator.ValidationResult result = securityDepositValidator.validate(securityDeposit.get());
securityDepositValidationResult.set(result); securityDepositValidationResult.set(result);
if (result.isValid) { if (result.isValid) {
@ -1040,6 +1054,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
public String getSecurityDepositLabel() { public String getSecurityDepositLabel() {
return dataModel.buyerAsTakerWithoutDeposit.get() && dataModel.isSellOffer() ? Res.get("createOffer.myDeposit") : return dataModel.buyerAsTakerWithoutDeposit.get() && dataModel.isSellOffer() ? Res.get("createOffer.myDeposit") :
dataModel.isMinSecurityDeposit() ? Res.get("createOffer.minSecurityDepositUsed") :
Preferences.USE_SYMMETRIC_SECURITY_DEPOSIT ? Res.get("createOffer.setDepositForBothTraders") : Preferences.USE_SYMMETRIC_SECURITY_DEPOSIT ? Res.get("createOffer.setDepositForBothTraders") :
dataModel.isBuyOffer() ? Res.get("createOffer.setDepositAsBuyer") : Res.get("createOffer.setDeposit"); dataModel.isBuyOffer() ? Res.get("createOffer.setDepositAsBuyer") : Res.get("createOffer.setDeposit");
} }
@ -1211,7 +1226,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
} }
private void setSecurityDepositToModel() { private void setSecurityDepositToModel() {
if (!(dataModel.buyerAsTakerWithoutDeposit.get() && dataModel.isSellOffer()) && securityDeposit.get() != null && !securityDeposit.get().isEmpty()) { if (securityDeposit.get() != null && !securityDeposit.get().isEmpty() && !isMinSecurityDeposit.get()) {
dataModel.setSecurityDepositPct(ParsingUtils.parsePercentStringToDouble(securityDeposit.get())); dataModel.setSecurityDepositPct(ParsingUtils.parsePercentStringToDouble(securityDeposit.get()));
} else { } else {
dataModel.setSecurityDepositPct(Restrictions.getDefaultSecurityDepositAsPercent()); dataModel.setSecurityDepositPct(Restrictions.getDefaultSecurityDepositAsPercent());
@ -1282,11 +1297,11 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
private void updateSecurityDeposit() { private void updateSecurityDeposit() {
isMinSecurityDeposit.set(dataModel.isMinSecurityDeposit()); isMinSecurityDeposit.set(dataModel.isMinSecurityDeposit());
securityDepositLabel.set(getSecurityDepositLabel());
if (dataModel.isMinSecurityDeposit()) { if (dataModel.isMinSecurityDeposit()) {
securityDepositLabel.set(Res.get("createOffer.minSecurityDepositUsed"));
securityDeposit.set(HavenoUtils.formatXmr(Restrictions.getMinSecurityDeposit())); securityDeposit.set(HavenoUtils.formatXmr(Restrictions.getMinSecurityDeposit()));
securityDepositValidationResult.set(new ValidationResult(true));
} else { } else {
securityDepositLabel.set(getSecurityDepositLabel());
boolean hasBuyerAsTakerWithoutDeposit = dataModel.buyerAsTakerWithoutDeposit.get() && dataModel.isSellOffer(); boolean hasBuyerAsTakerWithoutDeposit = dataModel.buyerAsTakerWithoutDeposit.get() && dataModel.isSellOffer();
securityDeposit.set(FormattingUtils.formatToPercent(hasBuyerAsTakerWithoutDeposit ? securityDeposit.set(FormattingUtils.formatToPercent(hasBuyerAsTakerWithoutDeposit ?
Restrictions.getDefaultSecurityDepositAsPercent() : // use default percent if no deposit from buyer Restrictions.getDefaultSecurityDepositAsPercent() : // use default percent if no deposit from buyer

View file

@ -185,7 +185,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
paymentMethodComboBox.setCellFactory(GUIUtil.getPaymentMethodCellFactory()); paymentMethodComboBox.setCellFactory(GUIUtil.getPaymentMethodCellFactory());
paymentMethodComboBox.setPrefWidth(250); paymentMethodComboBox.setPrefWidth(250);
matchingOffersToggleButton = AwesomeDude.createIconToggleButton(AwesomeIcon.USER, null, "1.3em", null); matchingOffersToggleButton = AwesomeDude.createIconToggleButton(AwesomeIcon.USER, null, "1.5em", null);
matchingOffersToggleButton.getStyleClass().add("toggle-button-no-slider"); matchingOffersToggleButton.getStyleClass().add("toggle-button-no-slider");
matchingOffersToggleButton.setPrefHeight(27); matchingOffersToggleButton.setPrefHeight(27);
Tooltip matchingOffersTooltip = new Tooltip(Res.get("offerbook.matchingOffers")); Tooltip matchingOffersTooltip = new Tooltip(Res.get("offerbook.matchingOffers"));
@ -1069,39 +1069,39 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
return new TableCell<>() { return new TableCell<>() {
OfferFilterService.Result canTakeOfferResult = null; OfferFilterService.Result canTakeOfferResult = null;
final ImageView iconView = new ImageView();
final AutoTooltipButton button = new AutoTooltipButton();
{
button.setGraphic(iconView);
button.setGraphicTextGap(10);
button.setPrefWidth(10000);
}
final ImageView iconView2 = new ImageView();
final AutoTooltipButton button2 = new AutoTooltipButton();
{
button2.setGraphic(iconView2);
button2.setGraphicTextGap(10);
button2.setPrefWidth(10000);
}
final HBox hbox = new HBox();
{
hbox.setSpacing(8);
hbox.setAlignment(Pos.CENTER);
hbox.getChildren().add(button);
hbox.getChildren().add(button2);
HBox.setHgrow(button, Priority.ALWAYS);
HBox.setHgrow(button2, Priority.ALWAYS);
}
@Override @Override
public void updateItem(final OfferBookListItem item, boolean empty) { public void updateItem(final OfferBookListItem item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
final ImageView iconView = new ImageView();
final AutoTooltipButton button = new AutoTooltipButton();
{
button.setGraphic(iconView);
button.setGraphicTextGap(10);
button.setPrefWidth(10000);
}
final ImageView iconView2 = new ImageView();
final AutoTooltipButton button2 = new AutoTooltipButton();
{
button2.setGraphic(iconView2);
button2.setGraphicTextGap(10);
button2.setPrefWidth(10000);
}
final HBox hbox = new HBox();
{
hbox.setSpacing(8);
hbox.setAlignment(Pos.CENTER);
hbox.getChildren().add(button);
hbox.getChildren().add(button2);
HBox.setHgrow(button, Priority.ALWAYS);
HBox.setHgrow(button2, Priority.ALWAYS);
}
TableRow<OfferBookListItem> tableRow = getTableRow(); TableRow<OfferBookListItem> tableRow = getTableRow();
if (item != null && !empty) { if (item != null && !empty) {
Offer offer = item.getOffer(); Offer offer = item.getOffer();

View file

@ -260,7 +260,10 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
showAllTradeCurrenciesProperty.set(showAllEntry); showAllTradeCurrenciesProperty.set(showAllEntry);
if (isEditEntry(code)) if (isEditEntry(code))
navigation.navigateTo(MainView.class, SettingsView.class, PreferencesView.class); navigation.navigateTo(MainView.class, SettingsView.class, PreferencesView.class);
else if (!showAllEntry) { else if (showAllEntry) {
this.selectedTradeCurrency = getDefaultTradeCurrency();
tradeCurrencyCode.set(selectedTradeCurrency.getCode());
} else {
this.selectedTradeCurrency = tradeCurrency; this.selectedTradeCurrency = tradeCurrency;
tradeCurrencyCode.set(code); tradeCurrencyCode.set(code);
} }
@ -579,7 +582,10 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
getCurrencyAndMethodPredicate(direction, selectedTradeCurrency).and(getOffersMatchingMyAccountsPredicate()) : getCurrencyAndMethodPredicate(direction, selectedTradeCurrency).and(getOffersMatchingMyAccountsPredicate()) :
getCurrencyAndMethodPredicate(direction, selectedTradeCurrency); getCurrencyAndMethodPredicate(direction, selectedTradeCurrency);
predicate = predicate.and(offerBookListItem -> offerBookListItem.getOffer().isPrivateOffer() == showPrivateOffers); // filter private offers
if (direction == OfferDirection.BUY) {
predicate = predicate.and(offerBookListItem -> offerBookListItem.getOffer().isPrivateOffer() == showPrivateOffers);
}
if (!filterText.isEmpty()) { if (!filterText.isEmpty()) {

View file

@ -561,8 +561,10 @@
.toggle-button-no-slider { .toggle-button-no-slider {
-fx-focus-color: transparent; -fx-focus-color: transparent;
-fx-faint-focus-color: transparent; -fx-faint-focus-color: transparent;
-fx-background-radius: 3;
-fx-background-insets: 0, 1;
} }
.toggle-button-no-slider:selected { .toggle-button-no-slider:selected {
-fx-background-color: -bs-color-gray-ddd; -fx-background-color: -bs-color-gray-bbb;
} }

View file

@ -118,6 +118,10 @@ The price node is separated from Haveno and is run as a standalone service. To d
After the price node is built and deployed, add the price node to `DEFAULT_NODES` in [ProvidersRepository.java](https://github.com/haveno-dex/haveno/blob/3cdd88b56915c7f8afd4f1a39e6c1197c2665d63/core/src/main/java/haveno/core/provider/ProvidersRepository.java#L50). After the price node is built and deployed, add the price node to `DEFAULT_NODES` in [ProvidersRepository.java](https://github.com/haveno-dex/haveno/blob/3cdd88b56915c7f8afd4f1a39e6c1197c2665d63/core/src/main/java/haveno/core/provider/ProvidersRepository.java#L50).
### Update the download URL
Change every instance of `https://haveno.exchange/downloads` to your download URL. For example, `https://havenoexample.com/downloads`.
## Review all local changes ## Review all local changes
For comparison, placeholders to run on mainnet are marked [here on this branch](https://github.com/haveno-dex/haveno/tree/mainnet_placeholders). For comparison, placeholders to run on mainnet are marked [here on this branch](https://github.com/haveno-dex/haveno/tree/mainnet_placeholders).

View file

@ -243,6 +243,10 @@ Set `ARBITRATOR_ASSIGNS_TRADE_FEE_ADDRESS` to `true` for the arbitrator to assig
Otherwise set `ARBITRATOR_ASSIGNS_TRADE_FEE_ADDRESS` to `false` and set the XMR address in `getGlobalTradeFeeAddress()` to collect all trade fees to a single address (e.g. a multisig wallet shared among network administrators). Otherwise set `ARBITRATOR_ASSIGNS_TRADE_FEE_ADDRESS` to `false` and set the XMR address in `getGlobalTradeFeeAddress()` to collect all trade fees to a single address (e.g. a multisig wallet shared among network administrators).
## Update the download URL
Change every instance of `https://haveno.exchange/downloads` to your download URL. For example, `https://havenoexample.com/downloads`.
## Start users for testing ## Start users for testing
Start user1 on Monero's mainnet using `make user1-desktop-mainnet` or Monero's stagenet using `make user1-desktop-stagenet`. Start user1 on Monero's mainnet using `make user1-desktop-mainnet` or Monero's stagenet using `make user1-desktop-stagenet`.
@ -266,7 +270,7 @@ Then follow these instructions: https://github.com/haveno-dex/haveno/blob/master
<b>Set the mandatory minimum version for trading (optional)</b> <b>Set the mandatory minimum version for trading (optional)</b>
If applicable, update the mandatory minimum version for trading, by entering `ctrl + f` to open the Filter window, enter a private key with developer privileges, and enter the minimum version (e.g. 1.0.16) in the field labeled "Min. version required for trading". If applicable, update the mandatory minimum version for trading, by entering `ctrl + f` to open the Filter window, enter a private key with developer privileges, and enter the minimum version (e.g. 1.0.17) in the field labeled "Min. version required for trading".
<b>Send update alert</b> <b>Send update alert</b>

View file

@ -31,8 +31,8 @@ Follow [instructions](https://github.com/haveno-dex/haveno-ts#run-tests) to run
For example, the gRPC function to get offers is implemented by [`GrpcServer`](https://github.com/haveno-dex/haveno/blob/master/daemon/src/main/java/haveno/daemon/grpc/GrpcServer.java) > [`GrpcOffersService.getOffers(...)`](https://github.com/haveno-dex/haveno/blob/b761dbfd378faf49d95090c126318b419af7926b/daemon/src/main/java/haveno/daemon/grpc/GrpcOffersService.java#L104) > [`CoreApi.getOffers(...)`](https://github.com/haveno-dex/haveno/blob/b761dbfd378faf49d95090c126318b419af7926b/core/src/main/java/haveno/core/api/CoreApi.java#L128) > [`CoreOffersService.getOffers(...)`](https://github.com/haveno-dex/haveno/blob/b761dbfd378faf49d95090c126318b419af7926b/core/src/main/java/haveno/core/api/CoreOffersService.java#L126) > [`OfferBookService.getOffers()`](https://github.com/haveno-dex/haveno/blob/b761dbfd378faf49d95090c126318b419af7926b/core/src/main/java/haveno/core/offer/OfferBookService.java#L193). For example, the gRPC function to get offers is implemented by [`GrpcServer`](https://github.com/haveno-dex/haveno/blob/master/daemon/src/main/java/haveno/daemon/grpc/GrpcServer.java) > [`GrpcOffersService.getOffers(...)`](https://github.com/haveno-dex/haveno/blob/b761dbfd378faf49d95090c126318b419af7926b/daemon/src/main/java/haveno/daemon/grpc/GrpcOffersService.java#L104) > [`CoreApi.getOffers(...)`](https://github.com/haveno-dex/haveno/blob/b761dbfd378faf49d95090c126318b419af7926b/core/src/main/java/haveno/core/api/CoreApi.java#L128) > [`CoreOffersService.getOffers(...)`](https://github.com/haveno-dex/haveno/blob/b761dbfd378faf49d95090c126318b419af7926b/core/src/main/java/haveno/core/api/CoreOffersService.java#L126) > [`OfferBookService.getOffers()`](https://github.com/haveno-dex/haveno/blob/b761dbfd378faf49d95090c126318b419af7926b/core/src/main/java/haveno/core/offer/OfferBookService.java#L193).
5. Build Haveno: `make` 5. Build Haveno: `make`
6. Update the gRPC client in haveno-ts: `npm install` 6. Update the gRPC client in haveno-ts: `npm install`
7. Add the corresponding typescript method(s) to [haveno.ts](https://github.com/haveno-dex/haveno-ts/blob/master/src/haveno.ts) with clear and concise documentation. 7. Add the corresponding typescript method(s) to [HavenoClient.ts](https://github.com/haveno-dex/haveno-ts/blob/master/src/HavenoClient.ts) with clear and concise documentation.
8. Add clean and comprehensive tests to [haveno.test.ts](https://github.com/haveno-dex/haveno-ts/blob/master/src/haveno.test.ts), following existing patterns. 8. Add clean and comprehensive tests to [HavenoClient.test.ts](https://github.com/haveno-dex/haveno-ts/blob/master/src/HavenoClient.test.ts), following existing patterns.
9. Run the tests with `npm run test -- -t 'my test'` to run tests by name and `npm test` to run all tests together. Ensure all tests pass and there are no exception stacktraces in the terminals of Alice, Bob, or the arbitrator. 9. Run the tests with `npm run test -- -t 'my test'` to run tests by name and `npm test` to run all tests together. Ensure all tests pass and there are no exception stacktraces in the terminals of Alice, Bob, or the arbitrator.
10. Open pull requests to the haveno and haveno-ts projects for the backend and frontend implementations. 10. Open pull requests to the haveno and haveno-ts projects for the backend and frontend implementations.

View file

@ -41,7 +41,7 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
public class SeedNodeMain extends ExecutableForAppWithP2p { public class SeedNodeMain extends ExecutableForAppWithP2p {
private static final long CHECK_CONNECTION_LOSS_SEC = 30; private static final long CHECK_CONNECTION_LOSS_SEC = 30;
private static final String VERSION = "1.0.15"; private static final String VERSION = "1.0.17";
private SeedNode seedNode; private SeedNode seedNode;
private Timer checkConnectionLossTime; private Timer checkConnectionLossTime;