mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 18:03:12 +01:00
Add feature for deactivating an offer #1368
This commit is contained in:
parent
521dd62114
commit
beaaf59b70
@ -1099,6 +1099,7 @@ message OpenOffer {
|
||||
RESERVED = 2;
|
||||
CLOSED = 3;
|
||||
CANCELED = 4;
|
||||
DEACTIVATED = 5;
|
||||
}
|
||||
|
||||
Offer offer = 1;
|
||||
|
@ -84,6 +84,8 @@ shared.bankName=Bank name
|
||||
shared.acceptedBanks=Accepted banks
|
||||
shared.amountMinMax=Amount (min - max)
|
||||
shared.remove=Remove
|
||||
shared.deactivate=Deactivate
|
||||
shared.activate=Activate
|
||||
shared.goTo=Go to {0}
|
||||
shared.BTCMinMax=BTC (min - max)
|
||||
shared.removeOffer=Remove offer
|
||||
@ -315,6 +317,8 @@ offerbook.yesCreateOffer=Yes, create offer
|
||||
offerbook.setupNewAccount=Set up a new trading account
|
||||
offerbook.removeOffer.success=Remove offer was successful.
|
||||
offerbook.removeOffer.failed=Remove offer failed:\n{0}
|
||||
offerbook.deactivateOffer.failed=Deactivating of offer failed:\n{0}
|
||||
offerbook.activateOffer.failed=Publishing of offer failed:\n{0}
|
||||
offerbook.withdrawFundsHint=You can withdraw the funds you paid in from the {0} screen.
|
||||
|
||||
offerbook.warning.noTradingAccountForCurrency.headline=No trading account for selected currency
|
||||
|
@ -144,6 +144,14 @@ public class OfferBookService {
|
||||
}
|
||||
}
|
||||
|
||||
public void activateOffer(Offer offer, @Nullable ResultHandler resultHandler, @Nullable ErrorMessageHandler errorMessageHandler) {
|
||||
addOffer(offer, resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
public void deactivateOffer(OfferPayload offerPayload, @Nullable ResultHandler resultHandler, @Nullable ErrorMessageHandler errorMessageHandler) {
|
||||
removeOffer(offerPayload, resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
public void removeOffer(OfferPayload offerPayload, @Nullable ResultHandler resultHandler, @Nullable ErrorMessageHandler errorMessageHandler) {
|
||||
if (p2PService.removeData(offerPayload, true)) {
|
||||
log.trace("Remove offer from network was successful. OfferPayload ID = " + offerPayload.getId());
|
||||
|
@ -19,6 +19,7 @@ package io.bisq.core.offer;
|
||||
|
||||
import io.bisq.common.Timer;
|
||||
import io.bisq.common.UserThread;
|
||||
import io.bisq.common.proto.ProtoUtil;
|
||||
import io.bisq.common.storage.Storage;
|
||||
import io.bisq.core.trade.Tradable;
|
||||
import io.bisq.core.trade.TradableList;
|
||||
@ -40,29 +41,34 @@ public final class OpenOffer implements Tradable {
|
||||
AVAILABLE,
|
||||
RESERVED,
|
||||
CLOSED,
|
||||
CANCELED
|
||||
CANCELED,
|
||||
DEACTIVATED
|
||||
}
|
||||
|
||||
@Getter
|
||||
private final Offer offer;
|
||||
@Getter
|
||||
private State state = State.AVAILABLE;
|
||||
private State state;
|
||||
|
||||
transient private Storage<TradableList<OpenOffer>> storage;
|
||||
|
||||
public OpenOffer(Offer offer, Storage<TradableList<OpenOffer>> storage) {
|
||||
this.offer = offer;
|
||||
this.storage = storage;
|
||||
state = State.AVAILABLE;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PROTO BUFFER
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private OpenOffer(Offer offer) {
|
||||
private OpenOffer(Offer offer, State state) {
|
||||
this.offer = offer;
|
||||
}
|
||||
this.state = state;
|
||||
|
||||
if (this.state == State.RESERVED)
|
||||
setState(State.AVAILABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PB.Tradable toProtoMessage() {
|
||||
@ -73,11 +79,8 @@ public final class OpenOffer implements Tradable {
|
||||
}
|
||||
|
||||
public static Tradable fromProto(PB.OpenOffer proto) {
|
||||
OpenOffer openOffer = new OpenOffer(Offer.fromProto(proto.getOffer()));
|
||||
// If we have a reserved state from the local db we reset it
|
||||
if (openOffer.getState() == State.RESERVED)
|
||||
openOffer.setState(State.AVAILABLE);
|
||||
return openOffer;
|
||||
return new OpenOffer(Offer.fromProto(proto.getOffer()),
|
||||
ProtoUtil.enumFromProto(OpenOffer.State.class, proto.getState().name()));
|
||||
}
|
||||
|
||||
|
||||
@ -118,6 +121,10 @@ public final class OpenOffer implements Tradable {
|
||||
stopTimeout();
|
||||
}
|
||||
|
||||
public boolean isDeactivated() {
|
||||
return state == State.DEACTIVATED;
|
||||
}
|
||||
|
||||
private void startTimeout() {
|
||||
stopTimeout();
|
||||
|
||||
|
@ -327,7 +327,30 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from my offers
|
||||
public void activateOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
Offer offer = openOffer.getOffer();
|
||||
openOffer.setStorage(openOfferTradableListStorage);
|
||||
offerBookService.activateOffer(offer,
|
||||
() -> {
|
||||
openOffer.setState(OpenOffer.State.AVAILABLE);
|
||||
log.debug("activateOpenOffer, offerId={}", offer.getId());
|
||||
resultHandler.handleResult();
|
||||
},
|
||||
errorMessageHandler);
|
||||
}
|
||||
|
||||
public void deactivateOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
Offer offer = openOffer.getOffer();
|
||||
openOffer.setStorage(openOfferTradableListStorage);
|
||||
offerBookService.deactivateOffer(offer.getOfferPayload(),
|
||||
() -> {
|
||||
openOffer.setState(OpenOffer.State.DEACTIVATED);
|
||||
log.debug("deactivateOpenOffer, offerId={}", offer.getId());
|
||||
resultHandler.handleResult();
|
||||
},
|
||||
errorMessageHandler);
|
||||
}
|
||||
|
||||
public void removeOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
Offer offer = openOffer.getOffer();
|
||||
offerBookService.removeOffer(offer.getOfferPayload(),
|
||||
@ -483,13 +506,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||
final OpenOffer openOffer = openOffersList.get(i);
|
||||
UserThread.runAfterRandomDelay(() -> {
|
||||
if (openOffers.contains(openOffer)) {
|
||||
// The openOffer.getId().contains("_") check is because there was once a version
|
||||
// where we encoded the version nr in the offer id with a "_" as separator.
|
||||
// That caused several issues and was reverted. So if there are still old offers out with that
|
||||
// special offer ID format those must not be published as they cause failed taker attempts
|
||||
// with lost taker fee.
|
||||
String id = openOffer.getId();
|
||||
if (id != null && !id.contains("_"))
|
||||
if (id != null && !openOffer.isDeactivated())
|
||||
republishOffer(openOffer);
|
||||
else
|
||||
log.warn("You have an offer with an invalid offer ID: offerID=" + id);
|
||||
@ -567,7 +585,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||
final OpenOffer openOffer = openOffersList.get(i);
|
||||
UserThread.runAfterRandomDelay(() -> {
|
||||
// we need to check if in the meantime the offer has been removed
|
||||
if (openOffers.contains(openOffer))
|
||||
if (openOffers.contains(openOffer) && !openOffer.isDeactivated())
|
||||
refreshOffer(openOffer);
|
||||
}, minDelay, maxDelay, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
@ -35,8 +35,8 @@ class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataMod
|
||||
final AccountAgeWitnessService accountAgeWitnessService;
|
||||
|
||||
@Inject
|
||||
public ClosedTradesViewModel(ClosedTradesDataModel dataModel,
|
||||
AccountAgeWitnessService accountAgeWitnessService,
|
||||
public ClosedTradesViewModel(ClosedTradesDataModel dataModel,
|
||||
AccountAgeWitnessService accountAgeWitnessService,
|
||||
BSFormatter formatter) {
|
||||
super(dataModel);
|
||||
this.accountAgeWitnessService = accountAgeWitnessService;
|
||||
@ -118,6 +118,9 @@ class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataMod
|
||||
return state.toString();
|
||||
case CANCELED:
|
||||
return Res.get("portfolio.closed.canceled");
|
||||
case DEACTIVATED:
|
||||
log.error("Invalid state {}", state);
|
||||
return state.toString();
|
||||
default:
|
||||
log.error("Unhandled state {}", state);
|
||||
return state.toString();
|
||||
|
@ -63,7 +63,15 @@ class OpenOffersDataModel extends ActivatableDataModel {
|
||||
priceFeedService.updateCounterProperty().removeListener(currenciesUpdateFlagPropertyListener);
|
||||
}
|
||||
|
||||
void onCancelOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
void onActivateOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
openOfferManager.activateOpenOffer(openOffer, resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
void onDeactivateOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
openOfferManager.deactivateOpenOffer(openOffer, resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
void onRemoveOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
openOfferManager.removeOpenOffer(openOffer, resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
|
@ -28,15 +28,16 @@
|
||||
|
||||
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TableColumn fx:id="offerIdColumn" minWidth="120" maxWidth="130"/>
|
||||
<TableColumn fx:id="dateColumn" minWidth="200"/>
|
||||
<TableColumn fx:id="marketColumn" minWidth="100"/>
|
||||
<TableColumn fx:id="priceColumn" minWidth="160"/>
|
||||
<TableColumn fx:id="amountColumn" minWidth="160"/>
|
||||
<TableColumn fx:id="volumeColumn" minWidth="180"/>
|
||||
<TableColumn fx:id="directionColumn" minWidth="100"/>
|
||||
<TableColumn fx:id="removeItemColumn" minWidth="120" maxWidth="120" sortable="false"/>
|
||||
<TableColumn fx:id="offerIdColumn" minWidth="110" maxWidth="130"/>
|
||||
<TableColumn fx:id="dateColumn" minWidth="180"/>
|
||||
<TableColumn fx:id="marketColumn" minWidth="90"/>
|
||||
<TableColumn fx:id="priceColumn" minWidth="150"/>
|
||||
<TableColumn fx:id="amountColumn" minWidth="150"/>
|
||||
<TableColumn fx:id="volumeColumn" minWidth="170"/>
|
||||
<TableColumn fx:id="directionColumn" minWidth="80"/>
|
||||
<TableColumn fx:id="deactivateItemColumn" minWidth="120" maxWidth="120" sortable="false"/>
|
||||
<TableColumn fx:id="removeItemColumn" minWidth="110" maxWidth="120" sortable="false"/>
|
||||
</columns>
|
||||
</TableView>
|
||||
|
||||
</VBox>
|
||||
</VBox>
|
||||
|
@ -38,6 +38,7 @@ import javafx.scene.control.*;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.util.Callback;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@ -48,7 +49,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
|
||||
TableView<OpenOfferListItem> tableView;
|
||||
@FXML
|
||||
TableColumn<OpenOfferListItem, OpenOfferListItem> priceColumn, amountColumn, volumeColumn,
|
||||
marketColumn, directionColumn, dateColumn, offerIdColumn, removeItemColumn;
|
||||
marketColumn, directionColumn, dateColumn, offerIdColumn, deactivateItemColumn, removeItemColumn;
|
||||
private final Navigation navigation;
|
||||
private final OfferDetailsWindow offerDetailsWindow;
|
||||
private SortedList<OpenOfferListItem> sortedList;
|
||||
@ -69,6 +70,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
|
||||
directionColumn.setText(Res.get("shared.offerType"));
|
||||
dateColumn.setText(Res.get("shared.dateTime"));
|
||||
offerIdColumn.setText(Res.get("shared.offerId"));
|
||||
deactivateItemColumn.setText("");
|
||||
removeItemColumn.setText("");
|
||||
|
||||
setOfferIdColumnCellFactory();
|
||||
@ -78,6 +80,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
|
||||
setAmountColumnCellFactory();
|
||||
setVolumeColumnCellFactory();
|
||||
setDateColumnCellFactory();
|
||||
setDeactivateColumnCellFactory();
|
||||
setRemoveColumnCellFactory();
|
||||
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
@ -115,6 +118,36 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
|
||||
sortedList.comparatorProperty().unbind();
|
||||
}
|
||||
|
||||
private void onDeactivateOpenOffer(OpenOffer openOffer) {
|
||||
if (model.isBootstrapped()) {
|
||||
model.onDeactivateOpenOffer(openOffer,
|
||||
() -> {
|
||||
log.debug("Deactivate offer was successful");
|
||||
},
|
||||
(message) -> {
|
||||
log.error(message);
|
||||
new Popup<>().warning(Res.get("offerbook.deactivateOffer.failed", message)).show();
|
||||
});
|
||||
} else {
|
||||
new Popup<>().information(Res.get("popup.warning.notFullyConnected")).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void onActivateOpenOffer(OpenOffer openOffer) {
|
||||
if (model.isBootstrapped()) {
|
||||
model.onActivateOpenOffer(openOffer,
|
||||
() -> {
|
||||
log.debug("Activate offer was successful");
|
||||
},
|
||||
(message) -> {
|
||||
log.error(message);
|
||||
new Popup<>().warning(Res.get("offerbook.activateOffer.failed", message)).show();
|
||||
});
|
||||
} else {
|
||||
new Popup<>().information(Res.get("popup.warning.notFullyConnected")).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void onRemoveOpenOffer(OpenOffer openOffer) {
|
||||
if (model.isBootstrapped()) {
|
||||
String key = "RemoveOfferWarning";
|
||||
@ -133,7 +166,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
|
||||
}
|
||||
|
||||
private void doRemoveOpenOffer(OpenOffer openOffer) {
|
||||
model.onCancelOpenOffer(openOffer,
|
||||
model.onRemoveOpenOffer(openOffer,
|
||||
() -> {
|
||||
log.debug("Remove offer was successful");
|
||||
String key = "WithdrawFundsAfterRemoveOfferInfo";
|
||||
@ -302,6 +335,61 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
|
||||
});
|
||||
}
|
||||
|
||||
private void setDeactivateColumnCellFactory() {
|
||||
deactivateItemColumn.setCellValueFactory((offerListItem) -> new ReadOnlyObjectWrapper<>(offerListItem.getValue()));
|
||||
deactivateItemColumn.setCellFactory(
|
||||
new Callback<TableColumn<OpenOfferListItem, OpenOfferListItem>, TableCell<OpenOfferListItem, OpenOfferListItem>>() {
|
||||
@Override
|
||||
public TableCell<OpenOfferListItem, OpenOfferListItem> call(TableColumn<OpenOfferListItem, OpenOfferListItem> column) {
|
||||
return new TableCell<OpenOfferListItem, OpenOfferListItem>() {
|
||||
final ImageView iconView = new ImageView();
|
||||
Button button;
|
||||
|
||||
private void updateState(@NotNull OpenOffer openOffer) {
|
||||
if (openOffer.isDeactivated()) {
|
||||
button.setText(Res.get("shared.activate"));
|
||||
iconView.setId("image-alert-round");
|
||||
button.setGraphic(iconView);
|
||||
} else {
|
||||
button.setText(Res.get("shared.deactivate"));
|
||||
iconView.setId("image-green_circle");
|
||||
button.setGraphic(iconView);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(final OpenOfferListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null && !empty) {
|
||||
if (button == null) {
|
||||
button = new Button();
|
||||
button.setGraphic(iconView);
|
||||
updateState(item.getOpenOffer());
|
||||
button.setMinWidth(70);
|
||||
setGraphic(button);
|
||||
}
|
||||
button.setOnAction(event -> {
|
||||
if (item.getOpenOffer().isDeactivated()) {
|
||||
onActivateOpenOffer(item.getOpenOffer());
|
||||
} else {
|
||||
onDeactivateOpenOffer(item.getOpenOffer());
|
||||
}
|
||||
updateState(item.getOpenOffer());
|
||||
});
|
||||
} else {
|
||||
setGraphic(null);
|
||||
if (button != null) {
|
||||
button.setOnAction(null);
|
||||
button = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setRemoveColumnCellFactory() {
|
||||
removeItemColumn.setCellValueFactory((offerListItem) -> new ReadOnlyObjectWrapper<>(offerListItem.getValue()));
|
||||
removeItemColumn.setCellFactory(
|
||||
@ -318,9 +406,9 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
|
||||
|
||||
if (item != null && !empty) {
|
||||
if (button == null) {
|
||||
iconView.setId("image-remove");
|
||||
button = new Button(Res.get("shared.remove"));
|
||||
button.setMinWidth(70);
|
||||
iconView.setId("image-remove");
|
||||
button.setGraphic(iconView);
|
||||
setGraphic(button);
|
||||
}
|
||||
|
@ -45,8 +45,16 @@ class OpenOffersViewModel extends ActivatableWithDataModel<OpenOffersDataModel>
|
||||
this.formatter = formatter;
|
||||
}
|
||||
|
||||
void onCancelOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
dataModel.onCancelOpenOffer(openOffer, resultHandler, errorMessageHandler);
|
||||
void onActivateOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
dataModel.onActivateOpenOffer(openOffer, resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
void onDeactivateOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
dataModel.onDeactivateOpenOffer(openOffer, resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
void onRemoveOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
dataModel.onRemoveOpenOffer(openOffer, resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
public ObservableList<OpenOfferListItem> getList() {
|
||||
|
Loading…
Reference in New Issue
Block a user