Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(wallet): Allow Creating Wallet on Buy Screen (uplift to 1.74.x) #26935

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions components/brave_wallet/browser/brave_wallet_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,11 @@ inline constexpr webui::LocalizedString kLocalizedStrings[] = {
{"braveWalletNoResultsFound", IDS_BRAVE_WALLET_NO_RESULTS_FOUND},
{"braveWalletTryDifferentKeywords",
IDS_BRAVE_WALLET_TRY_DIFFERENT_KEYWORDS},
{"braveWalletCreateAccountToBuyTitle",
IDS_BRAVE_WALLET_CREATE_ACCOUNT_TO_BUY_TITLE},
{"braveWalletCreateAccountToBuyDescription",
IDS_BRAVE_WALLET_CREATE_ACCOUNT_TO_BUY_DESCRIPTION},
{"braveWalletAccountName", IDS_BRAVE_WALLET_ACCOUNT_NAME},
{"braveWalletBuyWithSardine", IDS_BRAVE_WALLET_BUY_WITH_SARDINE},
{"braveWalletBuyWithTransak", IDS_BRAVE_WALLET_BUY_WITH_TRANSAK},
{"braveWalletBuyWithStripe", IDS_BRAVE_WALLET_BUY_WITH_STRIPE},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const IconWrapper = styled.div<{
`

export type IconSize =
| 'massive'
| 'huge'
| 'big'
| 'medium'
Expand All @@ -49,6 +50,8 @@ interface IconProps extends AssetIconProps {

function getNetworkIconWidthFromSize(size?: IconSize): string {
switch (size) {
case 'massive':
return '64px'
case 'huge':
return '32px'
case 'big':
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

import styled from 'styled-components'
import LeoInput from '@brave/leo/react/input'
import LeoDialog from '@brave/leo/react/dialog'
import {
layoutPanelWidth //
} from '../../../../../components/desktop/wallet-page-wrapper/wallet-page-wrapper.style'

export const Dialog = styled(LeoDialog).attrs({
size: window.innerWidth <= layoutPanelWidth ? 'mobile' : 'normal'
})`
--leo-dialog-padding: 32px;
--leo-dialog-width: 600px;
--leo-dialog-backdrop-filter: blur(8px);
`

export const Input = styled(LeoInput)`
width: 100%;
max-width: 400px;
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

import * as React from 'react'
import { skipToken } from '@reduxjs/toolkit/query'
import { DialogProps } from '@brave/leo/react/dialog'
import { InputEventDetail } from '@brave/leo/react/input'
import Button from '@brave/leo/react/button'

// Queries
import { useAccountsQuery } from '../../../../../common/slices/api.slice.extra'
import {
useAddAccountMutation,
useGetNetworkQuery
} from '../../../../../common/slices/api.slice'

// Selectors
import {
useSafeUISelector //
} from '../../../../../common/hooks/use-safe-selector'
import { UISelectors } from '../../../../../common/selectors'

// Types
import { MeldCryptoCurrency, BraveWallet } from '../../../../../constants/types'

// Utils
import { getLocale } from '../../../../../../common/locale'
import { suggestNewAccountName } from '../../../../../utils/address-utils'
import { keyringIdForNewAccount } from '../../../../../utils/account-utils'
import { getMeldTokensCoinType } from '../../../../../utils/meld_utils'

// Components
import {
BottomSheet //
} from '../../../../../components/shared/bottom_sheet/bottom_sheet'
import {
CreateNetworkIcon //
} from '../../../../../components/shared/create-network-icon'

// Styled Components
import { Input, Dialog } from './create_account.style'
import { DialogTitle } from '../shared/style'
import { Column, Row, Text } from '../../../../../components/shared/style'

interface Props extends DialogProps {
isOpen: boolean
token?: MeldCryptoCurrency
onSelectToken: (asset: MeldCryptoCurrency) => void
onClose: () => void
}

export const CreateAccount = (props: Props) => {
const { token, onSelectToken, onClose, isOpen, ...rest } = props

// Selectors
const isPanel = useSafeUISelector(UISelectors.isPanel)

// State
const [accountName, setAccountName] = React.useState<string>('')

// Queries
const { accounts } = useAccountsQuery()
const { data: network } = useGetNetworkQuery(
token?.chainId
? {
chainId: token.chainId,
coin: getMeldTokensCoinType(token)
}
: skipToken
)

// Computed
const suggestedAccountName = network
? suggestNewAccountName(accounts, network)
: ''

// Mutations
const [addAccount] = useAddAccountMutation()

// Methods
const onClickCreateAccount = React.useCallback(async () => {
if (!network || !token) {
return
}
try {
const account = await addAccount({
coin: network.coin,
keyringId: keyringIdForNewAccount(network.coin, network.chainId),
accountName: suggestedAccountName
}).unwrap()

if (account) {
onSelectToken(token)
}
} catch (error) {
console.log(error)
}
}, [addAccount, network, suggestedAccountName, onSelectToken, token])

const handleAccountNameChanged = (detail: InputEventDetail) => {
setAccountName(detail.value)
}

const handleKeyDown = React.useCallback(
(detail: InputEventDetail) => {
if ((detail.innerEvent as unknown as KeyboardEvent).key === 'Enter') {
onClickCreateAccount()
}
},
[onClickCreateAccount]
)

// Effects
React.useEffect(() => {
setAccountName(suggestedAccountName)
}, [suggestedAccountName])

// Memos
const createAccountContent = React.useMemo(() => {
return (
<>
<DialogTitle slot='title'>
{getLocale('braveWalletCreateAccountButton')}
</DialogTitle>
<Column
gap='16px'
margin='0px 0px 46px 0px'
>
<CreateNetworkIcon
network={network}
size='massive'
/>
<Text
textSize='22px'
textColor='primary'
isBold={true}
>
{token
? getLocale('braveWalletCreateAccountToBuyTitle').replace(
'$1',
token.name ?? ''
)
: ''}
</Text>
<Text
textSize='14px'
textColor='tertiary'
isBold={false}
>
{token
? getLocale('braveWalletCreateAccountToBuyDescription')
.replace('$1', token.currencyCode ?? '')
.replace('$2', token.name ?? '')
: ''}
</Text>
</Column>
<Row margin='0px 0px 46px 0px'>
<Input
value={accountName}
placeholder={getLocale('braveWalletAddAccountPlaceholder')}
onInput={handleAccountNameChanged}
onKeyDown={handleKeyDown}
maxlength={BraveWallet.ACCOUNT_NAME_MAX_CHARACTER_LENGTH}
>
<Text
textSize='12px'
textColor='primary'
isBold={true}
>
{getLocale('braveWalletAccountName')}
</Text>
</Input>
</Row>
<Row gap='16px'>
<Button
kind='outline'
onClick={onClose}
>
{getLocale('braveWalletButtonCancel')}
</Button>
<Button
kind='filled'
onClick={onClickCreateAccount}
isDisabled={accountName === ''}
>
{getLocale('braveWalletCreateAccountButton')}
</Button>
</Row>
</>
)
}, [
network,
token,
accountName,
onClose,
onClickCreateAccount,
handleKeyDown
])

if (isPanel) {
return (
<BottomSheet
onClose={onClose}
isOpen={isOpen}
>
<Column
fullWidth={true}
padding='0px 16px'
height='90vh'
>
{createAccountContent}
</Column>
</BottomSheet>
)
}

return (
<Dialog
isOpen={isOpen}
onClose={onClose}
showClose
backdropClickCloses={false}
{...rest}
>
{createAccountContent}
</Dialog>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { SelectCurrency } from './components/select_currency/select_currency'
import { SelectAccount } from './components/select_account/select_account'
import { SelectAsset } from './components/select_asset/select_asset'
import { BuyQuote } from './components/buy_quote/buy_quote'
import { CreateAccount } from './components/create_account/create_account'

// Styled Components
import {
Expand Down Expand Up @@ -106,7 +107,10 @@ export const FundWalletScreen = ({ isAndroid }: Props) => {
onBuy,
searchTerm,
onSearch,
hasQuoteError
hasQuoteError,
showCreateAccount,
onCloseCreateAccount,
pendingSelectedToken
} = useBuy()

// Redux
Expand Down Expand Up @@ -389,6 +393,13 @@ export const FundWalletScreen = ({ isAndroid }: Props) => {
selectedAsset={selectedAsset}
onClose={() => setIsAccountDialogOpen(false)}
/>

<CreateAccount
isOpen={showCreateAccount}
token={pendingSelectedToken || selectedAsset}
onClose={onCloseCreateAccount}
onSelectToken={onSelectToken}
/>
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {
MeldCryptoQuote,
MeldPaymentMethod,
CryptoBuySessionData,
CryptoWidgetCustomerData
CryptoWidgetCustomerData,
WalletRoutes
} from '../../../../constants/types'

// Hooks
Expand Down Expand Up @@ -136,6 +137,10 @@ export const useBuy = () => {
string | undefined
>(undefined)
const [searchTerm, setSearchTerm] = useState<string>('')
const [showCreateAccount, setShowCreateAccount] = useState<boolean>(false)
const [pendingSelectedToken, setPendingSelectedToken] = useState<
MeldCryptoCurrency | undefined
>(undefined)

// Mutations
const [generateQuotes] = useGenerateMeldCryptoQuotesMutation()
Expand Down Expand Up @@ -396,6 +401,13 @@ export const useBuy = () => {
selectedAccount.accountId.coin !== incomingAssetsCoinType
? getFirstAccountByCoinType(incomingAssetsCoinType, accounts)
: selectedAccount
if (!accountToUse) {
setPendingSelectedToken(asset)
setShowCreateAccount(true)
return
}
setShowCreateAccount(false)
setPendingSelectedToken(undefined)
history.replace(makeFundWalletRoute(asset, accountToUse))
setQuotes([])

Expand Down Expand Up @@ -464,6 +476,13 @@ export const useBuy = () => {
]
)

const onCloseCreateAccount = useCallback(() => {
if (!pendingSelectedToken) {
history.push(WalletRoutes.FundWalletPageStart)
}
setShowCreateAccount(false)
}, [pendingSelectedToken, history])

// Effects
useEffect(() => {
if (fiatCurrencies && fiatCurrencies.length > 0 && !selectedCurrency) {
Expand Down Expand Up @@ -509,6 +528,17 @@ export const useBuy = () => {
}
}, [selectedAsset, currencyCode, chainId, quotes, hasQuoteError])

useEffect(() => {
if (
selectedAsset.currencyCode !== DEFAULT_ASSET.currencyCode &&
currencyCode !== undefined &&
chainId !== undefined &&
selectedAccount === undefined
) {
setShowCreateAccount(true)
}
}, [selectedAsset, currencyCode, chainId, selectedAccount])

return {
selectedAsset,
selectedCurrency,
Expand Down Expand Up @@ -545,6 +575,9 @@ export const useBuy = () => {
cryptoEstimate,
hasQuoteError,
isLoadingServiceProvider,
reset
reset,
showCreateAccount,
onCloseCreateAccount,
pendingSelectedToken
}
}
Loading
Loading