Add pre-release software update notifications

This commit is contained in:
jmacxx 2021-01-22 20:08:02 -06:00
parent 80cd4a8ad6
commit da45b8e6cf
No known key found for this signature in database
GPG key ID: 155297BABFE94A1B
10 changed files with 99 additions and 28 deletions

View file

@ -17,6 +17,8 @@
package bisq.core.alert;
import bisq.core.user.Preferences;
import bisq.network.p2p.storage.payload.ExpirablePayload;
import bisq.network.p2p.storage.payload.ProtectedStoragePayload;
@ -51,6 +53,7 @@ public final class Alert implements ProtectedStoragePayload, ExpirablePayload {
private final String message;
private final boolean isUpdateInfo;
private final boolean isPreReleaseInfo;
private final String version;
@Nullable
@ -68,9 +71,11 @@ public final class Alert implements ProtectedStoragePayload, ExpirablePayload {
public Alert(String message,
boolean isUpdateInfo,
boolean isPreReleaseInfo,
String version) {
this.message = message;
this.isUpdateInfo = isUpdateInfo;
this.isPreReleaseInfo = isPreReleaseInfo;
this.version = version;
}
@ -82,12 +87,14 @@ public final class Alert implements ProtectedStoragePayload, ExpirablePayload {
@SuppressWarnings("NullableProblems")
public Alert(String message,
boolean isUpdateInfo,
boolean isPreReleaseInfo,
String version,
byte[] ownerPubKeyBytes,
String signatureAsBase64,
Map<String, String> extraDataMap) {
this.message = message;
this.isUpdateInfo = isUpdateInfo;
this.isPreReleaseInfo = isPreReleaseInfo;
this.version = version;
this.ownerPubKeyBytes = ownerPubKeyBytes;
this.signatureAsBase64 = signatureAsBase64;
@ -103,6 +110,7 @@ public final class Alert implements ProtectedStoragePayload, ExpirablePayload {
protobuf.Alert.Builder builder = protobuf.Alert.newBuilder()
.setMessage(message)
.setIsUpdateInfo(isUpdateInfo)
.setIsPreReleaseInfo(isPreReleaseInfo)
.setVersion(version)
.setOwnerPubKeyBytes(ByteString.copyFrom(ownerPubKeyBytes))
.setSignatureAsBase64(signatureAsBase64);
@ -119,6 +127,7 @@ public final class Alert implements ProtectedStoragePayload, ExpirablePayload {
return new Alert(proto.getMessage(),
proto.getIsUpdateInfo(),
proto.getIsPreReleaseInfo(),
proto.getVersion(),
proto.getOwnerPubKeyBytes().toByteArray(),
proto.getSignatureAsBase64(),
@ -143,7 +152,28 @@ public final class Alert implements ProtectedStoragePayload, ExpirablePayload {
ownerPubKeyBytes = Sig.getPublicKeyBytes(ownerPubKey);
}
public boolean isNewVersion() {
return Version.isNewVersion(version);
public boolean isSoftwareUpdateNotification() {
return (isUpdateInfo || isPreReleaseInfo);
}
public boolean isNewVersion(Preferences preferences) {
// regular release: always notify user
// pre-release: if user has set preference to receive pre-release notification
if (isUpdateInfo ||
(isPreReleaseInfo && preferences.isNotifyOnPreRelease())) {
return Version.isNewVersion(version);
}
return false;
}
public boolean canShowPopup(Preferences preferences) {
// only show popup if its version is newer than current
// and only if user has not checked "don't show again"?
return isNewVersion(preferences) && preferences.showAgain(showAgainKey());
}
public String showAgainKey() {
return "Update_" + version;
}
}

View file

@ -250,20 +250,23 @@ public class BisqSetup {
///////////////////////////////////////////////////////////////////////////////////////////
public void displayAlertIfPresent(Alert alert, boolean openNewVersionPopup) {
if (alert != null) {
if (alert.isUpdateInfo()) {
user.setDisplayedAlert(alert);
final boolean isNewVersion = alert.isNewVersion();
newVersionAvailableProperty.set(isNewVersion);
String key = "Update_" + alert.getVersion();
if (isNewVersion && (preferences.showAgain(key) || openNewVersionPopup) && displayUpdateHandler != null) {
displayUpdateHandler.accept(alert, key);
if (alert == null)
return;
if (alert.isSoftwareUpdateNotification()) {
// only process if the alert version is "newer" than ours
if (alert.isNewVersion(preferences)) {
user.setDisplayedAlert(alert); // save context to compare later
newVersionAvailableProperty.set(true); // shows link in footer bar
if ((alert.canShowPopup(preferences) || openNewVersionPopup) && displayUpdateHandler != null) {
displayUpdateHandler.accept(alert, alert.showAgainKey());
}
} else {
final Alert displayedAlert = user.getDisplayedAlert();
if ((displayedAlert == null || !displayedAlert.equals(alert)) && displayAlertHandler != null)
displayAlertHandler.accept(alert);
}
} else {
// it is a normal message alert
final Alert displayedAlert = user.getDisplayedAlert();
if ((displayedAlert == null || !displayedAlert.equals(alert)) && displayAlertHandler != null)
displayAlertHandler.accept(alert);
}
}

View file

@ -782,6 +782,11 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
requestPersistence();
}
public void setNotifyOnPreRelease(boolean value) {
prefPayload.setNotifyOnPreRelease(value);
requestPersistence();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getter
@ -1095,5 +1100,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
void setShowOffersMatchingMyAccounts(boolean value);
void setDenyApiTaker(boolean value);
void setNotifyOnPreRelease(boolean value);
}
}

View file

@ -133,6 +133,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
private boolean hideNonAccountPaymentMethods;
private boolean showOffersMatchingMyAccounts;
private boolean denyApiTaker;
private boolean notifyOnPreRelease;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@ -199,7 +200,8 @@ public final class PreferencesPayload implements PersistableEnvelope {
.collect(Collectors.toList()))
.setHideNonAccountPaymentMethods(hideNonAccountPaymentMethods)
.setShowOffersMatchingMyAccounts(showOffersMatchingMyAccounts)
.setDenyApiTaker(denyApiTaker);
.setDenyApiTaker(denyApiTaker)
.setNotifyOnPreRelease(notifyOnPreRelease);
Optional.ofNullable(backupDirectory).ifPresent(builder::setBackupDirectory);
Optional.ofNullable(preferredTradeCurrency).ifPresent(e -> builder.setPreferredTradeCurrency((protobuf.TradeCurrency) e.toProtoMessage()));
@ -296,7 +298,8 @@ public final class PreferencesPayload implements PersistableEnvelope {
.collect(Collectors.toList())),
proto.getHideNonAccountPaymentMethods(),
proto.getShowOffersMatchingMyAccounts(),
proto.getDenyApiTaker()
proto.getDenyApiTaker(),
proto.getNotifyOnPreRelease()
);
}
}

View file

@ -1223,6 +1223,7 @@ setting.preferences.useDarkMode=Use dark mode
setting.preferences.sortWithNumOffers=Sort market lists with no. of offers/trades
setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods
setting.preferences.denyApiTaker=Deny takers using the API
setting.preferences.notifyOnPreRelease=Receive pre-release notifications
setting.preferences.resetAllFlags=Reset all \"Don't show again\" flags
settings.preferences.languageChange=To apply the language change to all screens requires a restart.
settings.preferences.supportLanguageWarning=In case of a dispute, please note that mediation is handled in {0} and arbitration in {1}.
@ -2658,7 +2659,9 @@ selectDepositTxWindow.select=Select deposit transaction
sendAlertMessageWindow.headline=Send global notification
sendAlertMessageWindow.alertMsg=Alert message
sendAlertMessageWindow.enterMsg=Enter message
sendAlertMessageWindow.isUpdate=Is update notification
sendAlertMessageWindow.isSoftwareUpdate=Software download notification
sendAlertMessageWindow.isUpdate=Is full release
sendAlertMessageWindow.isPreRelease=Is pre-release
sendAlertMessageWindow.version=New version no.
sendAlertMessageWindow.send=Send notification
sendAlertMessageWindow.remove=Remove notification

View file

@ -41,7 +41,7 @@ public class UserPayloadModelVOTest {
public void testRoundtripFull() {
UserPayload vo = new UserPayload();
vo.setAccountId("accountId");
vo.setDisplayedAlert(new Alert("message", true, "version", new byte[]{12, -64, 12}, "string", null));
vo.setDisplayedAlert(new Alert("message", true, false, "version", new byte[]{12, -64, 12}, "string", null));
vo.setDevelopersFilter(new Filter(Lists.newArrayList(),
Lists.newArrayList(),
Lists.newArrayList(),

View file

@ -50,7 +50,7 @@ public class DisplayAlertMessageWindow extends Overlay<DisplayAlertMessageWindow
checkNotNull(alert, "alertMessage must not be null");
if (alert.isUpdateInfo()) {
if (alert.isUpdateInfo() || alert.isPreReleaseInfo()) {
information("");
headLine = Res.get("displayAlertMessageWindow.update.headline");
} else {
@ -78,7 +78,7 @@ public class DisplayAlertMessageWindow extends Overlay<DisplayAlertMessageWindow
private void addContent() {
checkNotNull(alert, "alertMessage must not be null");
addMultilineLabel(gridPane, ++rowIndex, alert.getMessage(), 10);
if (alert.isUpdateInfo()) {
if (alert.isUpdateInfo() || alert.isPreReleaseInfo()) {
String url = "https://bisq.network/downloads";
HyperlinkWithIcon hyperlinkWithIcon = FormBuilder.addLabelHyperlinkWithIcon(gridPane, ++rowIndex,
Res.get("displayAlertMessageWindow.update.download"), url, url).second;

View file

@ -39,7 +39,9 @@ import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextArea;
import javafx.scene.control.ToggleGroup;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
@ -49,6 +51,7 @@ import javafx.geometry.Insets;
import static bisq.desktop.util.FormBuilder.addInputTextField;
import static bisq.desktop.util.FormBuilder.addLabelCheckBox;
import static bisq.desktop.util.FormBuilder.addRadioButton;
import static bisq.desktop.util.FormBuilder.addTopLabelTextArea;
public class SendAlertMessageWindow extends Overlay<SendAlertMessageWindow> {
@ -107,13 +110,26 @@ public class SendAlertMessageWindow extends Overlay<SendAlertMessageWindow> {
TextArea alertMessageTextArea = labelTextAreaTuple2.second;
Label first = labelTextAreaTuple2.first;
first.setMinWidth(150);
CheckBox isUpdateCheckBox = addLabelCheckBox(gridPane, ++rowIndex,
Res.get("sendAlertMessageWindow.isUpdate"));
CheckBox isSoftwareUpdateCheckBox = addLabelCheckBox(gridPane, ++rowIndex,
Res.get("sendAlertMessageWindow.isSoftwareUpdate"));
HBox hBoxRelease = new HBox();
hBoxRelease.setSpacing(10);
GridPane.setRowIndex(hBoxRelease, ++rowIndex);
ToggleGroup toggleGroup = new ToggleGroup();
RadioButton isUpdateCheckBox = addRadioButton(gridPane, rowIndex, toggleGroup, Res.get("sendAlertMessageWindow.isUpdate"));
RadioButton isPreReleaseCheckBox = addRadioButton(gridPane, rowIndex, toggleGroup, Res.get("sendAlertMessageWindow.isPreRelease"));
hBoxRelease.getChildren().addAll(new Label(""), isUpdateCheckBox, isPreReleaseCheckBox);
gridPane.getChildren().add(hBoxRelease);
isSoftwareUpdateCheckBox.setSelected(true);
isUpdateCheckBox.setSelected(true);
InputTextField versionInputTextField = FormBuilder.addInputTextField(gridPane, ++rowIndex,
Res.get("sendAlertMessageWindow.version"));
versionInputTextField.disableProperty().bind(isUpdateCheckBox.selectedProperty().not());
versionInputTextField.disableProperty().bind(isSoftwareUpdateCheckBox.selectedProperty().not());
isUpdateCheckBox.disableProperty().bind(isSoftwareUpdateCheckBox.selectedProperty().not());
isPreReleaseCheckBox.disableProperty().bind(isSoftwareUpdateCheckBox.selectedProperty().not());
Button sendButton = new AutoTooltipButton(Res.get("sendAlertMessageWindow.send"));
sendButton.getStyleClass().add("action-button");
@ -121,8 +137,9 @@ public class SendAlertMessageWindow extends Overlay<SendAlertMessageWindow> {
sendButton.setOnAction(e -> {
final String version = versionInputTextField.getText();
boolean versionOK = false;
final boolean isUpdate = isUpdateCheckBox.isSelected();
if (isUpdate) {
final boolean isUpdate = (isSoftwareUpdateCheckBox.isSelected() && isUpdateCheckBox.isSelected());
final boolean isPreRelease = (isSoftwareUpdateCheckBox.isSelected() && isPreReleaseCheckBox.isSelected());
if (isUpdate || isPreRelease) {
final String[] split = version.split("\\.");
versionOK = split.length == 3;
if (!versionOK) // Do not translate as only used by devs
@ -130,10 +147,10 @@ public class SendAlertMessageWindow extends Overlay<SendAlertMessageWindow> {
.onClose(this::blurAgain)
.show();
}
if (!isUpdate || versionOK) {
if (!isSoftwareUpdateCheckBox.isSelected() || versionOK) {
if (alertMessageTextArea.getText().length() > 0 && keyInputTextField.getText().length() > 0) {
if (alertManager.addAlertMessageIfKeyIsValid(
new Alert(alertMessageTextArea.getText(), isUpdate, version),
new Alert(alertMessageTextArea.getText(), isUpdate, isPreRelease, version),
keyInputTextField.getText())
)
hide();

View file

@ -120,7 +120,8 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
private ComboBox<TradeCurrency> preferredTradeCurrencyComboBox;
private ToggleButton showOwnOffersInOfferBook, useAnimations, useDarkMode, sortMarketCurrenciesNumerically,
avoidStandbyMode, useCustomFee, autoConfirmXmrToggle, hideNonAccountPaymentMethodsToggle, denyApiTakerToggle;
avoidStandbyMode, useCustomFee, autoConfirmXmrToggle, hideNonAccountPaymentMethodsToggle, denyApiTakerToggle,
notifyOnPreReleaseToggle;
private int gridRow = 0;
private int displayCurrenciesGridRowIndex = 0;
private InputTextField transactionFeeInputTextField, ignoreTradersListInputTextField, ignoreDustThresholdInputTextField,
@ -612,6 +613,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
sortMarketCurrenciesNumerically = addSlideToggleButton(root, ++gridRow, Res.get("setting.preferences.sortWithNumOffers"));
hideNonAccountPaymentMethodsToggle = addSlideToggleButton(root, ++gridRow, Res.get("setting.preferences.onlyShowPaymentMethodsFromAccount"));
denyApiTakerToggle = addSlideToggleButton(root, ++gridRow, Res.get("setting.preferences.denyApiTaker"));
notifyOnPreReleaseToggle = addSlideToggleButton(root, ++gridRow, Res.get("setting.preferences.notifyOnPreRelease"));
resetDontShowAgainButton = addButton(root, ++gridRow, Res.get("setting.preferences.resetAllFlags"), 0);
resetDontShowAgainButton.getStyleClass().add("compact-button");
resetDontShowAgainButton.setMaxWidth(Double.MAX_VALUE);
@ -954,6 +956,9 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
denyApiTakerToggle.setSelected(preferences.isDenyApiTaker());
denyApiTakerToggle.setOnAction(e -> preferences.setDenyApiTaker(denyApiTakerToggle.isSelected()));
notifyOnPreReleaseToggle.setSelected(preferences.isNotifyOnPreRelease());
notifyOnPreReleaseToggle.setOnAction(e -> preferences.setNotifyOnPreRelease(notifyOnPreReleaseToggle.isSelected()));
resetDontShowAgainButton.setOnAction(e -> preferences.resetDontShowAgain());
editCustomBtcExplorer.setOnAction(e -> {
@ -1133,6 +1138,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
sortMarketCurrenciesNumerically.setOnAction(null);
hideNonAccountPaymentMethodsToggle.setOnAction(null);
denyApiTakerToggle.setOnAction(null);
notifyOnPreReleaseToggle.setOnAction(null);
showOwnOffersInOfferBook.setOnAction(null);
resetDontShowAgainButton.setOnAction(null);
if (displayStandbyModeFeature) {

View file

@ -620,6 +620,7 @@ message Alert {
string signature_as_base64 = 4;
bytes owner_pub_key_bytes = 5;
map<string, string> extra_data = 6;
bool is_pre_release_info = 7;
}
message Arbitrator {
@ -1638,6 +1639,7 @@ message PreferencesPayload {
bool hide_non_account_payment_methods = 58;
bool show_offers_matching_my_accounts = 59;
bool deny_api_taker = 60;
bool notify_on_pre_release = 61;
}
message AutoConfirmSettings {