diff --git a/app/actions/ClientActions.js b/app/actions/ClientActions.js index 01214a6b7e..679740e4ed 100644 --- a/app/actions/ClientActions.js +++ b/app/actions/ClientActions.js @@ -5,6 +5,7 @@ import * as sel from "selectors"; import eq from "lodash/fp/eq"; import { getNextAddressAttempt, + getPeerInfo, publishUnminedTransactionsAttempt } from "./ControlActions"; import { @@ -65,6 +66,7 @@ const startWalletServicesTrigger = () => (dispatch, getState) => } dispatch(discoverAvailableVSPs()); await dispatch(getNextAddressAttempt(0)); + await dispatch(getPeerInfo()); await dispatch(getTicketPriceAttempt()); await dispatch(getNetworkAttempt()); await dispatch(refreshStakepoolPurchaseInformation()); diff --git a/app/actions/ControlActions.js b/app/actions/ControlActions.js index e2bf6cdb79..356a406104 100644 --- a/app/actions/ControlActions.js +++ b/app/actions/ControlActions.js @@ -786,3 +786,17 @@ export const startTicketBuyerV2Attempt = ( }); }); }; + +export const GETPEERINFO_ATTEMPT = "GETPEERINFO_ATTEMPT"; +export const GETPEERINFO_FAILED = "GETPEERINFO_FAILED"; +export const GETPEERINFO_SUCCESS = "GETPEERINFO_SUCCESS"; + +export const getPeerInfo = () => (dispatch, getState) => { + dispatch({ type: GETPEERINFO_ATTEMPT }); + return wallet.getPeerInfo(getState().grpc.walletService) + .then(resp => { + const peersCount = resp.wrappers_[1].length; + dispatch({ type: GETPEERINFO_SUCCESS, peersCount }); + }) + .catch((error) => dispatch({ type: GETPEERINFO_FAILED, error })); +}; diff --git a/app/components/SideBar/Logo/Logo.jsx b/app/components/SideBar/Logo/Logo.jsx index 2bc3fc6f04..ab0dbd1683 100644 --- a/app/components/SideBar/Logo/Logo.jsx +++ b/app/components/SideBar/Logo/Logo.jsx @@ -10,10 +10,12 @@ const Logo = React.memo( onReduceSideBar, onExpandSideBar, isWatchingOnly, - accountMixerRunning + accountMixerRunning, + peersCount }) => (
+ className={expandSideBar ? style.logo : style.reducedLogo} + > {isWatchingOnly && ( + + }> +
{peersCount}
+
{accountMixerRunning && ( { rescanRequest, onExpandSideBar, onReduceSideBar, - isSPV + isSPV, + peersCount } = useSideBar(); const { rescanAttempt, rescanCancel } = useRescan(); @@ -44,7 +45,8 @@ const SideBar = () => { onReduceSideBar, onExpandSideBar, isWatchingOnly, - accountMixerRunning + accountMixerRunning, + peersCount }} />
diff --git a/app/components/SideBar/hooks.js b/app/components/SideBar/hooks.js index e48f7ca66d..ac11e3837c 100644 --- a/app/components/SideBar/hooks.js +++ b/app/components/SideBar/hooks.js @@ -6,9 +6,6 @@ import * as sba from "../../actions/SidebarActions"; export function useSideBar() { const [isShowingAccounts, setIsShowingAccounts] = useState(false); - const onShowAccounts = useCallback(() => setIsShowingAccounts(true), []); - const onHideAccounts = useCallback(() => setIsShowingAccounts(false), []); - const isTestNet = useSelector(sel.isTestNet); const balances = useSelector(sel.balances); const currentBlockHeight = useSelector(sel.currentBlockHeight); @@ -20,6 +17,7 @@ export function useSideBar() { const accountMixerRunning = useSelector(sel.getAccountMixerRunning); const rescanRequest = useSelector(sel.rescanRequest); const isSPV = useSelector(sel.isSPV); + const peersCount = useSelector(sel.getPeersCount); const dispatch = useDispatch(); @@ -32,8 +30,8 @@ export function useSideBar() { return { isShowingAccounts, - onShowAccounts, - onHideAccounts, + onShowAccounts: () => setIsShowingAccounts(true), + onHideAccounts: () => setIsShowingAccounts(false), isTestNet, balances, currentBlockHeight, @@ -46,6 +44,7 @@ export function useSideBar() { rescanRequest, onExpandSideBar, onReduceSideBar, - isSPV + isSPV, + peersCount }; } diff --git a/app/components/views/TicketsPage/PurchaseTab/PurchaseTickets/PurchaseTickets.jsx b/app/components/views/TicketsPage/PurchaseTab/PurchaseTickets/PurchaseTickets.jsx index e7b2a7d781..895849ba6e 100644 --- a/app/components/views/TicketsPage/PurchaseTab/PurchaseTickets/PurchaseTickets.jsx +++ b/app/components/views/TicketsPage/PurchaseTab/PurchaseTickets/PurchaseTickets.jsx @@ -15,7 +15,6 @@ const Tickets = ({ toggleIsLegacy }) => { // availableVSPsError, defaultSpendingAccount, ticketPrice, - getVSPTicketsByFeeStatus, onPurchaseTicketV3 } = usePurchaseTab(); diff --git a/app/connectors/walletContainer.js b/app/connectors/walletContainer.js index a8e569d521..be2682a940 100644 --- a/app/connectors/walletContainer.js +++ b/app/connectors/walletContainer.js @@ -2,6 +2,7 @@ import { connect } from "react-redux"; import { bindActionCreators } from "redux"; import { selectorMap } from "fp"; import * as ga from "actions/GovernanceActions"; +import { getPeerInfo } from "actions/ControlActions"; import * as sel from "selectors"; const mapStateToProps = selectorMap({ @@ -12,7 +13,8 @@ const mapStateToProps = selectorMap({ const mapDispatchToProps = (dispatch) => bindActionCreators( { - compareInventory: ga.compareInventory + compareInventory: ga.compareInventory, + getPeerInfo }, dispatch ); diff --git a/app/containers/Wallet.js b/app/containers/Wallet.js index ba7fd0c85b..82f103ce89 100644 --- a/app/containers/Wallet.js +++ b/app/containers/Wallet.js @@ -32,7 +32,7 @@ const pageAnimation = { class Wallet extends React.Component { constructor(props) { super(props); - const { compareInventory, politeiaEnabled } = props; + const { compareInventory, politeiaEnabled, getPeerInfo } = props; // Compare politeias inventory and update proposal list if they are different // every 1 minute. this.fetchPoliteiaInventory = this.props.setInterval(() => { @@ -40,6 +40,13 @@ class Wallet extends React.Component { compareInventory(); } }, 60000); + // Get peer info every 1 minute, so we can no if there are no available + // peers. + this.props.setInterval(() => { + if (politeiaEnabled) { + getPeerInfo(); + } + }, 60000); } render() { diff --git a/app/reducers/grpc.js b/app/reducers/grpc.js index 75d74ed10a..bec3f684f4 100644 --- a/app/reducers/grpc.js +++ b/app/reducers/grpc.js @@ -68,7 +68,9 @@ import { VERIFYMESSAGE_ATTEMPT, VERIFYMESSAGE_SUCCESS, VERIFYMESSAGE_FAILED, - VERIFYMESSAGE_CLEANSTORE + VERIFYMESSAGE_CLEANSTORE, + GETPEERINFO_SUCCESS, + GETPEERINFO_FAILED } from "../actions/ControlActions"; import { CLOSEWALLET_SUCCESS } from "actions/WalletLoaderActions"; import { @@ -632,6 +634,16 @@ export default function grpc(state = {}, action) { return { ...state, allAgendas: action.allAgendas }; case GETALLAGENDAS_FAILED: return { ...state, getAllAgendasError: String(action.error) }; + case GETPEERINFO_SUCCESS: + return { + ...state, + peersCount: action.peersCount + }; + case GETPEERINFO_FAILED: + return { + ...state, + getPeerInfoError: action.error + }; default: return state; } diff --git a/app/selectors.js b/app/selectors.js index c04f3ca620..05bf1afaf1 100644 --- a/app/selectors.js +++ b/app/selectors.js @@ -1016,6 +1016,8 @@ export const estimatedFee = compose( estimatedSignedSize ); +export const getPeersCount = get(["grpc", "peersCount"]); + export const totalSpent = createSelector( [totalPreviousOutputAmount, totalOutputAmount, totalAmount], (totalPreviousOutputAmount, totalOutputAmount, totalAmount) => diff --git a/app/wallet/control.js b/app/wallet/control.js index aec8c155ea..2155bf032d 100644 --- a/app/wallet/control.js +++ b/app/wallet/control.js @@ -293,3 +293,10 @@ export const syncVSPTickets = (walletService, passphrase, vspHost, vspPubkey, ac }); }); }); + +export const getPeerInfo = (walletService) => new Promise((ok, fail) => { + const request = new api.GetPeerInfoRequest(); + walletService.getPeerInfo(request, (err, res) => + err ? fail(err) : ok({ ...res }) + ); +});