diff --git a/packages/extension/src/languages/en.json b/packages/extension/src/languages/en.json index cbe34241fa..e8836cd59c 100644 --- a/packages/extension/src/languages/en.json +++ b/packages/extension/src/languages/en.json @@ -2,9 +2,13 @@ "main.account.chart.total-balance": "Total Balance", "main.account.chart.available-balance": "Available", "main.account.chart.staked-balance": "Staked", + "main.account.chart.reward-balance": "Rewards", "main.account.button.deposit": "Deposit", + "main.account.deposit.paragraph": "Add tokens to your wallet", "main.account.button.send": "Send", "main.account.tooltip.no-asset": "No token in your account. Deposit some tokens to send.", + "main.account.button.buy": "Buy", + "main.account.buy.paragraph": "Buy and add native tokens to your wallet", "main.modal.select-account.title": "Select your account", "main.modal.select-account.button.select": "Select Account", diff --git a/packages/extension/src/languages/ko.json b/packages/extension/src/languages/ko.json index 8f6ccb6a41..75fcd1d89d 100644 --- a/packages/extension/src/languages/ko.json +++ b/packages/extension/src/languages/ko.json @@ -2,9 +2,13 @@ "main.account.chart.total-balance": "총 자산", "main.account.chart.available-balance": "전송 가능한 자산", "main.account.chart.staked-balance": "스테이킹된 자산", + "main.account.chart.reward-balance": "보상", "main.account.button.deposit": "입금", "main.account.button.send": "보내기", "main.account.tooltip.no-asset": "계정에 자산이 없습니다. 자산을 입금해주세요.", + "main.account.deposit.paragraph": "지갑에 토큰 추가", + "main.account.button.buy": "구입하다", + "main.account.buy.paragraph": "지갑에 기본 토큰 구매 및 추가", "main.modal.select-account.title": "계정을 선택하세요", "main.modal.select-account.button.select": "계정 선택", diff --git a/packages/extension/src/pages/main/asset.module.scss b/packages/extension/src/pages/main/asset.module.scss index 6a5bdeabd6..411abbf657 100644 --- a/packages/extension/src/pages/main/asset.module.scss +++ b/packages/extension/src/pages/main/asset.module.scss @@ -96,16 +96,33 @@ } .progressDiv { - background-color: #11cdef; + background-color: #d43bf6; border-radius: 2px; + position: relative; } -.progress { +.progressAvailable { background-color: #5e72e4; height: 20px; border-radius: 2px; transition: 1s ease; transition-delay: 0.5s; + z-index: 2; + position: absolute; +} + +.progressStake { + background-color: #11cdef; + height: 20px; + border-radius: 2px; + transition: 1.5s ease; + transition-delay: 0.5s; + position: static; + z-index: 1; +} + +.hr { + margin: $main-card-padding 0; } .emptyState { @@ -121,6 +138,11 @@ font-size: 20px; } + .buyButton { + margin-top: 8px; + width: 100%; + } + img { width: 170px; height: 170px; diff --git a/packages/extension/src/pages/main/asset.tsx b/packages/extension/src/pages/main/asset.tsx index eb3a1c6fba..a08f52fab3 100644 --- a/packages/extension/src/pages/main/asset.tsx +++ b/packages/extension/src/pages/main/asset.tsx @@ -8,6 +8,7 @@ import styleAsset from "./asset.module.scss"; import { TxButtonView } from "./tx-button"; import walletIcon from "../../public/assets/icon/wallet.png"; import buyIcon from "../../public/assets/icon/buy.png"; +import { DepositView } from "./deposit"; export const ProgressBar = ({ width, @@ -16,24 +17,32 @@ export const ProgressBar = ({ width: number; data: number[]; }) => { - const [value, setValue] = useState(0); + const [values, setValues] = useState([0, 0]); useEffect(() => { - const total = data[0] + data[1]; - const percentage = data[0] / total; - setValue(percentage * width); - }, [width, data[0], data[1]]); + const total = data[0] + data[1] + data[2]; + const percentageAvailable = data[0] / total; + const percentageStake = data[1] / total; + setValues([percentageAvailable * width, percentageStake * width]); + }, [width, data[0], data[1], data[2]]); return (
-
+
+
); }; -const EmptyState: FunctionComponent = () => { +const EmptyState = ({ denom, chainId }: { denom: string; chainId: string }) => { return (

No funds added

@@ -41,9 +50,19 @@ const EmptyState: FunctionComponent = () => {

That’s okay, you can deposit tokens to your address or buy some.

- + + {chainId == "fetchhub-4" && ( + + + + )}
); }; @@ -75,12 +94,22 @@ export const AssetView: FunctionComponent = observer(() => { .getQueryBech32Address(accountInfo.bech32Address) .total.upperCase(true); + const rewards = queries.cosmos.queryRewards.getQueryBech32Address( + accountInfo.bech32Address + ); + + const stakableReward = rewards.stakableReward; + const stakedSum = delegated.add(unbonding); - const total = stakable.add(stakedSum); + const total = stakable.add(stakedSum).add(stakableReward); const stakablePrice = priceStore.calculatePrice(stakable, fiatCurrency); const stakedSumPrice = priceStore.calculatePrice(stakedSum, fiatCurrency); + const stakableRewardPrice = priceStore.calculatePrice( + stakableReward, + fiatCurrency + ); const totalPrice = priceStore.calculatePrice(total, fiatCurrency); @@ -93,6 +122,9 @@ export const AssetView: FunctionComponent = observer(() => { stakedSumPrice ? parseFloat(stakedSumPrice.toDec().toString()) : parseFloat(stakedSum.toDec().toString()), + stakableRewardPrice + ? parseFloat(stakableRewardPrice.toDec().toString()) + : parseFloat(stakableReward.toDec().toString()), ]; const hasBalance = totalPrice @@ -100,7 +132,12 @@ export const AssetView: FunctionComponent = observer(() => { : !total.toDec().isZero(); if (!hasBalance) { - return ; + return ( + + ); } return ( @@ -174,9 +211,25 @@ export const AssetView: FunctionComponent = observer(() => { {stakedSum.shrink(true).maxDecimals(6).toString()}
+
+
+ +
+
+
+ {stakableReward.shrink(true).maxDecimals(6).toString()} +
+
+
+ ); }); diff --git a/packages/extension/src/pages/main/deposit.module.scss b/packages/extension/src/pages/main/deposit.module.scss new file mode 100644 index 0000000000..6a91d12df8 --- /dev/null +++ b/packages/extension/src/pages/main/deposit.module.scss @@ -0,0 +1,44 @@ +@import "../../styles/var"; + +.container-inner { + display: flex; + flex-direction: row; + + height: 48px; + justify-content: center; + align-items: center; + + .paragraph-main { + line-height: 1.35; + } + + .paragraph-sub { + line-height: 1.35; + color: #8898aa; + } + + .vertical { + display: flex; + flex-direction: column; + } + + .button { + min-width: 76px; + padding: 6px 10px 4px; + } +} + +.deposit-modal { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + .qrcode { + margin-bottom: 20px; + } +} + +.hr { + margin: $main-card-padding 0; +} diff --git a/packages/extension/src/pages/main/deposit.tsx b/packages/extension/src/pages/main/deposit.tsx new file mode 100644 index 0000000000..11e976e908 --- /dev/null +++ b/packages/extension/src/pages/main/deposit.tsx @@ -0,0 +1,139 @@ +import React, { useEffect, useState, FunctionComponent, useRef } from "react"; +import { Button } from "reactstrap"; +import Modal from "react-modal"; + +import styleDeposit from "./deposit.module.scss"; +import classnames from "classnames"; +import { observer } from "mobx-react-lite"; +import { useStore } from "../../stores"; +import { FormattedMessage } from "react-intl"; + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const QrCode = require("qrcode"); + +const DepositModal: FunctionComponent<{ + bech32Address: string; +}> = ({ bech32Address }) => { + const qrCodeRef = useRef(null); + + useEffect(() => { + if (qrCodeRef.current && bech32Address) { + QrCode.toCanvas(qrCodeRef.current, bech32Address); + } + }, [bech32Address]); + + return ( +
+

Scan QR code

+ +
+ ); +}; + +export const DepositView: FunctionComponent = observer(() => { + const { accountStore, chainStore } = useStore(); + + const accountInfo = accountStore.getAccount(chainStore.current.chainId); + const [isDepositOpen, setIsDepositOpen] = useState(false); + + return ( +
+
+ { + setIsDepositOpen(false); + }} + > + + + +
+

+ {" "} + {chainStore.current.stakeCurrency.coinDenom.toUpperCase()} +

+

+ +

+
+
+ +
+ + {chainStore.current.chainId == "fetchhub-4" && ( +
+
+ +
+
+

+ {" "} + {chainStore.current.stakeCurrency.coinDenom.toUpperCase()} +

+

+ +

+
+ +
+ )} +
+ ); +}); diff --git a/packages/extension/src/pages/main/index.tsx b/packages/extension/src/pages/main/index.tsx index 0cdbf7bd34..8cc4be1691 100644 --- a/packages/extension/src/pages/main/index.tsx +++ b/packages/extension/src/pages/main/index.tsx @@ -20,6 +20,7 @@ import { ChainUpdaterService } from "@keplr-wallet/background"; import { DenomHelper } from "@keplr-wallet/common"; import { Dec } from "@keplr-wallet/unit"; import bellIcon from "../../public/assets/icon/bell.png"; +import { DepositView } from "./deposit"; // import { IBCTransferView } from "./ibc-transfer"; export const MainPage: FunctionComponent = observer(() => { @@ -118,7 +119,6 @@ export const MainPage: FunctionComponent = observer(() => {
- {chainStore.current.walletUrlForStaking ? "" : null} {hasTokens ? ( {} diff --git a/packages/extension/src/pages/main/tx-button.module.scss b/packages/extension/src/pages/main/tx-button.module.scss index d040a721c7..7765a3c5f5 100644 --- a/packages/extension/src/pages/main/tx-button.module.scss +++ b/packages/extension/src/pages/main/tx-button.module.scss @@ -10,7 +10,7 @@ padding-left: 1rem !important; margin-right: $layout-margin; - + margin-left: 0px; &:last-child { margin-right: 0; } diff --git a/packages/extension/src/pages/main/tx-button.tsx b/packages/extension/src/pages/main/tx-button.tsx index 45b6f46203..9fd0e71982 100644 --- a/packages/extension/src/pages/main/tx-button.tsx +++ b/packages/extension/src/pages/main/tx-button.tsx @@ -1,12 +1,6 @@ -import React, { - FunctionComponent, - useEffect, - useMemo, - useRef, - useState, -} from "react"; +import React, { FunctionComponent, useMemo, useState } from "react"; -import { Button, Tooltip } from "reactstrap"; +import { Button } from "reactstrap"; import styleTxButton from "./tx-button.module.scss"; @@ -14,7 +8,7 @@ import { observer } from "mobx-react-lite"; import { useStore } from "../../stores"; -import Modal from "react-modal"; +import { useNotification } from "../../components/notification"; import { FormattedMessage } from "react-intl"; import { useHistory } from "react-router"; @@ -23,41 +17,21 @@ import { Dec } from "@keplr-wallet/unit"; import classnames from "classnames"; import send from "../../public/assets/icon/send.png"; -import swap from "../../public/assets/icon/swap.png"; +import reward from "../../public/assets/icon/reward.png"; import stake from "../../public/assets/icon/stake.png"; import activeSend from "../../public/assets/icon/activeSend.png"; -import activeSwap from "../../public/assets/icon/activeSwap.png"; +import activeReward from "../../public/assets/icon/activeReward.png"; import activeStake from "../../public/assets/icon/activeStake.png"; -// eslint-disable-next-line @typescript-eslint/no-var-requires -const QrCode = require("qrcode"); - -const DepositModal: FunctionComponent<{ - bech32Address: string; -}> = ({ bech32Address }) => { - const qrCodeRef = useRef(null); - - useEffect(() => { - if (qrCodeRef.current && bech32Address) { - QrCode.toCanvas(qrCodeRef.current, bech32Address); - } - }, [bech32Address]); - - return ( -
-

Scan QR code

- -
- ); -}; - export const TxButtonView: FunctionComponent = observer(() => { const { accountStore, chainStore, queriesStore, analyticsStore } = useStore(); + const notification = useNotification(); + const [isActiveSend, setIsActiveSend] = useState(false); const [isActiveStake, setIsActiveStake] = useState(false); - const [isActiveSwap, setIsActiveSwap] = useState(false); + const [isActiveReward, setIsActiveReward] = useState(false); const accountInfo = accountStore.getAccount(chainStore.current.chainId); const queries = queriesStore.get(chainStore.current.chainId); @@ -65,17 +39,12 @@ export const TxButtonView: FunctionComponent = observer(() => { accountInfo.bech32Address ); - const [isDepositOpen, setIsDepositOpen] = useState(false); - - const [sendTooltipOpen, setSendTooltipOpen] = useState(false); - const [stakeTooltipOpen, setStakeTooltipOpen] = useState(false); const history = useHistory(); const hasAssets = queryBalances.balances.find((bal) => bal.balance.toDec().gt(new Dec(0))) !== undefined; - const sendBtnRef = useRef(null); const rewards = queries.cosmos.queryRewards.getQueryBech32Address( accountInfo.bech32Address ); @@ -84,37 +53,55 @@ export const TxButtonView: FunctionComponent = observer(() => { accountInfo.bech32Address ).stakable; - const isRewardExist = rewards.rewards.length > 0; + const isRewardExist = rewards.stakableReward.toDec().gt(new Dec(0)); const isStakableExist = useMemo(() => { return stakable.balance.toDec().gt(new Dec(0)); }, [stakable.balance]); - const stakeBtnRef = useRef(null); - return ( -
- { + if (accountInfo.isReadyToSendMsgs) { + try { + // When the user delegated too many validators, + // it can't be sent to withdraw rewards from all validators due to the block gas limit. + // So, to prevent this problem, just send the msgs up to 8. + await accountInfo.cosmos.sendWithdrawDelegationRewardMsgs( + rewards.getDescendingPendingRewardValidatorAddresses(8), + "", + undefined, + undefined, + { + onBroadcasted: () => { + analyticsStore.logEvent("Claim reward tx broadcasted", { + chainId: chainStore.current.chainId, + chainName: chainStore.current.chainName, + }); + }, + } + ); + + history.replace("/"); + } catch (e: any) { + history.replace("/"); + notification.push({ + type: "warning", + placement: "top-center", + duration: 5, + content: `Fail to withdraw rewards: ${e.message}`, + canDelete: true, + transition: { + duration: 0.25, }, - }} - isOpen={isDepositOpen} - onRequestClose={() => { - setIsDepositOpen(false); - }} - > - - + }); + } + } + }; + return ( +
- {/* - "Disabled" property in button tag will block the mouse enter/leave events. - So, tooltip will not work as expected. - To solve this problem, don't add "disabled" property to button tag and just add "disabled" class manually. - */} - - {!hasAssets ? ( - setSendTooltipOpen((value) => !value)} - fade - > - - - ) : null} + { } }} > - {/* - "Disabled" property in button tag will block the mouse enter/leave events. - So, tooltip will not work as expected. - To solve this problem, don't add "disabled" property to button tag and just add "disabled" class manually. - */} - {!isStakableExist ? ( - setStakeTooltipOpen((value) => !value)} - fade - > - - - ) : null}
); diff --git a/packages/extension/src/public/assets/icon/activeReward.png b/packages/extension/src/public/assets/icon/activeReward.png new file mode 100644 index 0000000000..56d5deef01 Binary files /dev/null and b/packages/extension/src/public/assets/icon/activeReward.png differ diff --git a/packages/extension/src/public/assets/icon/reward.png b/packages/extension/src/public/assets/icon/reward.png new file mode 100644 index 0000000000..5a3fc6569a Binary files /dev/null and b/packages/extension/src/public/assets/icon/reward.png differ diff --git a/packages/extension/src/styles/global.scss b/packages/extension/src/styles/global.scss index d2d559ce51..a3f70ba12f 100644 --- a/packages/extension/src/styles/global.scss +++ b/packages/extension/src/styles/global.scss @@ -1,5 +1,9 @@ @charset "utf-8"; +$theme-colors: ( + "primary": #3b82f6, +); + @import "../../../../node_modules/argon-dashboard-react/src/assets/plugins/nucleo/css/nucleo.css"; $fa-font-path: "~@fortawesome/fontawesome-free/webfonts";