Merge pull request #1795 from ManfredKarrer/DAO-UI-hide-details

Dao UI improvements
This commit is contained in:
Manfred Karrer 2018-10-23 17:19:56 -05:00 committed by GitHub
commit fd6681b9a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 715 additions and 358 deletions

View file

@ -917,6 +917,7 @@ message PersistableEnvelope {
UserPayload user_payload = 10;
PaymentAccountList payment_account_list = 11;
// deprecated
// BsqState bsq_state = 12; // not used but as other non-dao data have a higher index number we leave it to make clear that we cannot change following indexes
AccountAgeWitnessStore account_age_witness_store = 13;
@ -1272,6 +1273,9 @@ message PreferencesPayload {
bool use_market_notifications = 43;
bool use_price_notifications = 44;
bool use_standby_mode = 45;
bool is_dao_full_node = 46;
string rpc_user = 47;
string rpc_pw = 48;
}
///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -430,9 +430,8 @@ public abstract class BisqExecutable implements GracefulShutDownHandler {
parser.accepts(DaoOptionKeys.FULL_DAO_NODE,
description("If set to true the node requests the blockchain data via RPC requests from Bitcoin Core and " +
"provide the validated BSQ txs to the network. It requires that the other RPC properties are " +
"set as well.", false))
.withRequiredArg()
.ofType(boolean.class);
"set as well.", ""))
.withRequiredArg();
parser.accepts(DaoOptionKeys.GENESIS_TX_ID,
description("Genesis transaction ID when not using the hard coded one", ""))
.withRequiredArg();

View file

@ -641,4 +641,8 @@ public class BsqWalletService extends WalletService implements DaoStateListener
.findAny()
.orElse(wallet.freshReceiveAddress());
}
public String getUnusedBsqAddressAsString() {
return "B" + getUnusedAddress().toBase58();
}
}

View file

@ -54,6 +54,7 @@ import bisq.core.dao.state.blockchain.Tx;
import bisq.core.dao.state.blockchain.TxOutput;
import bisq.core.dao.state.blockchain.TxOutputKey;
import bisq.core.dao.state.blockchain.TxType;
import bisq.core.dao.state.governance.Issuance;
import bisq.core.dao.state.governance.Param;
import bisq.core.dao.state.period.DaoPhase;
import bisq.core.dao.state.period.PeriodService;
@ -80,6 +81,8 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
@ -90,6 +93,7 @@ import bisq.asset.Asset;
* Provides a facade to interact with the Dao domain. Hides complexity and domain details to clients (e.g. UI or APIs)
* by providing a reduced API and/or aggregating subroutines.
*/
@Slf4j
public class DaoFacade implements DaoSetupService {
private final ProposalListPresentation proposalListPresentation;
private final BallotListService ballotListService;
@ -212,13 +216,11 @@ public class DaoFacade implements DaoSetupService {
// Creation of Proposal and proposalTransaction
public ProposalWithTransaction getCompensationProposalWithTransaction(String name,
String link,
Coin requestedBsq,
String bsqAddress)
Coin requestedBsq)
throws ValidationException, InsufficientMoneyException, TxException {
return compensationProposalService.createProposalWithTransaction(name,
link,
requestedBsq,
bsqAddress);
requestedBsq);
}
public ProposalWithTransaction getParamProposalWithTransaction(String name,
@ -358,16 +360,92 @@ public class DaoFacade implements DaoSetupService {
// Use case: Presentation of phases
///////////////////////////////////////////////////////////////////////////////////////////
public int getFirstBlockOfPhase(int height, DaoPhase.Phase phase) {
return periodService.getFirstBlockOfPhase(height, phase);
// Because last block in request and voting phases must not be used fo making a tx as it will get confirmed in the
// next block which would be already the next phase we hide that last block to the user and add it to the break.
public int getFirstBlockOfPhaseForDisplay(int height, DaoPhase.Phase phase) {
int firstBlock = periodService.getFirstBlockOfPhase(height, phase);
switch (phase) {
case UNDEFINED:
break;
case PROPOSAL:
break;
case BREAK1:
firstBlock++;
break;
case BLIND_VOTE:
break;
case BREAK2:
firstBlock++;
break;
case VOTE_REVEAL:
break;
case BREAK3:
firstBlock++;
break;
case RESULT:
break;
}
return firstBlock;
}
public int getLastBlockOfPhase(int height, DaoPhase.Phase phase) {
return periodService.getLastBlockOfPhase(height, phase);
// Because last block in request and voting phases must not be used fo making a tx as it will get confirmed in the
// next block which would be already the next phase we hide that last block to the user and add it to the break.
public int getLastBlockOfPhaseForDisplay(int height, DaoPhase.Phase phase) {
int lastBlock = periodService.getLastBlockOfPhase(height, phase);
switch (phase) {
case UNDEFINED:
break;
case PROPOSAL:
lastBlock--;
break;
case BREAK1:
break;
case BLIND_VOTE:
lastBlock--;
break;
case BREAK2:
break;
case VOTE_REVEAL:
lastBlock--;
break;
case BREAK3:
break;
case RESULT:
break;
}
return lastBlock;
}
public int getDurationForPhase(DaoPhase.Phase phase) {
return periodService.getDurationForPhase(phase, daoStateService.getChainHeight());
// Because last block in request and voting phases must not be used fo making a tx as it will get confirmed in the
// next block which would be already the next phase we hide that last block to the user and add it to the break.
public int getDurationForPhaseForDisplay(DaoPhase.Phase phase) {
int duration = periodService.getDurationForPhase(phase, daoStateService.getChainHeight());
switch (phase) {
case UNDEFINED:
break;
case PROPOSAL:
duration--;
break;
case BREAK1:
duration++;
break;
case BLIND_VOTE:
duration--;
break;
case BREAK2:
duration++;
break;
case VOTE_REVEAL:
duration--;
break;
case BREAK3:
duration++;
break;
case RESULT:
break;
}
return duration;
}
// listeners for phase change
@ -439,6 +517,10 @@ public class DaoFacade implements DaoSetupService {
return daoStateService.getGenesisTotalSupply();
}
public Set<Issuance> getIssuanceSet() {
return daoStateService.getIssuanceSet();
}
public Set<Tx> getFeeTxs() {
return daoStateService.getBurntFeeTxs();
}

View file

@ -256,9 +256,8 @@ public class MyProposalListService implements PersistedDataHost, DaoStateListene
}
private boolean canRemoveProposal(Proposal proposal, DaoStateService daoStateService, PeriodService periodService) {
return daoStateService.getTx(proposal.getTxId())
.filter(tx -> isTxInProposalPhaseAndCycle(tx, periodService, daoStateService))
.isPresent();
boolean inPhase = periodService.isInPhase(daoStateService.getChainHeight(), DaoPhase.Phase.PROPOSAL);
return isMine(proposal) && inPhase;
}

View file

@ -62,11 +62,10 @@ public class CompensationProposalService extends BaseProposalService<Compensatio
public ProposalWithTransaction createProposalWithTransaction(String name,
String link,
Coin requestedBsq,
String bsqAddress)
Coin requestedBsq)
throws ValidationException, InsufficientMoneyException, TxException {
this.requestedBsq = requestedBsq;
this.bsqAddress = bsqAddress;
this.bsqAddress = bsqWalletService.getUnusedBsqAddressAsString();
return super.createProposalWithTransaction(name, link);
}

View file

@ -72,10 +72,18 @@ public class ChangeParamValidator extends ProposalValidator {
break;
case DEFAULT_TAKER_FEE_BSQ:
break;
case MIN_MAKER_FEE_BSQ:
break;
case MIN_TAKER_FEE_BSQ:
break;
case DEFAULT_MAKER_FEE_BTC:
break;
case DEFAULT_TAKER_FEE_BTC:
break;
case MIN_MAKER_FEE_BTC:
break;
case MIN_TAKER_FEE_BTC:
break;
case PROPOSAL_FEE:
break;

View file

@ -17,14 +17,12 @@
package bisq.core.dao.node;
import bisq.core.dao.DaoOptionKeys;
import bisq.core.dao.node.full.FullNode;
import bisq.core.dao.node.lite.LiteNode;
import bisq.core.user.Preferences;
import com.google.inject.Inject;
import javax.inject.Named;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@ -39,7 +37,14 @@ public class BsqNodeProvider {
@Inject
public BsqNodeProvider(LiteNode bsqLiteNode,
FullNode bsqFullNode,
@Named(DaoOptionKeys.FULL_DAO_NODE) boolean fullDaoNode) {
bsqNode = fullDaoNode ? bsqFullNode : bsqLiteNode;
Preferences preferences) {
boolean rpcDataSet = preferences.getRpcUser() != null && !preferences.getRpcUser().isEmpty()
&& preferences.getRpcPw() != null && !preferences.getRpcPw().isEmpty();
boolean daoFullNode = preferences.isDaoFullNode();
if (daoFullNode && !rpcDataSet)
log.warn("daoFullNode is set but RPC user and pw are missing");
bsqNode = rpcDataSet && daoFullNode ? bsqFullNode : bsqLiteNode;
}
}

View file

@ -17,12 +17,14 @@
package bisq.core.dao.node.full;
import bisq.core.app.BisqEnvironment;
import bisq.core.dao.DaoOptionKeys;
import bisq.core.dao.state.blockchain.PubKeyScript;
import bisq.core.dao.state.blockchain.RawBlock;
import bisq.core.dao.state.blockchain.RawTx;
import bisq.core.dao.state.blockchain.RawTxOutput;
import bisq.core.dao.state.blockchain.TxInput;
import bisq.core.user.Preferences;
import bisq.common.UserThread;
import bisq.common.handlers.ResultHandler;
@ -90,15 +92,23 @@ public class RpcService {
@SuppressWarnings("WeakerAccess")
@Inject
public RpcService(@Named(DaoOptionKeys.RPC_USER) String rpcUser,
@Named(DaoOptionKeys.RPC_PASSWORD) String rpcPassword,
public RpcService(Preferences preferences,
@Named(DaoOptionKeys.RPC_PORT) String rpcPort,
@Named(DaoOptionKeys.RPC_BLOCK_NOTIFICATION_PORT) String rpcBlockPort,
@Named(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) boolean dumpBlockchainData) {
this.rpcUser = rpcUser;
this.rpcPassword = rpcPassword;
this.rpcPort = rpcPort;
this.rpcBlockPort = rpcBlockPort;
this.rpcUser = preferences.getRpcUser();
this.rpcPassword = preferences.getRpcPw();
// mainnet is 8332, testnet 18332, regtest 18443
boolean isPortSet = rpcPort != null && !rpcPort.isEmpty();
boolean isMainnet = BisqEnvironment.getBaseCurrencyNetwork().isMainnet();
boolean isTestnet = BisqEnvironment.getBaseCurrencyNetwork().isTestnet();
this.rpcPort = isPortSet ? rpcPort :
isMainnet ? "8332" :
isTestnet ? "18332" :
"18443"; // regtest
this.rpcBlockPort = rpcBlockPort != null && !rpcBlockPort.isEmpty() ? rpcBlockPort : "5125";
this.dumpBlockchainData = dumpBlockchainData;
}

View file

@ -105,16 +105,21 @@ public enum Param {
PHASE_RESULT(2);
// See: https://github.com/bisq-network/proposals/issues/46
/*
PHASE_UNDEFINED(0),
PHASE_PROPOSAL(3600), // 24 days
PHASE_BREAK1(150), // 1 day
PHASE_BLIND_VOTE(600), // 4 days
PHASE_BREAK2(10), // 10 blocks
PHASE_VOTE_REVEAL(300), // 2 days
PHASE_BREAK3(10), // 10 blocks
PHASE_RESULT(10); // 10 block
*/
// The last block in the proposal and vote phases are not shown to the user as he cannot make a tx there as it would be
// confirmed in the next block which would be the following break phase. To hide that complexity we show only the
// blocks where the user can be active. To have still round numbers for the durations we add 1 block to those
// phases and subtract 1 block from the following breaks.
// So in the UI the user will see 3600 blocks and the last
// block of the technical 3601 blocks is displayed as part of the break1 phase.
/* PHASE_UNDEFINED(0),
PHASE_PROPOSAL(3601), // 24 days
PHASE_BREAK1(149), // 1 day
PHASE_BLIND_VOTE(601), // 4 days
PHASE_BREAK2(9), // 10 blocks
PHASE_VOTE_REVEAL(301), // 2 days
PHASE_BREAK3(9), // 10 blocks
PHASE_RESULT(10); // 10 block*/
@Getter
private long defaultValue;

View file

@ -39,7 +39,8 @@ public class CoreNetworkCapabilities {
supportedCapabilities.add(Capabilities.Capability.BLIND_VOTE.ordinal());
supportedCapabilities.add(Capabilities.Capability.BSQ_BLOCK.ordinal());
if (bisqEnvironment.getProperty(DaoOptionKeys.FULL_DAO_NODE, Boolean.class, false))
String isFullDaoNode = bisqEnvironment.getProperty(DaoOptionKeys.FULL_DAO_NODE, String.class, "");
if (isFullDaoNode != null && !isFullDaoNode.isEmpty())
supportedCapabilities.add(Capabilities.Capability.DAO_FULL_NODE.ordinal());
}

View file

@ -23,6 +23,7 @@ import bisq.core.btc.BaseCurrencyNetwork;
import bisq.core.btc.BtcOptionKeys;
import bisq.core.btc.nodes.BtcNodes;
import bisq.core.btc.wallet.Restrictions;
import bisq.core.dao.DaoOptionKeys;
import bisq.core.locale.Country;
import bisq.core.locale.CountryUtil;
import bisq.core.locale.CryptoCurrency;
@ -139,9 +140,8 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
private final Storage<PreferencesPayload> storage;
private final BisqEnvironment bisqEnvironment;
private final String btcNodesFromOptions;
private final String useTorFlagFromOptions;
private final String referralIdFromOptions;
private final String btcNodesFromOptions, useTorFlagFromOptions, referralIdFromOptions, fullDaoNodeFromOptions,
rpcUserFromOptions, rpcPasswordFromOptions;
@Getter
private final BooleanProperty useStandbyModeProperty = new SimpleBooleanProperty(prefPayload.isUseStandbyMode());
@ -157,13 +157,20 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
BisqEnvironment bisqEnvironment,
@Named(BtcOptionKeys.BTC_NODES) String btcNodesFromOptions,
@Named(BtcOptionKeys.USE_TOR_FOR_BTC) String useTorFlagFromOptions,
@Named(AppOptionKeys.REFERRAL_ID) String referralId) {
@Named(AppOptionKeys.REFERRAL_ID) String referralId,
@Named(DaoOptionKeys.FULL_DAO_NODE) String fullDaoNode,
@Named(DaoOptionKeys.RPC_USER) String rpcUser,
@Named(DaoOptionKeys.RPC_PASSWORD) String rpcPassword) {
this.storage = storage;
this.bisqEnvironment = bisqEnvironment;
this.btcNodesFromOptions = btcNodesFromOptions;
this.useTorFlagFromOptions = useTorFlagFromOptions;
this.referralIdFromOptions = referralId;
this.fullDaoNodeFromOptions = fullDaoNode;
this.rpcUserFromOptions = rpcUser;
this.rpcPasswordFromOptions = rpcPassword;
useAnimationsProperty.addListener((ov) -> {
prefPayload.setUseAnimations(useAnimationsProperty.get());
@ -206,7 +213,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
@Override
public void readPersisted() {
PreferencesPayload persisted = storage.initAndGetPersistedWithFileName("PreferencesPayload", 100);
final BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork();
BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork();
TradeCurrency preferredTradeCurrency;
if (persisted != null) {
prefPayload = persisted;
@ -288,6 +295,15 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
if (referralIdFromOptions != null && !referralIdFromOptions.isEmpty())
setReferralId(referralIdFromOptions);
if (fullDaoNodeFromOptions != null && !fullDaoNodeFromOptions.isEmpty())
setDaoFullNode(fullDaoNodeFromOptions.toLowerCase().equals("true"));
if (rpcUserFromOptions != null && !rpcUserFromOptions.isEmpty())
setRpcUser(rpcUserFromOptions);
if (rpcPasswordFromOptions != null && !rpcPasswordFromOptions.isEmpty())
setRpcPw(rpcPasswordFromOptions);
// For users from old versions the 4 flags a false but we want to have it true by default
// PhoneKeyAndToken is also null so we can use that to enable the flags
if (prefPayload.getPhoneKeyAndToken() == null) {
@ -596,6 +612,22 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
this.useStandbyModeProperty.set(useStandbyMode);
}
public void setDaoFullNode(boolean value) {
prefPayload.setDaoFullNode(value);
persist();
}
public void setRpcUser(String value) {
prefPayload.setRpcUser(value);
persist();
}
public void setRpcPw(String value) {
prefPayload.setRpcPw(value);
persist();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getter
///////////////////////////////////////////////////////////////////////////////////////////
@ -796,5 +828,11 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
long getWithdrawalTxFeeInBytes();
void setUseStandbyMode(boolean useStandbyMode);
void setDaoFullNode(boolean value);
void setRpcUser(String value);
void setRpcPw(String value);
}
}

View file

@ -110,6 +110,11 @@ public final class PreferencesPayload implements PersistableEnvelope {
boolean useMarketNotifications = true;
boolean usePriceNotifications = true;
boolean useStandbyMode = false;
boolean isDaoFullNode = false;
@Nullable
String rpcUser;
@Nullable
String rpcPw;
///////////////////////////////////////////////////////////////////////////////////////////
@ -164,7 +169,8 @@ public final class PreferencesPayload implements PersistableEnvelope {
.setUseTradeNotifications(useTradeNotifications)
.setUseMarketNotifications(useMarketNotifications)
.setUsePriceNotifications(usePriceNotifications)
.setUseStandbyMode(useStandbyMode);
.setUseStandbyMode(useStandbyMode)
.setIsDaoFullNode(isDaoFullNode);
Optional.ofNullable(backupDirectory).ifPresent(builder::setBackupDirectory);
Optional.ofNullable(preferredTradeCurrency).ifPresent(e -> builder.setPreferredTradeCurrency((PB.TradeCurrency) e.toProtoMessage()));
Optional.ofNullable(offerBookChartScreenCurrencyCode).ifPresent(builder::setOfferBookChartScreenCurrencyCode);
@ -177,6 +183,8 @@ public final class PreferencesPayload implements PersistableEnvelope {
Optional.ofNullable(customBridges).ifPresent(builder::setCustomBridges);
Optional.ofNullable(referralId).ifPresent(builder::setReferralId);
Optional.ofNullable(phoneKeyAndToken).ifPresent(builder::setPhoneKeyAndToken);
Optional.ofNullable(rpcUser).ifPresent(builder::setRpcUser);
Optional.ofNullable(rpcPw).ifPresent(builder::setRpcPw);
return PB.PersistableEnvelope.newBuilder().setPreferencesPayload(builder).build();
}
@ -238,6 +246,9 @@ public final class PreferencesPayload implements PersistableEnvelope {
proto.getUseTradeNotifications(),
proto.getUseMarketNotifications(),
proto.getUsePriceNotifications(),
proto.getUseStandbyMode());
proto.getUseStandbyMode(),
proto.getIsDaoFullNode(),
proto.getRpcUser().isEmpty() ? null : proto.getRpcUser(),
proto.getRpcPw().isEmpty() ? null : proto.getRpcPw());
}
}

View file

@ -98,8 +98,15 @@ public class BSFormatter {
return formatCoin(coin, decimalPlaces, false, 0);
}
public String formatCoin(long value, MonetaryFormat coinFormat) {
return formatCoin(Coin.valueOf(value), -1, false, 0, coinFormat);
}
public String formatCoin(Coin coin, int decimalPlaces, boolean decimalAligned, int maxNumberOfDigits) {
return formatCoin(coin, decimalPlaces, decimalAligned, maxNumberOfDigits, coinFormat);
}
public String formatCoin(Coin coin, int decimalPlaces, boolean decimalAligned, int maxNumberOfDigits, MonetaryFormat coinFormat) {
String formattedCoin = "";
if (coin != null) {
@ -122,6 +129,14 @@ public class BSFormatter {
}
public String formatCoinWithCode(Coin coin) {
return formatCoinWithCode(coin, coinFormat);
}
public String formatCoinWithCode(long value, MonetaryFormat coinFormat) {
return formatCoinWithCode(Coin.valueOf(value), coinFormat);
}
public String formatCoinWithCode(Coin coin, MonetaryFormat coinFormat) {
if (coin != null) {
try {
// we don't use the code feature from coinFormat as it does automatic switching between mBTC and BTC and
@ -137,6 +152,10 @@ public class BSFormatter {
}
public Coin parseToCoin(String input) {
return parseToCoin(input, coinFormat);
}
public Coin parseToCoin(String input, MonetaryFormat coinFormat) {
if (input != null && input.length() > 0) {
try {
return coinFormat.parse(cleanDoubleInput(input));
@ -458,9 +477,13 @@ public class BSFormatter {
}
public String formatDateTime(Date date) {
return formatDateTime(date,
DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale()),
DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale()));
}
public String formatDateTime(Date date, DateFormat dateFormatter, DateFormat timeFormatter) {
if (date != null) {
DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale());
DateFormat timeFormatter = DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale());
return dateFormatter.format(date) + " " + timeFormatter.format(date);
} else {
return "";
@ -564,10 +587,10 @@ public class BSFormatter {
}
public String formatDurationAsWords(long durationMillis) {
return formatDurationAsWords(durationMillis, false);
return formatDurationAsWords(durationMillis, false, true);
}
public static String formatDurationAsWords(long durationMillis, boolean showSeconds) {
public String formatDurationAsWords(long durationMillis, boolean showSeconds, boolean showZeroValues) {
String format = "";
String second = Res.get("time.second");
String minute = Res.get("time.minute");
@ -593,10 +616,19 @@ public class BSFormatter {
duration = StringUtils.replacePattern(duration, "^1 " + minutes + "|\\b1 " + minutes, "1 " + minute);
duration = StringUtils.replacePattern(duration, "^1 " + hours + "|\\b1 " + hours, "1 " + hour);
duration = StringUtils.replacePattern(duration, "^1 " + days + "|\\b1 " + days, "1 " + day);
if (!showZeroValues) {
duration = duration.replace(", 0 seconds", "");
duration = duration.replace(", 0 minutes", "");
duration = duration.replace(", 0 hours", "");
duration = duration.replace("0 days", "");
duration = duration.replace("0 hours, ", "");
duration = duration.replace("0 minutes, ", "");
duration = duration.replace("0 seconds", "");
}
return duration.trim();
}
public String booleanToYesNo(boolean value) {
return value ? Res.get("shared.yes") : Res.get("shared.no");
}

View file

@ -43,11 +43,14 @@ public class BsqFormatter extends BSFormatter {
private final String prefix = "B";
private final DecimalFormat amountFormat = new DecimalFormat("###,###,###.##");
private final DecimalFormat marketCapFormat = new DecimalFormat("###,###,###");
private final MonetaryFormat btcCoinFormat;
@Inject
private BsqFormatter() {
super();
btcCoinFormat = super.coinFormat;
final String baseCurrencyCode = BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode();
switch (baseCurrencyCode) {
case "BTC":
@ -104,20 +107,14 @@ public class BsqFormatter extends BSFormatter {
}
}
public String formatBtcSatoshi(long satoshi) {
return satoshi + " BTC Satoshi";
public String formatBTCWithCode(long satoshi) {
return super.formatCoinWithCode(satoshi, btcCoinFormat);
}
public Coin parseSatoshiToBtc(String satoshi) {
try {
return Coin.valueOf(Long.valueOf(satoshi));
} catch (Throwable e) {
return Coin.ZERO;
}
public Coin parseToBTC(String input) {
return super.parseToCoin(input, btcCoinFormat);
}
public String formatParamValue(Param param, long value) {
switch (param) {
case UNDEFINED:
@ -125,8 +122,12 @@ public class BsqFormatter extends BSFormatter {
case DEFAULT_MAKER_FEE_BSQ:
case DEFAULT_TAKER_FEE_BSQ:
case MIN_MAKER_FEE_BSQ:
case MIN_TAKER_FEE_BSQ:
case DEFAULT_MAKER_FEE_BTC:
case DEFAULT_TAKER_FEE_BTC:
case MIN_MAKER_FEE_BTC:
case MIN_TAKER_FEE_BTC:
return formatToPercentWithSymbol(value / 10000d);
case PROPOSAL_FEE:

View file

@ -859,6 +859,13 @@ setting.preferences.daoOptions=DAO options
setting.preferences.dao.resync.label=Rebuild DAO state from genesis tx:
setting.preferences.dao.resync.button=Resync
setting.preferences.dao.resync.popup=After an application restart the BSQ consensus state will be rebuilt from the genesis transaction.
setting.preferences.dao.isDaoFullNode=Run Bisq as DAO full node
setting.preferences.dao.rpcUser=RPC username
setting.preferences.dao.rpcPw=RPC password
setting.preferences.dao.fullNodeInfo=For running Bisq as DAO full node you need to have Bitcoin Core locally running \
and configured with RPC and other requirements which are documented in ''{0}''.
setting.preferences.dao.fullNodeInfo.ok=Open docs page
setting.preferences.dao.fullNodeInfo.cancel=No, I stick with lite node mode
settings.net.btcHeader=Bitcoin network
settings.net.p2pHeader=P2P network
@ -1147,10 +1154,10 @@ dao.tab.proposals=Governance
dao.tab.bonding=Bonding
dao.paidWithBsq=paid with BSQ
dao.availableBsqBalance=Available BSQ balance
dao.availableNonBsqBalance=Available non-BSQ balance
dao.unverifiedBsqBalance=Unverified BSQ balance
dao.lockedForVoteBalance=Locked for voting
dao.availableBsqBalance=Available
dao.availableNonBsqBalance=Available non-BSQ balance (BTC)
dao.unverifiedBsqBalance=Unverified (awaiting block confirmation)
dao.lockedForVoteBalance=Used for voting
dao.lockedInBonds=Locked in bonds
dao.totalBsqBalance=Total BSQ balance
@ -1167,7 +1174,7 @@ dao.cycle.proposal=Proposal phase:
dao.cycle.blindVote=Blind vote phase:
dao.cycle.voteReveal=Vote reveal phase:
dao.cycle.voteResult=Vote result:
dao.cycle.phaseDuration=Block: {0} - {1} ({2} - {3})
dao.cycle.phaseDuration={0} blocks (≈{1}); Block {2} - {3} (≈{4} - ≈{5})
dao.cycle.info.headline=Information
dao.cycle.info.details=Please note:\n\
@ -1282,8 +1289,6 @@ dao.phase.PHASE_RESULT=Result phase
dao.results.votes.table.header.stakeAndMerit=Vote weight
dao.results.votes.table.header.stake=Stake
dao.results.votes.table.header.merit=Earned
dao.results.votes.table.header.blindVoteTxId=Blind vote Tx ID
dao.results.votes.table.header.voteRevealTxId=Vote reveal Tx ID
dao.results.votes.table.header.vote=Vote
dao.bonding.menuItem.bondedRoles=Bonded roles
@ -1399,6 +1404,9 @@ dao.proposal.type.short.CONFISCATE_BOND=Confiscating a bond
dao.proposal.details=Proposal details
dao.proposal.selectedProposal=Selected proposal
dao.proposal.active.header=Proposals of current cycle
dao.proposal.active.remove.confirm=Are you sure you want to remove that proposal?\n\
The already paid proposal fee will be lost.
dao.proposal.active.remove.doRemove=Yes, remove my proposal
dao.proposal.active.remove.failed=Could not remove proposal.
dao.proposal.myVote.accept=Accept proposal
dao.proposal.myVote.reject=Reject proposal
@ -1407,7 +1415,7 @@ dao.proposal.myVote.merit=Vote weight from earned BSQ
dao.proposal.myVote.stake=Vote weight from stake
dao.proposal.myVote.blindVoteTxId=Blind vote transaction ID
dao.proposal.myVote.revealTxId=Vote reveal transaction ID
dao.proposal.myVote.stake.prompt=Available balance for voting: {0}
dao.proposal.myVote.stake.prompt=Max. available balance for voting: {0}
dao.proposal.votes.header=Vote on all proposals
dao.proposal.votes.header.voted=My vote
dao.proposal.myVote.button=Vote on all proposals
@ -1426,11 +1434,14 @@ dao.proposal.display.txId=Proposal transaction ID:
dao.proposal.display.proposalFee=Proposal fee:
dao.proposal.display.myVote=My vote:
dao.proposal.display.voteResult=Vote result summary:
dao.proposal.display.bondedRoleComboBox.label=Choose bonded role type
dao.proposal.display.bondedRoleComboBox.label=Bonded role type
dao.proposal.display.requiredBondForRole.label=Required bond for role
dao.proposal.display.tickerSymbol.label=Ticker Symbol
dao.proposal.table.header.proposalType=Proposal type
dao.proposal.table.header.link=Link
dao.proposal.table.icon.tooltip.removeProposal=Remove my proposal
dao.proposal.table.icon.tooltip.changeVote=Current vote: ''{0}''. Change vote to: ''{1}''
dao.proposal.display.myVote.accepted=Accepted
dao.proposal.display.myVote.rejected=Rejected
@ -1441,7 +1452,7 @@ dao.proposal.voteResult.success=Accepted
dao.proposal.voteResult.failed=Rejected
dao.proposal.voteResult.summary=Result: {0}; Threshold: {1} (required > {2}); Quorum: {3} (required > {4})
dao.proposal.display.paramComboBox.label=Choose parameter
dao.proposal.display.paramComboBox.label=Parameter
dao.proposal.display.paramValue=Parameter value:
dao.proposal.display.confiscateBondComboBox.label=Choose bond
@ -1456,31 +1467,37 @@ dao.wallet.menuItem.send=Send
dao.wallet.menuItem.receive=Receive
dao.wallet.menuItem.transactions=Transactions
dao.wallet.dashboard.statistics=Statistics
dao.wallet.dashboard.myBalance=My wallet balance
dao.wallet.dashboard.distribution=Distribution of all BSQ
dao.wallet.dashboard.locked=Global state of locked BSQ
dao.wallet.dashboard.market=Market data
dao.wallet.dashboard.txDetails=BSQ transactions details
dao.wallet.dashboard.genesisBlockHeight=Genesis block height:
dao.wallet.dashboard.genesisTxId=Genesis transaction ID:
dao.wallet.dashboard.genesisIssueAmount=Issued amount at genesis transaction:
dao.wallet.dashboard.compRequestIssueAmount=Issued amount from compensation requests:
dao.wallet.dashboard.availableAmount=Total available amount:
dao.wallet.dashboard.burntAmount=Amount of burned BSQ (fees):
dao.wallet.dashboard.totalLockedUpAmount=Amount of locked up BSQ (bonds):
dao.wallet.dashboard.totalUnlockingAmount=Amount of unlocking BSQ (bonds):
dao.wallet.dashboard.totalUnlockedAmount=Amount of unlocked BSQ (bonds):
dao.wallet.dashboard.genesisIssueAmount=BSQ issued at genesis transaction:
dao.wallet.dashboard.compRequestIssueAmount=BSQ issued for compensation requests:
dao.wallet.dashboard.availableAmount=Total available BSQ:
dao.wallet.dashboard.burntAmount=Burned BSQ (fees):
dao.wallet.dashboard.totalLockedUpAmount=Locked up in bonds:
dao.wallet.dashboard.totalUnlockingAmount=Unlocking BSQ from bonds:
dao.wallet.dashboard.totalUnlockedAmount=Unlocked BSQ from bonds:
dao.wallet.dashboard.allTx=No. of all BSQ transactions:
dao.wallet.dashboard.utxo=No. of all unspent transaction outputs:
dao.wallet.dashboard.burntTx=No. of all fee payments transactions (burnt):
dao.wallet.dashboard.price=Price:
dao.wallet.dashboard.marketCap=Market capitalisation:
dao.wallet.dashboard.issuanceTx=No. of all issuance transactions:
dao.wallet.dashboard.burntTx=No. of all fee payments transactions:
dao.wallet.dashboard.price=Latest BSQ/BTC trade price (in Bisq):
dao.wallet.dashboard.marketCap=Market capitalisation (based on trade price):
dao.wallet.receive.fundBSQWallet=Fund Bisq BSQ wallet
dao.wallet.receive.fundYourWallet=Fund your BSQ wallet
dao.wallet.receive.bsqAddress=BSQ address
dao.wallet.send.sendFunds=Send funds
dao.wallet.send.sendBtcFunds=Send non-BSQ funds
dao.wallet.send.sendBtcFunds=Send non-BSQ funds (BTC)
dao.wallet.send.amount=Amount in BSQ:
dao.wallet.send.btcAmount=Amount in BTC Satoshi:
dao.wallet.send.btcAmount=Amount in BTC (non-BSQ funds):
dao.wallet.send.setAmount=Set amount to withdraw (min. amount is {0})
dao.wallet.send.setBtcAmount=Set amount in BTC Satoshi to withdraw (min. amount is {0} Satoshi)
dao.wallet.send.setBtcAmount=Set amount in BTC to withdraw (min. amount is {0})
dao.wallet.send.receiverAddress=Receiver's BSQ address:
dao.wallet.send.receiverBtcAddress=Receiver's BTC address:
dao.wallet.send.setDestinationAddress=Fill in your destination address

View file

@ -1291,7 +1291,7 @@ dao.wallet.menuItem.send=Senden
dao.wallet.menuItem.receive=Empfangen
dao.wallet.menuItem.transactions=Transaktionen
dao.wallet.dashboard.statistics=Statistiken
dao.wallet.dashboard.distribution=Statistiken
dao.wallet.dashboard.genesisBlockHeight=Ursprungsblock-Höhe:
dao.wallet.dashboard.genesisTxId=Ursprungstransaktion-ID:
dao.wallet.dashboard.genesisIssueAmount=Ausgestellter Betrag in Ursprungstransaktion:

View file

@ -1291,7 +1291,7 @@ dao.wallet.menuItem.send=Αποστολή
dao.wallet.menuItem.receive=Λήψη
dao.wallet.menuItem.transactions=Συναλλαγές
dao.wallet.dashboard.statistics=Στατιστικά
dao.wallet.dashboard.distribution=Στατιστικά
dao.wallet.dashboard.genesisBlockHeight=Ύψος μπλοκ Genesis:
dao.wallet.dashboard.genesisTxId=Ταυτότητα συναλλαγής genesis:
dao.wallet.dashboard.genesisIssueAmount=Issued amount at genesis transaction:

View file

@ -1291,7 +1291,7 @@ dao.wallet.menuItem.send=Enviar
dao.wallet.menuItem.receive=Recibir
dao.wallet.menuItem.transactions=Transacciones
dao.wallet.dashboard.statistics=Estadísticas
dao.wallet.dashboard.distribution=Estadísticas
dao.wallet.dashboard.genesisBlockHeight=Altura de bloque génesis:
dao.wallet.dashboard.genesisTxId=ID de transacción génesis:
dao.wallet.dashboard.genesisIssueAmount=Issued amount at genesis transaction:

View file

@ -1291,7 +1291,7 @@ dao.wallet.menuItem.send=ارسال
dao.wallet.menuItem.receive=دریافت
dao.wallet.menuItem.transactions=تراکنش ها
dao.wallet.dashboard.statistics=آمار
dao.wallet.dashboard.distribution=آمار
dao.wallet.dashboard.genesisBlockHeight=ارتفاع جنسیس بلاک:
dao.wallet.dashboard.genesisTxId=شناسه تراکنش جنسیس:
dao.wallet.dashboard.genesisIssueAmount=Issued amount at genesis transaction:

View file

@ -1033,7 +1033,7 @@ dao.wallet.menuItem.send=Envoyer
dao.wallet.menuItem.receive=Recevoir
dao.wallet.menuItem.transactions=Transactions
dao.wallet.dashboard.statistics=Statistics
dao.wallet.dashboard.distribution=Statistics
dao.wallet.dashboard.genesisBlockHeight=Genesis block height:
dao.wallet.dashboard.genesisTxId=Genesis transaction ID:
dao.wallet.dashboard.issuedAmount=Total issued amount:

View file

@ -1291,7 +1291,7 @@ dao.wallet.menuItem.send=Küldés
dao.wallet.menuItem.receive=Fogadd:
dao.wallet.menuItem.transactions=Tranzakciók
dao.wallet.dashboard.statistics=Statisztika
dao.wallet.dashboard.distribution=Statisztika
dao.wallet.dashboard.genesisBlockHeight=Genesis blokk magassága:
dao.wallet.dashboard.genesisTxId=Genesis tranzakció azonosítója:
dao.wallet.dashboard.genesisIssueAmount=Issued amount at genesis transaction:

View file

@ -1291,7 +1291,7 @@ dao.wallet.menuItem.send=Enviar
dao.wallet.menuItem.receive=Receber
dao.wallet.menuItem.transactions=Transações
dao.wallet.dashboard.statistics=Estatísticas
dao.wallet.dashboard.distribution=Estatísticas
dao.wallet.dashboard.genesisBlockHeight=Altura do bloco gênese:
dao.wallet.dashboard.genesisTxId=ID da transação gênese:
dao.wallet.dashboard.genesisIssueAmount=Issued amount at genesis transaction:

View file

@ -1291,7 +1291,7 @@ dao.wallet.menuItem.send=Trimite
dao.wallet.menuItem.receive=Încasează
dao.wallet.menuItem.transactions=Tranzacții
dao.wallet.dashboard.statistics=Statistici
dao.wallet.dashboard.distribution=Statistici
dao.wallet.dashboard.genesisBlockHeight=Înălțimea blocului geneză:
dao.wallet.dashboard.genesisTxId=Codul tranzacției geneză:
dao.wallet.dashboard.genesisIssueAmount=Issued amount at genesis transaction:

View file

@ -1291,7 +1291,7 @@ dao.wallet.menuItem.send=Послать
dao.wallet.menuItem.receive=Получить
dao.wallet.menuItem.transactions=Транзакции
dao.wallet.dashboard.statistics=Statistics
dao.wallet.dashboard.distribution=Statistics
dao.wallet.dashboard.genesisBlockHeight=Номер исходного блока:
dao.wallet.dashboard.genesisTxId=Идентификатор зачисления на счёт
dao.wallet.dashboard.genesisIssueAmount=Сумма выпущенная в исходной транзакции:

View file

@ -1291,7 +1291,7 @@ dao.wallet.menuItem.send=Pošalji
dao.wallet.menuItem.receive=Primi
dao.wallet.menuItem.transactions=Transakcije
dao.wallet.dashboard.statistics=Statistics
dao.wallet.dashboard.distribution=Statistics
dao.wallet.dashboard.genesisBlockHeight=Genesis block height:
dao.wallet.dashboard.genesisTxId=Genesis transaction ID:
dao.wallet.dashboard.genesisIssueAmount=Issued amount at genesis transaction:

View file

@ -1291,7 +1291,7 @@ dao.wallet.menuItem.send=ส่ง
dao.wallet.menuItem.receive=รับ
dao.wallet.menuItem.transactions=การทำธุรกรรม
dao.wallet.dashboard.statistics=สถิติ
dao.wallet.dashboard.distribution=สถิติ
dao.wallet.dashboard.genesisBlockHeight=ความสูงของบ็อกต้นกำเนิด (Genesis block):
dao.wallet.dashboard.genesisTxId=ID การทำธุรกรรมต้นกำเนิด:
dao.wallet.dashboard.genesisIssueAmount=Issued amount at genesis transaction:

View file

@ -1291,7 +1291,7 @@ dao.wallet.menuItem.send=Gửi
dao.wallet.menuItem.receive=Nhận
dao.wallet.menuItem.transactions=Giao dịch
dao.wallet.dashboard.statistics=Số liệu thống kê
dao.wallet.dashboard.distribution=Số liệu thống kê
dao.wallet.dashboard.genesisBlockHeight=Chiều cao block ban đầu:
dao.wallet.dashboard.genesisTxId=ID giao dịch ban đầu:
dao.wallet.dashboard.genesisIssueAmount=Issued amount at genesis transaction:

View file

@ -1291,7 +1291,7 @@ dao.wallet.menuItem.send=发送
dao.wallet.menuItem.receive=接收
dao.wallet.menuItem.transactions=交易记录
dao.wallet.dashboard.statistics=统计
dao.wallet.dashboard.distribution=统计
dao.wallet.dashboard.genesisBlockHeight=创始块高度:
dao.wallet.dashboard.genesisTxId=创始交易ID:
dao.wallet.dashboard.genesisIssueAmount=Issued amount at genesis transaction:

View file

@ -69,7 +69,7 @@ public class PreferencesTest {
storage = mock(Storage.class);
bisqEnvironment = mock(BisqEnvironment.class);
preferences = new Preferences(storage, bisqEnvironment, null, null, null);
preferences = new Preferences(storage, bisqEnvironment, null, null, null, null, null, null);
}
@Test

View file

@ -17,11 +17,12 @@
package bisq.desktop.components;
import bisq.desktop.util.FormBuilder;
import bisq.core.locale.Res;
import bisq.common.UserThread;
import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon;
import javafx.scene.Parent;
@ -61,7 +62,7 @@ public class InfoDisplay extends Parent {
private boolean useReadMore;
private final Label icon = AwesomeDude.createIconLabel(AwesomeIcon.INFO_SIGN);
private final Label icon = FormBuilder.getIcon(AwesomeIcon.INFO_SIGN);
private final TextFlow textFlow;
private final Label label;
private final Hyperlink link;

View file

@ -156,6 +156,13 @@ public class TxIdTextField extends AnchorPane {
txConfidenceIndicator.setVisible(true);
AnchorPane.setRightAnchor(txConfidenceIndicator, 0.0);
}
} else {
//TODO we should show some placeholder in case of a tx which we are not aware of but which can be
// confirmed already. This is for instance the case of the other peers trade fee tx, as it is not related
// to our wallet we don't have a confidence object but we should show that it is in a unknown state instead
// of not showing anything which causes confusion that the tx was not broadcasted. Best would be to request
// it from a block explorer service but that is a bit too heavy for that use case...
// Maybe a question mark with a tooltip explaining why we don't know about the confidence might be ok...
}
}
}

View file

@ -126,9 +126,9 @@ public class PhasesView implements DaoStateListener {
private void applyData(int height) {
if (height > 0) {
phaseBarsItems.forEach(item -> {
int firstBlock = daoFacade.getFirstBlockOfPhase(height, item.getPhase());
int lastBlock = daoFacade.getLastBlockOfPhase(height, item.getPhase());
final int duration = daoFacade.getDurationForPhase(item.getPhase());
int firstBlock = daoFacade.getFirstBlockOfPhaseForDisplay(height, item.getPhase());
int lastBlock = daoFacade.getLastBlockOfPhaseForDisplay(height, item.getPhase());
int duration = daoFacade.getDurationForPhaseForDisplay(item.getPhase());
item.setPeriodRange(firstBlock, lastBlock, duration);
double progress = 0;
if (height >= firstBlock && height <= lastBlock) {

View file

@ -19,15 +19,13 @@ package bisq.desktop.main.dao.governance;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.InputTextField;
import bisq.desktop.components.TxIdTextField;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.GUIUtil;
import bisq.desktop.util.Layout;
import bisq.desktop.util.validation.BsqAddressValidator;
import bisq.desktop.util.validation.BsqValidator;
import bisq.core.btc.BaseCurrencyNetwork;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.governance.ballot.Ballot;
import bisq.core.dao.governance.ballot.vote.Vote;
@ -65,6 +63,7 @@ import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.beans.value.ChangeListener;
@ -83,7 +82,10 @@ import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
import static bisq.desktop.util.FormBuilder.*;
import static bisq.desktop.util.FormBuilder.addLabelHyperlinkWithIcon;
import static bisq.desktop.util.FormBuilder.addLabelInputTextField;
import static bisq.desktop.util.FormBuilder.addLabelTextField;
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
import static com.google.common.base.Preconditions.checkNotNull;
@ -95,17 +97,16 @@ import bisq.asset.Asset;
public class ProposalDisplay {
private final GridPane gridPane;
private final BsqFormatter bsqFormatter;
private final BsqWalletService bsqWalletService;
private final DaoFacade daoFacade;
@Nullable
private TextField uidTextField, proposalFeeTextField;
private TextField proposalFeeTextField, comboBoxValueTextField, requiredBondForRoleTextField;
private TextField proposalTypeTextField, myVoteTextField, voteResultTextField;
private Label myVoteLabel, voteResultLabel;
public InputTextField nameTextField;
public InputTextField linkInputTextField;
@Nullable
public InputTextField requestedBsqTextField, bsqAddressTextField, paramValueTextField;
public InputTextField requestedBsqTextField, paramValueTextField;
@Nullable
public ComboBox<Param> paramComboBox;
@Nullable
@ -118,8 +119,6 @@ public class ProposalDisplay {
@Getter
private int gridRow;
private HyperlinkWithIcon linkHyperlinkWithIcon;
@Nullable
private TxIdTextField txIdTextField;
private int gridRowStartIndex;
private final List<Runnable> inputChangedListeners = new ArrayList<>();
@Getter
@ -129,12 +128,13 @@ public class ProposalDisplay {
private final ChangeListener<Boolean> focusOutListener;
private final ChangeListener<Object> inputListener;
private ChangeListener<Param> paramChangeListener;
private ChangeListener<BondedRoleType> requiredBondForRoleListener;
private TitledGroupBg titledGroupBg;
private int titledGroupBgRowSpan;
public ProposalDisplay(GridPane gridPane, BsqFormatter bsqFormatter, BsqWalletService bsqWalletService,
DaoFacade daoFacade) {
public ProposalDisplay(GridPane gridPane, BsqFormatter bsqFormatter, DaoFacade daoFacade) {
this.gridPane = gridPane;
this.bsqFormatter = bsqFormatter;
this.bsqWalletService = bsqWalletService;
this.daoFacade = daoFacade;
// focusOutListener = observable -> inputChangedListeners.forEach(Runnable::run);
@ -159,41 +159,31 @@ public class ProposalDisplay {
removeAllFields();
this.gridRowStartIndex = gridRowStartIndex;
this.gridRow = gridRowStartIndex;
int titledGroupBgRowSpan = 6;
titledGroupBgRowSpan = 5;
switch (proposalType) {
case COMPENSATION_REQUEST:
titledGroupBgRowSpan += 1;
break;
case CHANGE_PARAM:
titledGroupBgRowSpan += 1;
titledGroupBgRowSpan = 6;
break;
case BONDED_ROLE:
titledGroupBgRowSpan = 6;
break;
case CONFISCATE_BOND:
break;
case GENERIC:
titledGroupBgRowSpan -= 1;
titledGroupBgRowSpan = 4;
break;
case REMOVE_ASSET:
break;
}
// at isMakeProposalScreen we show fee but no uid and txID (+1)
// otherwise we don't show fee but show uid and txID (+2)
if (isMakeProposalScreen)
titledGroupBgRowSpan += 1;
else
titledGroupBgRowSpan += 2;
addTitledGroupBg(gridPane, gridRow, titledGroupBgRowSpan, title, top);
titledGroupBg = addTitledGroupBg(gridPane, gridRow, titledGroupBgRowSpan, title, top);
double proposalTypeTop = top == Layout.GROUP_DISTANCE ? Layout.FIRST_ROW_AND_GROUP_DISTANCE : Layout.FIRST_ROW_DISTANCE;
proposalTypeTextField = addLabelTextField(gridPane, gridRow,
Res.getWithCol("dao.proposal.display.type"), proposalType.getDisplayName(), proposalTypeTop).second;
if (!isMakeProposalScreen)
uidTextField = addLabelTextField(gridPane, ++gridRow, Res.getWithCol("shared.id")).second;
nameTextField = addLabelInputTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.name")).second;
nameTextField.setValidator(new InputValidator());
inputControls.add(nameTextField);
@ -210,6 +200,7 @@ public class ProposalDisplay {
linkHyperlinkWithIcon.setVisible(false);
linkHyperlinkWithIcon.setManaged(false);
int comboBoxValueTextFieldIndex = -1;
switch (proposalType) {
case COMPENSATION_REQUEST:
requestedBsqTextField = addLabelInputTextField(gridPane, ++gridRow,
@ -220,19 +211,12 @@ public class ProposalDisplay {
checkNotNull(requestedBsqTextField, "requestedBsqTextField must not be null");
requestedBsqTextField.setValidator(bsqValidator);
inputControls.add(requestedBsqTextField);
// TODO validator, addressTF
bsqAddressTextField = addLabelInputTextField(gridPane, ++gridRow,
Res.get("dao.proposal.display.bsqAddress")).second;
checkNotNull(bsqAddressTextField, "bsqAddressTextField must not be null");
bsqAddressTextField.setText("B" + bsqWalletService.getUnusedAddress().toBase58());
bsqAddressTextField.setValidator(new BsqAddressValidator(bsqFormatter));
inputControls.add(bsqAddressTextField);
break;
case CHANGE_PARAM:
checkNotNull(gridPane, "gridPane must not be null");
paramComboBox = FormBuilder.<Param>addLabelComboBox(gridPane, ++gridRow,
Res.getWithCol("dao.proposal.display.paramComboBox.label")).second;
comboBoxValueTextFieldIndex = gridRow;
checkNotNull(paramComboBox, "paramComboBox must not be null");
List<Param> list = Arrays.stream(Param.values())
.filter(e -> e != Param.UNDEFINED && e != Param.PHASE_UNDEFINED)
@ -252,7 +236,6 @@ public class ProposalDisplay {
comboBoxes.add(paramComboBox);
paramValueTextField = addLabelInputTextField(gridPane, ++gridRow,
Res.get("dao.proposal.display.paramValue")).second;
//noinspection ConstantConditions
//TODO use custom param validator
paramValueTextField.setValidator(new InputValidator());
@ -271,6 +254,7 @@ public class ProposalDisplay {
case BONDED_ROLE:
bondedRoleTypeComboBox = FormBuilder.<BondedRoleType>addLabelComboBox(gridPane, ++gridRow,
Res.getWithCol("dao.proposal.display.bondedRoleComboBox.label")).second;
comboBoxValueTextFieldIndex = gridRow;
checkNotNull(bondedRoleTypeComboBox, "bondedRoleTypeComboBox must not be null");
bondedRoleTypeComboBox.setItems(FXCollections.observableArrayList(BondedRoleType.values()));
bondedRoleTypeComboBox.setConverter(new StringConverter<>() {
@ -285,10 +269,21 @@ public class ProposalDisplay {
}
});
comboBoxes.add(bondedRoleTypeComboBox);
requiredBondForRoleTextField = FormBuilder.addLabelTextField(gridPane, ++gridRow,
Res.getWithCol("dao.proposal.display.requiredBondForRole.label")).second;
requiredBondForRoleListener = (observable, oldValue, newValue) -> {
if (newValue != null) {
requiredBondForRoleTextField.setText(bsqFormatter.formatCoinWithCode(Coin.valueOf(newValue.getRequiredBond())));
}
};
bondedRoleTypeComboBox.getSelectionModel().selectedItemProperty().addListener(requiredBondForRoleListener);
break;
case CONFISCATE_BOND:
confiscateBondComboBox = FormBuilder.<BondedRole>addLabelComboBox(gridPane, ++gridRow,
Res.getWithCol("dao.proposal.display.confiscateBondComboBox.label")).second;
comboBoxValueTextFieldIndex = gridRow;
checkNotNull(confiscateBondComboBox, "confiscateBondComboBox must not be null");
confiscateBondComboBox.setItems(FXCollections.observableArrayList(daoFacade.getValidBondedRoleList()));
confiscateBondComboBox.setConverter(new StringConverter<>() {
@ -309,6 +304,7 @@ public class ProposalDisplay {
case REMOVE_ASSET:
assetComboBox = FormBuilder.<Asset>addLabelComboBox(gridPane, ++gridRow,
Res.getWithCol("dao.proposal.display.assetComboBox.label")).second;
comboBoxValueTextFieldIndex = gridRow;
checkNotNull(assetComboBox, "assetComboBox must not be null");
List<Asset> assetList = CurrencyUtil.getAssetRegistry().stream()
.filter(e -> !e.getTickerSymbol().equals("BSQ"))
@ -331,10 +327,17 @@ public class ProposalDisplay {
break;
}
if (!isMakeProposalScreen) {
txIdTextField = addLabelTxIdTextField(gridPane, ++gridRow,
Res.get("dao.proposal.display.txId"), "").second;
txIdTextField.setBsq(true);
if (comboBoxValueTextFieldIndex > -1) {
comboBoxValueTextField = new TextField("");
comboBoxValueTextField.setEditable(false);
comboBoxValueTextField.setMouseTransparent(true);
comboBoxValueTextField.setFocusTraversable(false);
comboBoxValueTextField.setVisible(false);
comboBoxValueTextField.setManaged(false);
GridPane.setRowIndex(comboBoxValueTextField, comboBoxValueTextFieldIndex);
GridPane.setColumnIndex(comboBoxValueTextField, 1);
GridPane.setMargin(comboBoxValueTextField, new Insets(top, 0, 0, 0));
gridPane.getChildren().add(comboBoxValueTextField);
}
if (isMakeProposalScreen) {
@ -379,6 +382,8 @@ public class ProposalDisplay {
}
public void applyEvaluatedProposal(@Nullable EvaluatedProposal evaluatedProposal) {
GridPane.setRowSpan(titledGroupBg, titledGroupBgRowSpan + 1);
boolean isEvaluatedProposalNotNull = evaluatedProposal != null;
if (isEvaluatedProposalNotNull) {
String result = evaluatedProposal.isAccepted() ? Res.get("dao.proposal.voteResult.success") :
@ -426,26 +431,25 @@ public class ProposalDisplay {
public void applyProposalPayload(Proposal proposal) {
proposalTypeTextField.setText(proposal.getType().getDisplayName());
if (uidTextField != null)
uidTextField.setText(proposal.getTxId());
nameTextField.setText(proposal.getName());
linkInputTextField.setVisible(false);
linkInputTextField.setManaged(false);
linkHyperlinkWithIcon.setVisible(true);
linkHyperlinkWithIcon.setManaged(true);
linkHyperlinkWithIcon.setText(proposal.getLink());
linkHyperlinkWithIcon.setOnAction(e -> GUIUtil.openWebPage(proposal.getLink()));
if (linkHyperlinkWithIcon != null) {
linkHyperlinkWithIcon.setVisible(true);
linkHyperlinkWithIcon.setManaged(true);
linkHyperlinkWithIcon.setText(proposal.getLink());
linkHyperlinkWithIcon.setOnAction(e -> GUIUtil.openWebPage(proposal.getLink()));
}
if (proposal instanceof CompensationProposal) {
CompensationProposal compensationProposal = (CompensationProposal) proposal;
checkNotNull(requestedBsqTextField, "requestedBsqTextField must not be null");
requestedBsqTextField.setText(bsqFormatter.formatCoinWithCode(compensationProposal.getRequestedBsq()));
if (bsqAddressTextField != null)
bsqAddressTextField.setText(compensationProposal.getBsqAddress());
} else if (proposal instanceof ChangeParamProposal) {
ChangeParamProposal changeParamProposal = (ChangeParamProposal) proposal;
checkNotNull(paramComboBox, "paramComboBox must not be null");
paramComboBox.getSelectionModel().select(changeParamProposal.getParam());
comboBoxValueTextField.setText(paramComboBox.getConverter().toString(changeParamProposal.getParam()));
checkNotNull(paramValueTextField, "paramValueTextField must not be null");
paramValueTextField.setText(bsqFormatter.formatParamValue(changeParamProposal.getParam(), changeParamProposal.getParamValue()));
} else if (proposal instanceof BondedRoleProposal) {
@ -453,26 +457,29 @@ public class ProposalDisplay {
checkNotNull(bondedRoleTypeComboBox, "bondedRoleComboBox must not be null");
BondedRole bondedRole = bondedRoleProposal.getBondedRole();
bondedRoleTypeComboBox.getSelectionModel().select(bondedRole.getBondedRoleType());
comboBoxValueTextField.setText(bondedRoleTypeComboBox.getConverter().toString(bondedRole.getBondedRoleType()));
requiredBondForRoleTextField.setText(bsqFormatter.formatCoin(Coin.valueOf(bondedRole.getBondedRoleType().getRequiredBond())));
} else if (proposal instanceof ConfiscateBondProposal) {
ConfiscateBondProposal confiscateBondProposal = (ConfiscateBondProposal) proposal;
checkNotNull(confiscateBondComboBox, "confiscateBondComboBox must not be null");
daoFacade.getBondedRoleFromHash(confiscateBondProposal.getHash())
.ifPresent(bondedRole -> confiscateBondComboBox.getSelectionModel().select(bondedRole));
.ifPresent(bondedRole -> {
confiscateBondComboBox.getSelectionModel().select(bondedRole);
comboBoxValueTextField.setText(confiscateBondComboBox.getConverter().toString(bondedRole));
});
} else if (proposal instanceof GenericProposal) {
// do nothing
} else if (proposal instanceof RemoveAssetProposal) {
RemoveAssetProposal removeAssetProposal = (RemoveAssetProposal) proposal;
checkNotNull(assetComboBox, "assetComboBox must not be null");
CurrencyUtil.findAsset(removeAssetProposal.getTickerSymbol(), BaseCurrencyNetwork.BTC_MAINNET)
.ifPresent(asset -> assetComboBox.getSelectionModel().select(asset));
}
int chainHeight;
if (txIdTextField != null) {
txIdTextField.setup(proposal.getTxId());
chainHeight = daoFacade.getChainHeight();
} else {
chainHeight = daoFacade.getTx(proposal.getTxId()).map(Tx::getBlockHeight).orElse(0);
.ifPresent(asset -> {
assetComboBox.getSelectionModel().select(asset);
comboBoxValueTextField.setText(assetComboBox.getConverter().toString(asset));
});
}
int chainHeight = daoFacade.getTx(proposal.getTxId()).map(Tx::getBlockHeight).orElse(daoFacade.getChainHeight());
if (proposalFeeTextField != null)
proposalFeeTextField.setText(bsqFormatter.formatCoinWithCode(daoFacade.getProposalFee(chainHeight)));
}
@ -504,30 +511,42 @@ public class ProposalDisplay {
if (paramComboBox != null && paramChangeListener != null)
paramComboBox.getSelectionModel().selectedItemProperty().removeListener(paramChangeListener);
if (bondedRoleTypeComboBox != null && requiredBondForRoleListener != null)
bondedRoleTypeComboBox.getSelectionModel().selectedItemProperty().removeListener(requiredBondForRoleListener);
}
public void clearForm() {
inputControls.stream().filter(Objects::nonNull).forEach(TextInputControl::clear);
if (uidTextField != null) uidTextField.clear();
if (linkHyperlinkWithIcon != null) linkHyperlinkWithIcon.clear();
if (txIdTextField != null) txIdTextField.cleanup();
if (linkHyperlinkWithIcon != null)
linkHyperlinkWithIcon.clear();
comboBoxes.stream()
.filter(Objects::nonNull).forEach(comboBox -> {
comboBoxes.stream().filter(Objects::nonNull).forEach(comboBox -> {
comboBox.getSelectionModel().clearSelection();
});
}
public void setEditable(boolean isEditable) {
inputControls.stream().filter(Objects::nonNull).forEach(e -> e.setEditable(isEditable));
comboBoxes.stream().filter(Objects::nonNull).forEach(comboBox -> comboBox.setDisable(!isEditable));
comboBoxes.stream().filter(Objects::nonNull).forEach(comboBox -> {
comboBox.setVisible(isEditable);
comboBox.setManaged(isEditable);
if (comboBoxValueTextField != null) {
comboBoxValueTextField.setVisible(!isEditable);
comboBoxValueTextField.setManaged(!isEditable);
}
});
linkInputTextField.setVisible(true);
linkInputTextField.setManaged(true);
linkHyperlinkWithIcon.setVisible(false);
linkHyperlinkWithIcon.setManaged(false);
linkHyperlinkWithIcon.setOnAction(null);
if (linkHyperlinkWithIcon != null) {
linkHyperlinkWithIcon.setVisible(false);
linkHyperlinkWithIcon.setManaged(false);
linkHyperlinkWithIcon.setOnAction(null);
}
}
public void removeAllFields() {

View file

@ -34,7 +34,10 @@ import javax.inject.Inject;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import static bisq.desktop.util.FormBuilder.addLabelTextField;
import static bisq.desktop.util.FormBuilder.addMultilineLabel;
@ -134,11 +137,15 @@ public class ProposalDashboardView extends ActivatableView<GridPane, Void> imple
}
private String getPhaseDuration(int height, DaoPhase.Phase phase) {
final long start = daoFacade.getFirstBlockOfPhase(height, phase);
final long end = daoFacade.getLastBlockOfPhase(height, phase);
long start = daoFacade.getFirstBlockOfPhaseForDisplay(height, phase);
long end = daoFacade.getLastBlockOfPhaseForDisplay(height, phase);
long duration = daoFacade.getDurationForPhaseForDisplay(phase);
long now = new Date().getTime();
String startDateTime = formatter.formatDateTime(new Date(now + (start - height) * 10 * 60 * 1000L));
String endDateTime = formatter.formatDateTime(new Date(now + (end - height) * 10 * 60 * 1000L));
return Res.get("dao.cycle.phaseDuration", start, end, startDateTime, endDateTime);
SimpleDateFormat dateFormatter = new SimpleDateFormat("dd MMM", Locale.getDefault());
SimpleDateFormat timeFormatter = new SimpleDateFormat("hh:mm", Locale.getDefault());
String startDateTime = formatter.formatDateTime(new Date(now + (start - height) * 10 * 60 * 1000L), dateFormatter, timeFormatter);
String endDateTime = formatter.formatDateTime(new Date(now + (end - height) * 10 * 60 * 1000L), dateFormatter, timeFormatter);
String durationTime = formatter.formatDurationAsWords(duration * 10 * 60 * 1000, false, false);
return Res.get("dao.cycle.phaseDuration", duration, durationTime, start, end, startDateTime, endDateTime);
}
}

View file

@ -260,12 +260,9 @@ public class MakeProposalView extends ActivatableView<GridPane, Void> implements
case COMPENSATION_REQUEST:
checkNotNull(proposalDisplay.requestedBsqTextField,
"proposalDisplay.requestedBsqTextField must not be null");
checkNotNull(proposalDisplay.bsqAddressTextField,
"proposalDisplay.bsqAddressTextField must not be null");
return daoFacade.getCompensationProposalWithTransaction(proposalDisplay.nameTextField.getText(),
proposalDisplay.linkInputTextField.getText(),
bsqFormatter.parseToCoin(proposalDisplay.requestedBsqTextField.getText()),
proposalDisplay.bsqAddressTextField.getText());
bsqFormatter.parseToCoin(proposalDisplay.requestedBsqTextField.getText()));
case CHANGE_PARAM:
checkNotNull(proposalDisplay.paramComboBox,
"proposalDisplay.paramComboBox must no tbe null");
@ -324,7 +321,7 @@ public class MakeProposalView extends ActivatableView<GridPane, Void> implements
private void addProposalDisplay() {
if (selectedProposalType != null) {
proposalDisplay = new ProposalDisplay(root, bsqFormatter, bsqWalletService, daoFacade);
proposalDisplay = new ProposalDisplay(root, bsqFormatter, daoFacade);
proposalDisplay.createAllFields(Res.get("dao.proposal.create.createNew"), alwaysVisibleGridRowIndex, Layout.GROUP_DISTANCE,
selectedProposalType, true);

View file

@ -17,17 +17,21 @@
package bisq.desktop.main.dao.governance.proposals;
import bisq.desktop.util.FormBuilder;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.governance.ballot.Ballot;
import bisq.core.dao.governance.ballot.vote.Vote;
import bisq.core.dao.governance.proposal.Proposal;
import bisq.core.dao.state.period.DaoPhase;
import bisq.core.locale.Res;
import bisq.core.util.BsqFormatter;
import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.beans.value.ChangeListener;
@ -43,6 +47,20 @@ import javax.annotation.Nullable;
@EqualsAndHashCode
//TODO merge with vote result ProposalListItem
public class ProposalsListItem {
enum IconButtonTypes {
REMOVE_PROPOSAL(Res.get("dao.proposal.table.icon.tooltip.removeProposal")),
ACCEPT(Res.get("dao.proposal.display.myVote.accepted")),
REJECT(Res.get("dao.proposal.display.myVote.rejected")),
IGNORE(Res.get("dao.proposal.display.myVote.ignored"));
@Getter
private String title;
IconButtonTypes(String title) {
this.title = title;
}
}
@Getter
private final Proposal proposal;
private final DaoFacade daoFacade;
@ -53,7 +71,7 @@ public class ProposalsListItem {
private Ballot ballot;
@Getter
private Label icon;
private Button iconButton;
private ChangeListener<DaoPhase.Phase> phaseChangeListener;
@ -101,33 +119,61 @@ public class ProposalsListItem {
public void onPhaseChanged(DaoPhase.Phase phase) {
//noinspection IfCanBeSwitch
Label icon;
if (phase == DaoPhase.Phase.PROPOSAL) {
icon = AwesomeDude.createIconLabel(AwesomeIcon.FILE_TEXT);
icon = FormBuilder.getIcon(AwesomeIcon.TRASH);
icon.getStyleClass().addAll("icon", "dao-remove-proposal-icon");
iconButton = new Button("", icon);
boolean isMyProposal = daoFacade.isMyProposal(proposal);
icon.setVisible(isMyProposal);
icon.setManaged(isMyProposal);
} else if (icon != null) {
icon.setVisible(true);
icon.setManaged(true);
if (isMyProposal)
iconButton.setUserData(IconButtonTypes.REMOVE_PROPOSAL);
iconButton.setVisible(isMyProposal);
iconButton.setManaged(isMyProposal);
iconButton.getStyleClass().add("hidden-icon-button");
iconButton.setTooltip(new Tooltip(Res.get("dao.proposal.table.icon.tooltip.removeProposal")));
} else if (iconButton != null) {
iconButton.setVisible(true);
iconButton.setManaged(true);
}
// ballot
if (ballot != null) {
final Vote vote = ballot.getVote();
Vote vote = ballot.getVote();
if (vote != null) {
if ((vote).isAccepted()) {
icon = AwesomeDude.createIconLabel(AwesomeIcon.THUMBS_UP);
icon = FormBuilder.getIcon(AwesomeIcon.THUMBS_UP);
icon.getStyleClass().addAll("icon", "dao-accepted-icon");
iconButton = new Button("", icon);
iconButton.setUserData(IconButtonTypes.ACCEPT);
} else {
icon = AwesomeDude.createIconLabel(AwesomeIcon.THUMBS_DOWN);
icon = FormBuilder.getIcon(AwesomeIcon.THUMBS_DOWN);
icon.getStyleClass().addAll("icon", "dao-rejected-icon");
iconButton = new Button("", icon);
iconButton.setUserData(IconButtonTypes.REJECT);
}
} else {
icon = AwesomeDude.createIconLabel(AwesomeIcon.MINUS);
icon = FormBuilder.getIcon(AwesomeIcon.MINUS);
icon.getStyleClass().addAll("icon", "dao-ignored-icon");
iconButton = new Button("", icon);
iconButton.setUserData(IconButtonTypes.IGNORE);
}
icon.layout();
iconButton.setTooltip(new Tooltip(Res.get("dao.proposal.table.icon.tooltip.changeVote",
((IconButtonTypes) iconButton.getUserData()).getTitle(),
getNext(((IconButtonTypes) iconButton.getUserData()))
)));
iconButton.getStyleClass().add("hidden-icon-button");
iconButton.layout();
}
}
private String getNext(IconButtonTypes iconButtonTypes) {
if (iconButtonTypes == IconButtonTypes.ACCEPT)
return IconButtonTypes.REJECT.getTitle();
else if (iconButtonTypes == IconButtonTypes.REJECT)
return IconButtonTypes.IGNORE.getTitle();
else
return IconButtonTypes.ACCEPT.getTitle();
}
}

View file

@ -132,6 +132,7 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
private ListChangeListener<Ballot> ballotListChangeListener;
private ChangeListener<String> stakeListener;
private Subscription selectedProposalSubscription, phaseSubscription;
private boolean areVoteButtonsVisible;
///////////////////////////////////////////////////////////////////////////////////////////
@ -306,7 +307,7 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
onSelectProposal(null);
}
GUIUtil.setFitToRowsForTableView(tableView, 33, 28, 2, 4);
GUIUtil.setFitToRowsForTableView(tableView, 37, 28, 2, 4);
tableView.layout();
root.layout();
}
@ -346,7 +347,7 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
ignoreButton = tuple.third;
acceptButton.setOnAction(event -> onAccept());
rejectButton.setOnAction(event -> onReject());
ignoreButton.setOnAction(event -> onCancelVote());
ignoreButton.setOnAction(event -> onIgnore());
voteButtons.clear();
voteButtons.add(voteButton);
@ -405,13 +406,18 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
private void onRemoveProposal() {
if (daoFacade.phaseProperty().get() == DaoPhase.Phase.PROPOSAL) {
final Proposal proposal = selectedItem.getProposal();
if (daoFacade.removeMyProposal(proposal)) {
hideProposalDisplay();
} else {
new Popup<>().warning(Res.get("dao.proposal.active.remove.failed")).show();
}
tableView.getSelectionModel().clearSelection();
Proposal proposal = selectedItem.getProposal();
new Popup<>().warning(Res.get("dao.proposal.active.remove.confirm"))
.actionButtonText(Res.get("dao.proposal.active.remove.doRemove"))
.onAction(() -> {
if (daoFacade.removeMyProposal(proposal)) {
hideProposalDisplay();
} else {
new Popup<>().warning(Res.get("dao.proposal.active.remove.failed")).show();
}
tableView.getSelectionModel().clearSelection();
})
.show();
}
}
@ -466,7 +472,7 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
updateStateAfterVote();
}
private void onCancelVote() {
private void onIgnore() {
daoFacade.setVote(getBallotListItem().getBallot(), null);
proposalDisplay.applyBallot(getBallotListItem().getBallot());
updateStateAfterVote();
@ -543,15 +549,17 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
stakeInputTextField.setMouseTransparent(true);
}
boolean showVoteFields = isBlindVotePhaseButNotLastBlock || hasAlreadyVoted;
boolean hasProposals = !daoFacade.getActiveOrMyUnconfirmedProposals().isEmpty();
boolean showVoteFields = (isBlindVotePhaseButNotLastBlock && hasProposals) || hasAlreadyVoted;
voteFields.forEach(node -> {
node.setVisible(showVoteFields);
node.setManaged(showVoteFields);
});
areVoteButtonsVisible = hasProposals && isBlindVotePhaseButNotLastBlock && !hasAlreadyVoted;
voteButtons.forEach(button -> {
button.setVisible(isBlindVotePhaseButNotLastBlock && !hasAlreadyVoted);
button.setManaged(isBlindVotePhaseButNotLastBlock && !hasAlreadyVoted);
button.setVisible(areVoteButtonsVisible);
button.setManaged(areVoteButtonsVisible);
});
blindVoteTxIdTextField.setup("");
@ -643,7 +651,7 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
}
private void createEmptyProposalDisplay() {
proposalDisplay = new ProposalDisplay(proposalDisplayGridPane, bsqFormatter, bsqWalletService, daoFacade);
proposalDisplay = new ProposalDisplay(proposalDisplayGridPane, bsqFormatter, daoFacade);
proposalDisplayView = proposalDisplay.getView();
GridPane.setMargin(proposalDisplayView, new Insets(0, -10, 0, -10));
GridPane.setRowIndex(proposalDisplayView, ++gridRow);
@ -819,32 +827,51 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
column = new TableColumn<>();
column.setMinWidth(40);
column.setMinWidth(50);
column.setMaxWidth(column.getMinWidth());
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(new Callback<TableColumn<ProposalsListItem, ProposalsListItem>,
TableCell<ProposalsListItem, ProposalsListItem>>() {
column.setCellFactory(new Callback<>() {
@Override
public TableCell<ProposalsListItem, ProposalsListItem> call(TableColumn<ProposalsListItem,
ProposalsListItem> column) {
return new TableCell<ProposalsListItem, ProposalsListItem>() {
Label icon;
return new TableCell<>() {
Button iconButton;
@Override
public void updateItem(final ProposalsListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
if (icon == null) {
if (iconButton == null) {
item.onPhaseChanged(currentPhase);
icon = item.getIcon();
setGraphic(icon);
iconButton = item.getIconButton();
log.error("1 areVoteButtonsVisible " + areVoteButtonsVisible);
if (iconButton != null) {
iconButton.setOnAction(e -> {
log.error("2 areVoteButtonsVisible " + areVoteButtonsVisible);
if (areVoteButtonsVisible) {
onSelectProposal(item);
if (iconButton.getUserData() == ProposalsListItem.IconButtonTypes.REMOVE_PROPOSAL)
onRemoveProposal();
else if (iconButton.getUserData() == ProposalsListItem.IconButtonTypes.ACCEPT)
onReject();
else if (iconButton.getUserData() == ProposalsListItem.IconButtonTypes.REJECT)
onIgnore();
else if (iconButton.getUserData() == ProposalsListItem.IconButtonTypes.IGNORE)
onAccept();
}
});
if (!areVoteButtonsVisible && iconButton.getUserData() != ProposalsListItem.IconButtonTypes.REMOVE_PROPOSAL) {
iconButton.setMouseTransparent(true);
iconButton.setStyle("-fx-cursor: default;");
}
setGraphic(iconButton);
}
}
} else {
setGraphic(null);
if (icon != null)
icon = null;
if (iconButton != null)
iconButton = null;
}
}
};

View file

@ -17,6 +17,8 @@
package bisq.desktop.main.dao.governance.result;
import bisq.desktop.util.FormBuilder;
import bisq.core.dao.governance.ballot.Ballot;
import bisq.core.dao.governance.ballot.vote.Vote;
import bisq.core.dao.governance.proposal.Proposal;
@ -33,7 +35,6 @@ import bisq.core.util.BsqFormatter;
import org.bitcoinj.core.Coin;
import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon;
import javafx.scene.control.Label;
@ -66,15 +67,15 @@ public class ProposalListItem {
Label myVoteIcon;
if (vote != null) {
if ((vote).isAccepted()) {
myVoteIcon = AwesomeDude.createIconLabel(AwesomeIcon.THUMBS_UP);
myVoteIcon.getStyleClass().addAll("icon", "dao-accepted-icon");
myVoteIcon = FormBuilder.getIcon(AwesomeIcon.THUMBS_UP);
myVoteIcon.getStyleClass().add("dao-accepted-icon");
} else {
myVoteIcon = AwesomeDude.createIconLabel(AwesomeIcon.THUMBS_DOWN);
myVoteIcon.getStyleClass().addAll("icon", "dao-rejected-icon");
myVoteIcon = FormBuilder.getIcon(AwesomeIcon.THUMBS_DOWN);
myVoteIcon.getStyleClass().add("dao-rejected-icon");
}
} else {
myVoteIcon = AwesomeDude.createIconLabel(AwesomeIcon.MINUS);
myVoteIcon.getStyleClass().addAll("icon", "dao-ignored-icon");
myVoteIcon = FormBuilder.getIcon(AwesomeIcon.MINUS);
myVoteIcon.getStyleClass().add("dao-ignored-icon");
}
return myVoteIcon;
}

View file

@ -347,7 +347,7 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
private void createProposalDisplay(EvaluatedProposal evaluatedProposal, Ballot ballot) {
Proposal proposal = evaluatedProposal.getProposal();
ProposalDisplay proposalDisplay = new ProposalDisplay(new GridPane(), bsqFormatter, bsqWalletService, daoFacade);
ProposalDisplay proposalDisplay = new ProposalDisplay(new GridPane(), bsqFormatter, daoFacade);
ScrollPane proposalDisplayView = proposalDisplay.getView();
GridPane.setMargin(proposalDisplayView, new Insets(0, -10, -15, -10));
@ -851,74 +851,6 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements D
}
});
votesTableView.getColumns().add(column);
column = new AutoTooltipTableColumn<>(Res.get("dao.results.votes.table.header.blindVoteTxId"));
column.setSortable(false);
column.setMinWidth(130);
column.setMaxWidth(column.getMinWidth());
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(new Callback<>() {
@Override
public TableCell<VoteListItem, VoteListItem> call(TableColumn<VoteListItem,
VoteListItem> column) {
return new TableCell<>() {
private HyperlinkWithIcon hyperlinkWithIcon;
@Override
public void updateItem(final VoteListItem item, boolean empty) {
super.updateItem(item, empty);
//noinspection Duplicates
if (item != null && !empty) {
String blindVoteTxId = item.getBlindVoteTxId();
hyperlinkWithIcon = new HyperlinkWithIcon(blindVoteTxId, AwesomeIcon.EXTERNAL_LINK);
hyperlinkWithIcon.setOnAction(event -> openTxInBlockExplorer(item.getBlindVoteTxId()));
hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", blindVoteTxId)));
setGraphic(hyperlinkWithIcon);
} else {
setGraphic(null);
if (hyperlinkWithIcon != null)
hyperlinkWithIcon.setOnAction(null);
}
}
};
}
});
votesTableView.getColumns().add(column);
column = new AutoTooltipTableColumn<>(Res.get("dao.results.votes.table.header.voteRevealTxId"));
column.setSortable(false);
column.setMinWidth(140);
column.setMaxWidth(column.getMinWidth());
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(new Callback<>() {
@Override
public TableCell<VoteListItem, VoteListItem> call(TableColumn<VoteListItem,
VoteListItem> column) {
return new TableCell<>() {
private HyperlinkWithIcon hyperlinkWithIcon;
@Override
public void updateItem(final VoteListItem item, boolean empty) {
super.updateItem(item, empty);
//noinspection Duplicates
if (item != null && !empty) {
String voteRevealTxId = item.getVoteRevealTxId();
hyperlinkWithIcon = new HyperlinkWithIcon(voteRevealTxId, AwesomeIcon.EXTERNAL_LINK);
hyperlinkWithIcon.setOnAction(event -> openTxInBlockExplorer(item.getVoteRevealTxId()));
hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", voteRevealTxId)));
setGraphic(hyperlinkWithIcon);
} else {
setGraphic(null);
if (hyperlinkWithIcon != null)
hyperlinkWithIcon.setOnAction(null);
}
}
};
}
});
votesTableView.getColumns().add(column);
}
private void openTxInBlockExplorer(String txId) {

View file

@ -17,6 +17,7 @@
package bisq.desktop.main.dao.wallet;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.util.Layout;
import bisq.core.btc.listeners.BsqBalanceListener;
@ -24,10 +25,13 @@ import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.locale.Res;
import bisq.core.util.BsqFormatter;
import bisq.common.util.Tuple2;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
@ -47,6 +51,8 @@ public class BsqBalanceUtil implements BsqBalanceListener {
// Displaying bond dashboard info
private TextField lockupAmountTextField, unlockingAmountTextField;
private TitledGroupBg titledGroupBg;
private Label availableNonBsqBalanceLabel;
@Inject
private BsqBalanceUtil(BsqWalletService bsqWalletService,
@ -56,16 +62,12 @@ public class BsqBalanceUtil implements BsqBalanceListener {
}
public int addGroup(GridPane gridPane, int gridRow) {
addTitledGroupBg(gridPane, gridRow, 6, Res.get("shared.balance"));
titledGroupBg = addTitledGroupBg(gridPane, gridRow, 5, Res.get("dao.wallet.dashboard.myBalance"));
availableBalanceTextField = addLabelTextField(gridPane, gridRow,
Res.getWithCol("dao.availableBsqBalance"),
Layout.FIRST_ROW_DISTANCE).second;
availableBalanceTextField.setMouseTransparent(false);
availableNonBsqBalanceTextField = addLabelTextField(gridPane, ++gridRow,
Res.getWithCol("dao.availableNonBsqBalance")).second;
availableNonBsqBalanceTextField.setMouseTransparent(false);
unverifiedBalanceTextField = addLabelTextField(gridPane, ++gridRow,
Res.getWithCol("dao.unverifiedBsqBalance")).second;
unverifiedBalanceTextField.setMouseTransparent(false);
@ -84,6 +86,14 @@ public class BsqBalanceUtil implements BsqBalanceListener {
Res.getWithCol("dao.totalBsqBalance")).second;
totalBalanceTextField.setMouseTransparent(false);
Tuple2<Label, TextField> tuple2 = addLabelTextField(gridPane, ++gridRow,
Res.getWithCol("dao.availableNonBsqBalance"));
availableNonBsqBalanceLabel = tuple2.first;
availableNonBsqBalanceTextField = tuple2.second;
availableNonBsqBalanceTextField.setMouseTransparent(false);
availableNonBsqBalanceTextField.setVisible(false);
availableNonBsqBalanceTextField.setManaged(false);
return gridRow;
}
@ -125,8 +135,12 @@ public class BsqBalanceUtil implements BsqBalanceListener {
Coin lockedForVotingBalance,
Coin lockupBondsBalance,
Coin unlockingBondsBalance) {
boolean isNonBsqBalanceAvailable = availableNonBsqBalance.value > 0;
int rowSpan = isNonBsqBalanceAvailable ? 6 : 5;
GridPane.setRowSpan(titledGroupBg, rowSpan);
availableBalanceTextField.setText(bsqFormatter.formatCoinWithCode(availableBalance));
availableNonBsqBalanceTextField.setText(bsqFormatter.formatBtcSatoshi(availableNonBsqBalance.value));
unverifiedBalanceTextField.setText(bsqFormatter.formatCoinWithCode(unverifiedBalance));
lockedForVoteBalanceTextField.setText(bsqFormatter.formatCoinWithCode(lockedForVotingBalance));
lockedInBondsBalanceTextField.setText(bsqFormatter.formatCoinWithCode(
@ -137,6 +151,12 @@ public class BsqBalanceUtil implements BsqBalanceListener {
unlockingAmountTextField.setText(bsqFormatter.formatCoinWithCode(unlockingBondsBalance));
}
availableNonBsqBalanceLabel.setVisible(isNonBsqBalanceAvailable);
availableNonBsqBalanceLabel.setManaged(isNonBsqBalanceAvailable);
availableNonBsqBalanceTextField.setVisible(isNonBsqBalanceAvailable);
availableNonBsqBalanceTextField.setManaged(isNonBsqBalanceAvailable);
availableNonBsqBalanceTextField.setText(bsqFormatter.formatBTCWithCode(availableNonBsqBalance.value));
final Coin total = availableBalance
.add(unverifiedBalance)
.add(lockedForVotingBalance)

View file

@ -19,10 +19,8 @@ package bisq.desktop.main.dao.wallet.dashboard;
import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.main.dao.wallet.BsqBalanceUtil;
import bisq.desktop.util.GUIUtil;
import bisq.desktop.util.Layout;
import bisq.core.dao.DaoFacade;
@ -42,17 +40,13 @@ import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import de.jensd.fx.fontawesome.AwesomeIcon;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane;
import javafx.geometry.Insets;
import javafx.beans.value.ChangeListener;
import static bisq.desktop.util.FormBuilder.addLabelHyperlinkWithIcon;
import static bisq.desktop.util.FormBuilder.addLabelTextField;
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
@ -68,9 +62,8 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
private int gridRow = 0;
private TextField genesisIssueAmountTextField, compRequestIssueAmountTextField, availableAmountTextField,
burntAmountTextField, totalLockedUpAmountTextField, totalUnlockingAmountTextField,
totalUnlockedAmountTextField, allTxTextField,
burntTxTextField,
utxoTextField, priceTextField, marketCapTextField;
totalUnlockedAmountTextField, allTxTextField, burntTxTextField, utxoTextField, issuanceTxTextField,
priceTextField, marketCapTextField;
private ChangeListener<Number> priceChangeListener;
private HyperlinkWithIcon hyperlinkWithIcon;
@ -96,34 +89,34 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
public void initialize() {
gridRow = bsqBalanceUtil.addGroup(root, gridRow);
addTitledGroupBg(root, ++gridRow, 14, Res.get("dao.wallet.dashboard.statistics"), Layout.GROUP_DISTANCE);
addTitledGroupBg(root, ++gridRow, 4, Res.get("dao.wallet.dashboard.distribution"), Layout.GROUP_DISTANCE);
addLabelTextField(root, gridRow, Res.get("dao.wallet.dashboard.genesisBlockHeight"),
String.valueOf(daoFacade.getGenesisBlockHeight()), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
Label label = new AutoTooltipLabel(Res.get("dao.wallet.dashboard.genesisTxId"));
GridPane.setRowIndex(label, ++gridRow);
root.getChildren().add(label);
hyperlinkWithIcon = new HyperlinkWithIcon(daoFacade.getGenesisTxId(), AwesomeIcon.EXTERNAL_LINK);
hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", daoFacade.getGenesisTxId())));
GridPane.setRowIndex(hyperlinkWithIcon, gridRow);
GridPane.setColumnIndex(hyperlinkWithIcon, 1);
GridPane.setMargin(hyperlinkWithIcon, new Insets(0, 0, 0, -4));
root.getChildren().add(hyperlinkWithIcon);
genesisIssueAmountTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.genesisIssueAmount")).second;
genesisIssueAmountTextField = addLabelTextField(root, gridRow, Res.get("dao.wallet.dashboard.genesisIssueAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
compRequestIssueAmountTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.compRequestIssueAmount")).second;
availableAmountTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.availableAmount")).second;
burntAmountTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.burntAmount")).second;
totalLockedUpAmountTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.totalLockedUpAmount")).second;
availableAmountTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.availableAmount")).second;
addTitledGroupBg(root, ++gridRow, 3, Res.get("dao.wallet.dashboard.locked"), Layout.GROUP_DISTANCE);
totalLockedUpAmountTextField = addLabelTextField(root, gridRow, Res.get("dao.wallet.dashboard.totalLockedUpAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
totalUnlockingAmountTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.totalUnlockingAmount")).second;
totalUnlockedAmountTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.totalUnlockedAmount")).second;
addTitledGroupBg(root, ++gridRow, 2, Res.get("dao.wallet.dashboard.market"), Layout.GROUP_DISTANCE);
priceTextField = addLabelTextField(root, gridRow, Res.get("dao.wallet.dashboard.price"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
marketCapTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.marketCap")).second;
addTitledGroupBg(root, ++gridRow, 6, Res.get("dao.wallet.dashboard.txDetails"), Layout.GROUP_DISTANCE);
addLabelTextField(root, gridRow, Res.get("dao.wallet.dashboard.genesisBlockHeight"),
String.valueOf(daoFacade.getGenesisBlockHeight()), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
hyperlinkWithIcon = addLabelHyperlinkWithIcon(root, ++gridRow, Res.get("dao.wallet.dashboard.genesisTxId"),
daoFacade.getGenesisTxId(), preferences.getBsqBlockChainExplorer().txUrl + daoFacade.getGenesisTxId()).second;
hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", daoFacade.getGenesisTxId())));
allTxTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.allTx")).second;
utxoTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.utxo")).second;
issuanceTxTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.issuanceTx")).second;
burntTxTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.burntTx")).second;
priceTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.price")).second;
marketCapTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.marketCap")).second;
priceChangeListener = (observable, oldValue, newValue) -> updatePrice();
}
@ -135,8 +128,6 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
daoFacade.addBsqStateListener(this);
priceFeedService.updateCounterProperty().addListener(priceChangeListener);
hyperlinkWithIcon.setOnAction(event -> GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + daoFacade.getGenesisTxId()));
updateWithBsqBlockChainData();
updatePrice();
}
@ -146,7 +137,6 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
bsqBalanceUtil.deactivate();
daoFacade.removeBsqStateListener(this);
priceFeedService.updateCounterProperty().removeListener(priceChangeListener);
hyperlinkWithIcon.setOnAction(null);
}
@ -186,12 +176,13 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
Coin availableAmount = issuedAmountFromGenesis.add(issuedAmountFromCompRequests).subtract(burntFee);
availableAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(availableAmount));
burntAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(burntFee));
burntAmountTextField.setText("-" + bsqFormatter.formatAmountWithGroupSeparatorAndCode(burntFee));
totalLockedUpAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalLockedUpAmount));
totalUnlockingAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockingAmount));
totalUnlockedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockedAmount));
allTxTextField.setText(String.valueOf(daoFacade.getTxs().size()));
utxoTextField.setText(String.valueOf(daoFacade.getUnspentTxOutputs().size()));
issuanceTxTextField.setText(String.valueOf(daoFacade.getIssuanceSet().size()));
burntTxTextField.setText(String.valueOf(daoFacade.getFeeTxs().size()));
}
@ -203,6 +194,9 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
priceTextField.setText(bsqFormatter.formatPrice(Price.valueOf("BSQ", bsqPrice)) + " BSQ/BTC");
marketCapTextField.setText(bsqFormatter.formatMarketCap(bsqMarketPrice, priceFeedService.getMarketPrice("USD"), issuedAmount));
} else {
priceTextField.setText(Res.get("shared.na"));
marketCapTextField.setText(Res.get("shared.na"));
}
}
}

View file

@ -62,7 +62,7 @@ public class BsqReceiveView extends ActivatableView<GridPane, Void> {
addTitledGroupBg(root, ++gridRow, 1, Res.get("dao.wallet.receive.fundYourWallet"), Layout.GROUP_DISTANCE);
addressTextField = addLabelBsqAddressTextField(root, gridRow, Res.getWithCol("shared.address"),
addressTextField = addLabelBsqAddressTextField(root, gridRow, Res.getWithCol("dao.wallet.receive.bsqAddress"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
addressTextField.setPaymentLabel(paymentLabelString);
}

View file

@ -221,7 +221,7 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
receiversAddressInputTextField.setValidator(bsqAddressValidator);
amountInputTextField = addLabelInputTextField(root, ++gridRow, Res.get("dao.wallet.send.amount")).second;
amountInputTextField.setPromptText(Res.get("dao.wallet.send.setAmount", bsqFormatter.formatCoin(Restrictions.getMinNonDustOutput())));
amountInputTextField.setPromptText(Res.get("dao.wallet.send.setAmount", bsqFormatter.formatCoinWithCode(Restrictions.getMinNonDustOutput())));
amountInputTextField.setValidator(bsqValidator);
focusOutListener = (observable, oldValue, newValue) -> {
@ -290,16 +290,16 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
Tuple2<Label, InputTextField> tuple2 = addLabelInputTextField(root, ++gridRow, Res.get("dao.wallet.send.btcAmount"));
btcAmountLabel = tuple2.first;
btcAmountInputTextField = tuple2.second;
btcAmountInputTextField.setPromptText(Res.get("dao.wallet.send.setBtcAmount", Restrictions.getMinNonDustOutput().value));
btcAmountInputTextField.setPromptText(Res.get("dao.wallet.send.setBtcAmount",
bsqFormatter.formatBTCWithCode(Restrictions.getMinNonDustOutput().value)));
btcAmountInputTextField.setValidator(btcValidator);
sendBtcButton = addButtonAfterGroup(root, ++gridRow, Res.get("dao.wallet.send.sendBtc"));
sendBtcButton.setOnAction((event) -> {
// TODO break up in methods
if (GUIUtil.isReadyForTxBroadcast(p2PService, walletsSetup)) {
String receiversAddressString = receiversBtcAddressInputTextField.getText();
Coin receiverAmount = bsqFormatter.parseSatoshiToBtc(btcAmountInputTextField.getText());
Coin receiverAmount = bsqFormatter.parseToBTC(btcAmountInputTextField.getText());
try {
Transaction preparedSendTx = bsqWalletService.getPreparedSendBtcTx(receiversAddressString, receiverAmount);
Transaction txWithBtcFee = btcWalletService.completePreparedSendBsqTx(preparedSendTx, true);

View file

@ -44,7 +44,6 @@ import org.bitcoinj.core.Transaction;
import javax.inject.Inject;
import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon;
import javafx.scene.control.Label;
@ -569,7 +568,7 @@ public class BsqTxView extends ActivatableView<GridPane, Void> implements BsqBal
style = "dao-tx-type-unverified-icon";
break;
}
Label label = AwesomeDude.createIconLabel(awesomeIcon);
Label label = FormBuilder.getIcon(awesomeIcon);
label.getStyleClass().addAll("icon", style);
label.setTooltip(new Tooltip(toolTipText));
if (doRotate)

View file

@ -156,7 +156,7 @@ public class EmptyWalletWindow extends Overlay<EmptyWalletWindow> {
addressInputTextField = addLabelInputTextField(gridPane, ++rowIndex, Res.get("emptyWalletWindow.address")).second;
} else {
addLabelTextField(gridPane, ++rowIndex, Res.get("emptyWalletWindow.bsq.btcBalance"),
bsqFormatter.formatBtcSatoshi(bsqWalletService.getAvailableNonBsqBalance().value), 10);
bsqFormatter.formatBTCWithCode(bsqWalletService.getAvailableNonBsqBalance().value), 10);
}
closeButton = new AutoTooltipButton(Res.get("shared.cancel"));
closeButton.setOnAction(e -> {

View file

@ -23,9 +23,11 @@ import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.AutoTooltipButton;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.InputTextField;
import bisq.desktop.components.PasswordTextField;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.GUIUtil;
import bisq.desktop.util.ImageUtil;
import bisq.desktop.util.Layout;
@ -99,7 +101,12 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
private CheckBox useAnimationsCheckBox, avoidStandbyModeCheckBox,
showOwnOffersInOfferBook, sortMarketCurrenciesNumericallyCheckBox, useCustomFeeCheckbox;
private int gridRow = 0;
private InputTextField transactionFeeInputTextField, ignoreTradersListInputTextField, referralIdInputTextField;
private InputTextField transactionFeeInputTextField, ignoreTradersListInputTextField, referralIdInputTextField, rpcUserTextField;
private CheckBox isDaoFullNodeCheckBox;
private PasswordTextField rpcPwTextField;
private TitledGroupBg daoOptionsTitledGroupBg;
private Label rpcUserLabel, rpcPwLabel;
private ChangeListener<Boolean> transactionFeeFocusedListener;
private final Preferences preferences;
private final FeeService feeService;
@ -124,11 +131,12 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
private ObservableList<CryptoCurrency> allCryptoCurrencies;
private ObservableList<TradeCurrency> tradeCurrencies;
private InputTextField deviationInputTextField;
private ChangeListener<String> deviationListener, ignoreTradersListListener, referralIdListener;
private ChangeListener<String> deviationListener, ignoreTradersListListener, referralIdListener, rpcUserListener, rpcPwListener;
private ChangeListener<Boolean> deviationFocusedListener;
private ChangeListener<Boolean> useCustomFeeCheckboxListener;
private ChangeListener<Number> transactionFeeChangeListener;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, initialisation
///////////////////////////////////////////////////////////////////////////////////////////
@ -501,12 +509,36 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
}
private void initializeDaoOptions() {
TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 1, Res.get("setting.preferences.daoOptions"), Layout.GROUP_DISTANCE);
GridPane.setColumnSpan(titledGroupBg, 4);
daoOptionsTitledGroupBg = addTitledGroupBg(root, ++gridRow, 2, Res.get("setting.preferences.daoOptions"), Layout.GROUP_DISTANCE);
GridPane.setColumnSpan(daoOptionsTitledGroupBg, 4);
resyncDaoButton = addLabelButton(root, gridRow, Res.get("setting.preferences.dao.resync.label"),
Res.get("setting.preferences.dao.resync.button"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
isDaoFullNodeCheckBox = addLabelCheckBox(root, ++gridRow, Res.getWithCol("setting.preferences.dao.isDaoFullNode")).second;
Tuple2<Label, InputTextField> tuple = addLabelInputTextField(root, ++gridRow, Res.getWithCol("setting.preferences.dao.rpcUser"));
rpcUserLabel = tuple.first;
rpcUserLabel.setVisible(false);
rpcUserLabel.setManaged(false);
rpcUserTextField = tuple.second;
rpcUserTextField.setVisible(false);
rpcUserTextField.setManaged(false);
Tuple2<Label, PasswordTextField> tuple2 = addLabelPasswordTextField(root, ++gridRow, Res.getWithCol("setting.preferences.dao.rpcPw"));
rpcPwLabel = tuple2.first;
rpcPwLabel.setVisible(false);
rpcPwLabel.setManaged(false);
rpcPwTextField = tuple2.second;
rpcPwTextField.setVisible(false);
rpcPwTextField.setManaged(false);
rpcUserListener = (observable, oldValue, newValue) -> {
preferences.setRpcUser(rpcUserTextField.getText());
};
rpcPwListener = (observable, oldValue, newValue) -> {
preferences.setRpcPw(rpcPwTextField.getText());
};
}
///////////////////////////////////////////////////////////////////////////////////////////
// Activate
///////////////////////////////////////////////////////////////////////////////////////////
@ -695,12 +727,67 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
}
private void activateDaoPreferences() {
boolean daoFullNode = preferences.isDaoFullNode();
isDaoFullNodeCheckBox.setSelected(daoFullNode);
String rpcUser = preferences.getRpcUser();
String rpcPw = preferences.getRpcPw();
if (daoFullNode && (rpcUser == null || rpcUser.isEmpty() || rpcPw == null || rpcPw.isEmpty())) {
log.warn("You have full DAO node selected but have not provided the rpc username and password. We reset daoFullNode to false");
isDaoFullNodeCheckBox.setSelected(false);
}
rpcUserTextField.setText(rpcUser);
rpcPwTextField.setText(rpcPw);
updateDaoFields();
resyncDaoButton.setOnAction(e -> daoFacade.resyncDao(() -> {
new Popup<>().attention(Res.get("setting.preferences.dao.resync.popup"))
.useShutDownButton()
.hideCloseButton()
.show();
}));
isDaoFullNodeCheckBox.setOnAction(e -> {
String key = "daoFullModeInfoShown";
if (isDaoFullNodeCheckBox.isSelected() && preferences.showAgain(key)) {
String url = "https://bisq.network/docs/dao-full-node";
new Popup<>().backgroundInfo(Res.get("setting.preferences.dao.fullNodeInfo", url))
.onAction(() -> {
GUIUtil.openWebPage(url);
})
.actionButtonText(Res.get("setting.preferences.dao.fullNodeInfo.ok"))
.closeButtonText(Res.get("setting.preferences.dao.fullNodeInfo.cancel"))
.onClose(() -> UserThread.execute(() -> {
isDaoFullNodeCheckBox.setSelected(false);
updateDaoFields();
}))
.dontShowAgainId(key)
.width(800)
.show();
}
updateDaoFields();
});
rpcUserTextField.textProperty().addListener(rpcUserListener);
rpcPwTextField.textProperty().addListener(rpcPwListener);
}
private void updateDaoFields() {
boolean isDaoFullNode = isDaoFullNodeCheckBox.isSelected();
GridPane.setRowSpan(daoOptionsTitledGroupBg, isDaoFullNode ? 4 : 2);
rpcUserLabel.setVisible(isDaoFullNode);
rpcUserLabel.setManaged(isDaoFullNode);
rpcUserTextField.setVisible(isDaoFullNode);
rpcUserTextField.setManaged(isDaoFullNode);
rpcPwLabel.setVisible(isDaoFullNode);
rpcPwLabel.setManaged(isDaoFullNode);
rpcPwTextField.setVisible(isDaoFullNode);
rpcPwTextField.setManaged(isDaoFullNode);
preferences.setDaoFullNode(isDaoFullNode);
if (!isDaoFullNode) {
rpcPwTextField.clear();
rpcUserTextField.clear();
}
}
private void onSelectNetwork() {
@ -754,5 +841,8 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
private void deactivateDaoPreferences() {
resyncDaoButton.setOnAction(null);
isDaoFullNodeCheckBox.setOnAction(null);
rpcUserTextField.textProperty().removeListener(rpcUserListener);
rpcPwTextField.textProperty().removeListener(rpcUserListener);
}
}

View file

@ -60,10 +60,13 @@ import mockit.MockUp;
import mockit.Tested;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
//TODO @Christoph Can you have a look why JMockit is nto working anymore in that module?
@Ignore
public class TradesChartsViewModelTest {
@Tested
TradesChartsViewModel model;

View file

@ -42,7 +42,8 @@ public class PreferenceMakers {
lookup.valueOf(bisqEnvironment, new SameValueDonor<BisqEnvironment>(null)),
lookup.valueOf(btcNodesFromOptions, new SameValueDonor<String>(null)),
lookup.valueOf(useTorFlagFromOptions, new SameValueDonor<String>(null)),
lookup.valueOf(referralID, new SameValueDonor<String>(null)));
lookup.valueOf(referralID, new SameValueDonor<String>(null)),
null, null, null);
public static final Preferences empty = make(a(Preferences));

View file

@ -89,9 +89,9 @@ public class BSFormatterTest {
assertEquals("1 day, 0 hours, 0 minutes", formatter.formatDurationAsWords(oneDay));
assertEquals("2 days, 0 hours, 1 minute", formatter.formatDurationAsWords(oneDay * 2 + oneMinute));
assertEquals("2 days, 0 hours, 2 minutes", formatter.formatDurationAsWords(oneDay * 2 + oneMinute * 2));
assertEquals("1 hour, 0 minutes, 0 seconds", formatter.formatDurationAsWords(oneHour, true));
assertEquals("1 hour, 0 minutes, 1 second", formatter.formatDurationAsWords(oneHour + oneSecond, true));
assertEquals("1 hour, 0 minutes, 2 seconds", formatter.formatDurationAsWords(oneHour + oneSecond * 2, true));
assertEquals("1 hour, 0 minutes, 0 seconds", formatter.formatDurationAsWords(oneHour, true, true));
assertEquals("1 hour, 0 minutes, 1 second", formatter.formatDurationAsWords(oneHour + oneSecond, true, true));
assertEquals("1 hour, 0 minutes, 2 seconds", formatter.formatDurationAsWords(oneHour + oneSecond * 2, true, true));
assertEquals("2 days, 21 hours, 28 minutes", formatter.formatDurationAsWords(oneDay * 2 + oneHour * 21 + oneMinute * 28));
assertEquals("", formatter.formatDurationAsWords(0));
assertTrue(formatter.formatDurationAsWords(0).isEmpty());

View file

@ -20,11 +20,9 @@ package bisq.seednode;
import bisq.core.app.misc.AppSetup;
import bisq.core.app.misc.AppSetupWithP2P;
import bisq.core.app.misc.AppSetupWithP2PAndDAO;
import bisq.core.dao.DaoOptionKeys;
import bisq.core.user.Preferences;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.name.Names;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@ -39,8 +37,8 @@ public class SeedNode {
}
public void startApplication() {
Boolean fullDaoNode = injector.getInstance(Key.get(Boolean.class, Names.named(DaoOptionKeys.FULL_DAO_NODE)));
appSetup = fullDaoNode ? injector.getInstance(AppSetupWithP2PAndDAO.class) : injector.getInstance(AppSetupWithP2P.class);
boolean isDaoFullNode = injector.getInstance(Preferences.class).isDaoFullNode();
appSetup = isDaoFullNode ? injector.getInstance(AppSetupWithP2PAndDAO.class) : injector.getInstance(AppSetupWithP2P.class);
appSetup.start();
}
}