Changeset View
Changeset View
Standalone View
Standalone View
cashtab/src/components/Home/Home.tsx
| // Copyright (c) 2024 The Bitcoin developers | // Copyright (c) 2024 The Bitcoin developers | ||||
| // Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
| // file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
| import React, { useState, useContext } from 'react'; | import React, { useState, useContext } from 'react'; | ||||
| import styled from 'styled-components'; | import styled from 'styled-components'; | ||||
| import { WalletContext, isWalletContextLoaded } from 'wallet/context'; | import { WalletContext, isWalletContextLoaded } from 'wallet/context'; | ||||
| import { Link } from 'react-router'; | import { Link } from 'react-router'; | ||||
| import TxHistory from './TxHistory'; | import TxHistory from './TxHistory'; | ||||
| import ApiError from 'components/Common/ApiError'; | import ApiError from 'components/Common/ApiError'; | ||||
| import Receive from 'components/Receive/Receive'; | import { Info } from 'components/Common/Atoms'; | ||||
| import { Alert, Info } from 'components/Common/Atoms'; | |||||
| import PrimaryButton, { | import PrimaryButton, { | ||||
| SecondaryButton, | SecondaryButton, | ||||
| PrimaryLink, | PrimaryLink, | ||||
| SecondaryLink, | SecondaryLink, | ||||
| } from 'components/Common/Buttons'; | } from 'components/Common/Buttons'; | ||||
| import { toast } from 'react-toastify'; | import { toast } from 'react-toastify'; | ||||
| import { token as tokenConfig } from 'config/token'; | import { token as tokenConfig } from 'config/token'; | ||||
| import { InlineLoader } from 'components/Common/Spinner'; | import { InlineLoader } from 'components/Common/Spinner'; | ||||
| import ReCAPTCHA from 'react-google-recaptcha'; | import ReCAPTCHA from 'react-google-recaptcha'; | ||||
| import { ReactComponent as WarningIcon } from 'assets/warning.svg'; | |||||
| import ActionButtonRow from 'components/Common/ActionButtonRow'; | |||||
| import { ReactComponent as GiftIcon } from 'assets/gift-icon.svg'; | |||||
| export const Tabs = styled.div` | export const Tabs = styled.div` | ||||
| margin: auto; | margin: auto; | ||||
| display: inline-block; | display: inline-block; | ||||
| text-align: center; | text-align: center; | ||||
| width: 100%; | width: 100%; | ||||
| margin: 20px 0; | margin: 20px 0; | ||||
| `; | `; | ||||
| export const TxHistoryCtn = styled.div` | export const TxHistoryCtn = styled.div` | ||||
| display: flex; | display: flex; | ||||
| flex-direction: column; | flex-direction: column; | ||||
| gap: 6px; | |||||
| color: ${props => props.theme.primaryText}; | color: ${props => props.theme.primaryText}; | ||||
| background-color: ${props => props.theme.primaryBackground}; | @media (max-width: 768px) { | ||||
| padding: 20px; | padding: 0 10px; | ||||
| } | |||||
| `; | |||||
| const BackupWalletAlert = styled(Link)` | |||||
| text-decoration: none; | |||||
| background-color: #454111; | |||||
| color: ${props => props.theme.primaryText}; | |||||
| border-radius: 10px; | border-radius: 10px; | ||||
| padding: 20px; | |||||
| display: flex; | |||||
| align-items: center; | |||||
| width: 100%; | |||||
| margin: 0 auto; | |||||
| gap: 10px; | |||||
| margin-bottom: 10px; | |||||
| svg { | |||||
| flex-shrink: 0; | |||||
| width: 50px; | |||||
| height: 50px; | |||||
| fill: #ffc400; | |||||
| } | |||||
| > div { | |||||
| text-align: left; | |||||
| > p { | |||||
| margin: 0; | |||||
| } | |||||
| } | |||||
| em { | |||||
| font-weight: 700; | |||||
| color: #ffc400; | |||||
| } | |||||
| :hover { | |||||
| background-color: #8d7f0f; | |||||
| color: ${props => props.theme.primaryText}; | |||||
| } | |||||
| @media (max-width: 768px) { | @media (max-width: 768px) { | ||||
| border-radius: 0; | |||||
| padding: 10px; | padding: 10px; | ||||
| gap: 5px; | |||||
| svg { | |||||
| width: 30px; | |||||
| height: 30px; | |||||
| } | |||||
| > div { | |||||
| > p { | |||||
| font-size: var(--text-sm); | |||||
| line-height: var(--text-sm--line-height); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| `; | `; | ||||
| export const AlertLink = styled(Link)` | export const AlertHeader = styled.h2` | ||||
| color: red; | color: ${props => props.theme.primaryText}; | ||||
| :hover { | font-size: var(--text-lg); | ||||
| color: #000; | font-weight: 600; | ||||
| line-height: var(--text-lg--line-height); | |||||
| margin: 0; | |||||
| text-decoration: underline; | |||||
| @media (max-width: 768px) { | |||||
| font-size: var(--text-base); | |||||
| } | } | ||||
| `; | `; | ||||
| export const AddrSwitchContainer = styled.div` | export const AddrSwitchContainer = styled.div` | ||||
| text-align: center; | text-align: center; | ||||
| padding: 6px 0 12px 0; | padding: 6px 0 12px 0; | ||||
| `; | `; | ||||
| export const AirdropButton = styled(PrimaryButton)` | export const AirdropButton = styled(PrimaryButton)` | ||||
| margin-bottom: 0; | margin-bottom: 0; | ||||
| div { | div { | ||||
| margin: auto; | margin: auto; | ||||
| } | } | ||||
| `; | `; | ||||
| export const TokenRewardButton = styled(SecondaryButton)` | export const TokenRewardButton = styled(SecondaryButton)` | ||||
| margin-bottom: 0; | margin-bottom: 0; | ||||
| div { | div { | ||||
| margin: auto; | margin: auto; | ||||
| } | } | ||||
| `; | `; | ||||
| export const ClaimRewardsButton = styled.button<{ disabled?: boolean }>` | |||||
| width: 100%; | |||||
| display: flex; | |||||
| border: none; | |||||
| outline: none; | |||||
| color: ${props => props.theme.primaryText}; | |||||
| justify-content: space-between; | |||||
| align-items: center; | |||||
| gap: 10px; | |||||
| opacity: ${props => (props.disabled ? 0.5 : 1)}; | |||||
| padding: 30px 20px; | |||||
| background: linear-gradient(180deg, #764aa7, #4f178c); | |||||
| border-radius: 10px; | |||||
| cursor: ${props => (props.disabled ? 'not-allowed' : 'pointer')}; | |||||
| margin: 0 auto; | |||||
| :hover { | |||||
| background-color: #8d7f0f; | |||||
| } | |||||
| @media (max-width: 768px) { | |||||
| padding: 15px 10px; | |||||
| } | |||||
| > div { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| text-align: left; | |||||
| gap: 14px; | |||||
| font-size: var(--text-lg); | |||||
| font-weight: 700; | |||||
| line-height: 1.2em; | |||||
| @media (max-width: 768px) { | |||||
| font-size: var(--text-base); | |||||
| gap: 10px; | |||||
| } | |||||
| } | |||||
| svg { | |||||
| width: 30px; | |||||
| height: 30px; | |||||
| @media (max-width: 768px) { | |||||
| width: 20px; | |||||
| height: 20px; | |||||
| flex-shrink: 0; | |||||
| } | |||||
| } | |||||
| span { | |||||
| background: #fff; | |||||
| color: ${props => props.theme.primaryBackground}; | |||||
| padding: 6px 16px; | |||||
| border-radius: 8px; | |||||
| font-size: var(--text-sm); | |||||
| font-weight: 700; | |||||
| opacity: ${props => (props.disabled ? 0.5 : 1)}; | |||||
| @media (max-width: 768px) { | |||||
| font-size: 12px; | |||||
| flex-shrink: 0; | |||||
| } | |||||
| } | |||||
| `; | |||||
| const Home: React.FC = () => { | const Home: React.FC = () => { | ||||
| const ContextValue = useContext(WalletContext); | const ContextValue = useContext(WalletContext); | ||||
| if (!isWalletContextLoaded(ContextValue)) { | if (!isWalletContextLoaded(ContextValue)) { | ||||
| // Confirm we have all context required to load the page | // Confirm we have all context required to load the page | ||||
| return null; | return null; | ||||
| } | } | ||||
| const { apiError, cashtabState, transactionHistory, ecashWallet } = | const { apiError, cashtabState, transactionHistory, ecashWallet } = | ||||
| ContextValue; | ContextValue; | ||||
| ▲ Show 20 Lines • Show All 119 Lines • ▼ Show 20 Lines | return ( | ||||
| {apiError && <ApiError />} | {apiError && <ApiError />} | ||||
| <TxHistoryCtn data-testid="tx-history"> | <TxHistoryCtn data-testid="tx-history"> | ||||
| {transactionHistory === null ? ( | {transactionHistory === null ? ( | ||||
| <div style={{ display: 'flex', justifyContent: 'center' }}> | <div style={{ display: 'flex', justifyContent: 'center' }}> | ||||
| <InlineLoader /> | <InlineLoader /> | ||||
| </div> | </div> | ||||
| ) : ( | ) : ( | ||||
| <> | <> | ||||
| <ActionButtonRow variant="homepage" /> | |||||
| <TxHistory /> | <TxHistory /> | ||||
| {isNewishWallet && ( | {isNewishWallet && ( | ||||
| <> | <> | ||||
| <Info style={{ marginBottom: '20px' }}> | <Info style={{ marginBottom: '20px' }}> | ||||
| ℹ️ Nice, you have some eCash. What can you | ℹ️ Nice, you have some eCash. What can you | ||||
| do? | do? | ||||
| </Info> | </Info> | ||||
| <PrimaryLink to="/create-token"> | <PrimaryLink to="/create-token"> | ||||
| Show All 11 Lines | return ( | ||||
| > | > | ||||
| more uses for XEC | more uses for XEC | ||||
| </a> | </a> | ||||
| </Info> | </Info> | ||||
| </> | </> | ||||
| )} | )} | ||||
| {!hasHistory && ( | {!hasHistory && ( | ||||
| <> | <> | ||||
| <Alert> | <BackupWalletAlert to="/backup"> | ||||
| <p> | <WarningIcon | ||||
| <b> | title="Warning" | ||||
| <AlertLink to="/backup"> | aria-hidden="true" | ||||
| /> | |||||
| <div> | |||||
| <AlertHeader> | |||||
| Backup your wallet | Backup your wallet | ||||
| </AlertLink> | </AlertHeader> | ||||
| </b> | |||||
| </p> | |||||
| <p> | <p> | ||||
| Write down your 12-word seed and keep it | Write down your 12-word seed and | ||||
| in a safe place.{' '} | keep it in a safe place. | ||||
| <br /> | |||||
| <em> | <em> | ||||
| Do not share your backup with | Do not share your backup with | ||||
| anyone. | anyone. | ||||
| </em> | </em> | ||||
| </p> | </p> | ||||
| </Alert> | </div> | ||||
| </BackupWalletAlert> | |||||
| {import.meta.env.VITE_BUILD_ENV !== | {import.meta.env.VITE_BUILD_ENV !== | ||||
| 'extension' && | 'extension' && | ||||
| import.meta.env.VITE_TESTNET !== 'true' && ( | import.meta.env.VITE_TESTNET !== 'true' && ( | ||||
| <> | <> | ||||
| <div | |||||
| style={{ | |||||
| marginBottom: '12px', | |||||
| display: 'flex', | |||||
| justifyContent: 'center', | |||||
| }} | |||||
| > | |||||
| <ReCAPTCHA | |||||
| ref={recaptchaRef} | |||||
| sitekey={ | |||||
| import.meta.env | |||||
| .VITE_RECAPTCHA_SITE_KEY || | |||||
| '' | |||||
| } | |||||
| onChange={ | |||||
| handleRecaptchaChange | |||||
| } | |||||
| /> | |||||
| </div> | |||||
| {cashtabState.wallets.length === | {cashtabState.wallets.length === | ||||
| 1 ? ( | 1 ? ( | ||||
| <AirdropButton | <ClaimRewardsButton | ||||
| onClick={ | onClick={ | ||||
| claimAirdropForNewWallet | claimAirdropForNewWallet | ||||
| } | } | ||||
| disabled={ | disabled={ | ||||
| airdropPending || | airdropPending || | ||||
| !recaptchaToken | !recaptchaToken | ||||
| } | } | ||||
| > | > | ||||
| <div> | |||||
| <GiftIcon /> Free XEC | |||||
| Welcome Bonus! | |||||
| </div> | |||||
| <span> | |||||
| {airdropPending ? ( | {airdropPending ? ( | ||||
| <InlineLoader /> | <InlineLoader /> | ||||
| ) : ( | ) : ( | ||||
| 'Claim Free XEC' | 'Claim Now!' | ||||
| )} | )} | ||||
| </AirdropButton> | </span> | ||||
| </ClaimRewardsButton> | |||||
| ) : ( | ) : ( | ||||
| <TokenRewardButton | <ClaimRewardsButton | ||||
| onClick={ | onClick={ | ||||
| claimTokenRewardsForNewWallet | claimTokenRewardsForNewWallet | ||||
| } | } | ||||
| disabled={ | disabled={ | ||||
| tokenRewardsPending | tokenRewardsPending | ||||
| } | } | ||||
| > | > | ||||
| <div> | |||||
| <GiftIcon /> You've | |||||
| earned Token Rewards! | |||||
| </div> | |||||
| <span> | |||||
| {tokenRewardsPending ? ( | {tokenRewardsPending ? ( | ||||
| <InlineLoader /> | <InlineLoader /> | ||||
| ) : ( | ) : ( | ||||
| 'Claim Token Rewards' | 'Claim Now!' | ||||
| )} | )} | ||||
| </TokenRewardButton> | </span> | ||||
| </ClaimRewardsButton> | |||||
| )} | )} | ||||
| <div | |||||
| style={{ | |||||
| marginTop: '12px', | |||||
| display: 'flex', | |||||
| justifyContent: 'center', | |||||
| }} | |||||
| > | |||||
| <ReCAPTCHA | |||||
| ref={recaptchaRef} | |||||
| sitekey={ | |||||
| import.meta.env | |||||
| .VITE_RECAPTCHA_SITE_KEY || | |||||
| '' | |||||
| } | |||||
| onChange={ | |||||
| handleRecaptchaChange | |||||
| } | |||||
| /> | |||||
| </div> | |||||
| </> | </> | ||||
| )} | )} | ||||
| <Receive /> | |||||
| </> | </> | ||||
| )} | )} | ||||
| </> | </> | ||||
| )} | )} | ||||
| </TxHistoryCtn> | </TxHistoryCtn> | ||||
| </> | </> | ||||
| ); | ); | ||||
| }; | }; | ||||
| export default Home; | export default Home; | ||||