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

Add shortcut buttons and address list to Token Details Modal #1237

Merged
merged 17 commits into from
Feb 14, 2025
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@ const combineBalancesByAddress = (
...combineIsLoading(results)
})

const {
useData: useFetchWalletBalancesTokensByAddress,
DataContextProvider: UseFetchWalletBalancesTokensByAddressContextProvider
} = createDataContext<AddressTokensBalancesQueryFnData, AddressesTokensBalances['data']>({
const { useData, DataContextProvider } = createDataContext<
AddressTokensBalancesQueryFnData,
AddressesTokensBalances['data']
>({
useDataHook: useFetchWalletBalancesTokens,
combineFn: combineBalancesByAddress,
defaultValue: {}
})

const useFetchWalletBalancesTokensByAddress = useData
const UseFetchWalletBalancesTokensByAddressContextProvider = DataContextProvider
nop33 marked this conversation as resolved.
Show resolved Hide resolved

export default useFetchWalletBalancesTokensByAddress
export { UseFetchWalletBalancesTokensByAddressContextProvider }
35 changes: 26 additions & 9 deletions apps/desktop-wallet/src/components/Buttons/ShortcutButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AddressHash } from '@alephium/shared'
import { ALPH } from '@alephium/token-list'
import { ArrowDownToLine, CreditCard, Send, Settings } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
Expand All @@ -9,18 +10,14 @@ import { useAppDispatch, useAppSelector } from '@/hooks/redux'
import { useFetchAddressesHashesWithBalance } from '@/hooks/useAddresses'
import { selectAddressByHash, selectDefaultAddress } from '@/storage/addresses/addressesSelectors'
import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors'
import { TokenId } from '@/types/tokens'

interface ShortcutButtonBaseProps {
analyticsOrigin: string
highlight?: boolean
}

interface ShortcutButtonsGroupWalletProps extends ShortcutButtonBaseProps {
settings?: boolean
lock?: boolean
}

export const ShortcutButtonsGroupWallet = ({ ...buttonProps }: ShortcutButtonsGroupWalletProps) => {
export const ShortcutButtonsGroupWallet = ({ ...buttonProps }: ShortcutButtonBaseProps) => {
const { hash: defaultAddressHash } = useAppSelector(selectDefaultAddress)

return (
Expand All @@ -45,6 +42,22 @@ export const ShortcutButtonsGroupAddress = ({ addressHash, ...buttonProps }: Sho
</ButtonsContainer>
)

interface ShortcutButtonsGroupTokenProps extends ShortcutButtonBaseProps {
tokenId: TokenId
}

export const ShortcutButtonsGroupToken = ({ tokenId, ...buttonProps }: ShortcutButtonsGroupTokenProps) => {
const { hash: defaultAddressHash } = useAppSelector(selectDefaultAddress)

return (
<ButtonsContainer>
<ReceiveButton addressHash={defaultAddressHash} {...buttonProps} />
<SendButton addressHash={defaultAddressHash} tokenId={tokenId} {...buttonProps} />
{tokenId === ALPH.id && <BuyButton addressHash={defaultAddressHash} {...buttonProps} />}
</ButtonsContainer>
)
}

interface SettingsButtonProps extends ShortcutButtonBaseProps {
addressHash?: AddressHash
}
Expand Down Expand Up @@ -93,12 +106,16 @@ const ReceiveButton = ({ addressHash, analyticsOrigin }: ShortcutButtonsGroupAdd
)
}

const SendButton = ({ addressHash, analyticsOrigin }: ShortcutButtonsGroupAddressProps) => {
interface SendButtonProps extends ShortcutButtonsGroupAddressProps {
tokenId?: TokenId
}

const SendButton = ({ addressHash, tokenId, analyticsOrigin }: SendButtonProps) => {
const { sendAnalytics } = useAnalytics()
const { t } = useTranslation()
const fromAddress = useAppSelector((s) => selectAddressByHash(s, addressHash))
const dispatch = useAppDispatch()
const { data: addressesHashesWithBalance } = useFetchAddressesHashesWithBalance()
const { data: addressesHashesWithBalance } = useFetchAddressesHashesWithBalance(tokenId)
const currentNetwork = useAppSelector(selectCurrentlyOnlineNetworkId)

if (!fromAddress) return null
Expand All @@ -114,7 +131,7 @@ const SendButton = ({ addressHash, analyticsOrigin }: ShortcutButtonsGroupAddres
const handleSendClick = () => {
if (isDisabled) return

dispatch(openModal({ name: 'TransferSendModal', props: { initialTxData: { fromAddress } } }))
dispatch(openModal({ name: 'TransferSendModal', props: { initialTxData: { fromAddress, tokenId } } }))
sendAnalytics({ event: 'Send button clicked', props: { origin: analyticsOrigin } })
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface TransferAddressesTxModalContentProps {

const TransferAddressesTxModalContent = ({ data, onSubmit, onCancel }: TransferAddressesTxModalContentProps) => {
const { t } = useTranslation()
const { data: fromAddresses } = useFetchAddressesHashesWithBalance()
const { data: fromAddresses } = useFetchAddressesHashesWithBalance(data.tokenId)

const [fromAddressHash, setFromAddressHash] = useState(data.fromAddress.hash)
const [toAddress, setToAddress] = useStateWithError(data?.toAddress ?? '')
Expand Down Expand Up @@ -61,7 +61,8 @@ const TransferAddressesTxModalContent = ({ data, onSubmit, onCancel }: TransferA
onClick={() =>
onSubmit({
fromAddress,
toAddress: toAddress.value
toAddress: toAddress.value,
tokenId: data.tokenId
})
}
disabled={!isSubmitButtonActive}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ export interface TransferBuildTxModalContentProps {
onSubmit: (data: TransferTxData) => void
}

const defaultAssetAmounts = [{ id: ALPH.id }]

const TransferBuildTxModalContent = ({ data, onSubmit }: TransferBuildTxModalContentProps) => {
const { t } = useTranslation()
const {
Expand All @@ -37,7 +35,9 @@ const TransferBuildTxModalContent = ({ data, onSubmit }: TransferBuildTxModalCon
} = useGasSettings(data?.gasAmount?.toString(), data?.gasPrice)

const [lockTime, setLockTime] = useState(data.lockTime)
const [assetAmounts, setAssetAmounts] = useState<AssetAmountInputType[]>(data.assetAmounts || defaultAssetAmounts)
const [assetAmounts, setAssetAmounts] = useState<AssetAmountInputType[]>(
data.assetAmounts || [{ id: data.tokenId ?? ALPH.id }]
)

const { fromAddress, toAddress } = data

Expand Down
7 changes: 6 additions & 1 deletion apps/desktop-wallet/src/features/send/sendTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AssetAmount } from '@alephium/shared'
import { node } from '@alephium/web3'

import { Address } from '@/types/addresses'
import { TokenId } from '@/types/tokens'

export interface TransferTxData {
fromAddress: Address
Expand All @@ -11,6 +12,7 @@ export interface TransferTxData {
gasAmount?: number
gasPrice?: string
lockTime?: Date
tokenId?: TokenId
}

export interface CallContractTxData {
Expand Down Expand Up @@ -38,7 +40,10 @@ export type TransferTxModalData = PartialTxData<TransferTxData, 'fromAddress'>
export type CallContractTxModalData = PartialTxData<CallContractTxData, 'fromAddress'>
export type DeployContractTxModalData = PartialTxData<DeployContractTxData, 'fromAddress'>

export type TransferAddressesTxModalOnSubmitData = PartialTxData<TransferTxData, 'fromAddress' | 'toAddress'>
export type TransferAddressesTxModalOnSubmitData = PartialTxData<
TransferTxData,
'fromAddress' | 'toAddress' | 'tokenId'
>

export type AddressesTxModalData =
| TransferAddressesTxModalOnSubmitData
Expand Down
25 changes: 17 additions & 8 deletions apps/desktop-wallet/src/hooks/useAddresses.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { ALPH } from '@alephium/token-list'
import { orderBy } from 'lodash'
import { useMemo } from 'react'

import { SkipProp } from '@/api/apiDataHooks/apiDataHooksTypes'
import useFetchLatestTransactionOfEachAddress from '@/api/apiDataHooks/wallet/useFetchLatestTransactionOfEachAddress'
import useFetchWalletBalancesAlphByAddress from '@/api/apiDataHooks/wallet/useFetchWalletBalancesAlphByAddress'
import useFetchWalletBalancesTokensByAddress from '@/api/apiDataHooks/wallet/useFetchWalletBalancesTokensByAddress'
import { useAppSelector } from '@/hooks/redux'
import { useUnsortedAddressesHashes } from '@/hooks/useUnsortedAddresses'
import { selectDefaultAddress } from '@/storage/addresses/addressesSelectors'
import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors'
import { TokenId } from '@/types/tokens'

export const useFetchSortedAddressesHashes = (props?: SkipProp) => {
const isNetworkOffline = useAppSelector(selectCurrentlyOnlineNetworkId) === undefined
Expand Down Expand Up @@ -40,24 +43,30 @@ export const useFetchSortedAddressesHashesWithLatestTx = (props?: SkipProp) => {
}
}

export const useFetchAddressesHashesWithBalance = () => {
export const useFetchAddressesHashesWithBalance = (tokenId?: TokenId) => {
const isNetworkOffline = useAppSelector(selectCurrentlyOnlineNetworkId) === undefined
const allAddressHashes = useUnsortedAddressesHashes()
const { data: addressesAlphBalances, isLoading } = useFetchWalletBalancesAlphByAddress()
const { data: addressesAlphBalances, isLoading: isLoadingAlphBalances } = useFetchWalletBalancesAlphByAddress()
const { data: addressesTokenBalances, isLoading: isLoadingTokenBalances } = useFetchWalletBalancesTokensByAddress()

const filteredAddressHashes = useMemo(
() =>
isNetworkOffline
? allAddressHashes
: allAddressHashes.filter(
(addressHash) =>
addressesAlphBalances[addressHash] && addressesAlphBalances[addressHash].totalBalance !== '0'
),
[addressesAlphBalances, allAddressHashes, isNetworkOffline]
: allAddressHashes.filter((addressHash) => {
if (!tokenId || tokenId === ALPH.id) {
return addressesAlphBalances[addressHash] && addressesAlphBalances[addressHash].totalBalance !== '0'
}

const addressTokenBalance = addressesTokenBalances[addressHash]?.find(({ id }) => id === tokenId)

return addressTokenBalance && addressTokenBalance.totalBalance !== '0'
}),
[addressesAlphBalances, addressesTokenBalances, allAddressHashes, isNetworkOffline, tokenId]
)

return {
data: filteredAddressHashes,
isLoading
isLoading: isLoadingAlphBalances || isLoadingTokenBalances
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useTranslation } from 'react-i18next'
import styled from 'styled-components'

import AnimatedBackground from '@/components/AnimatedBackground'
import { ShortcutButtonsGroupToken } from '@/components/Buttons/ShortcutButtons'
import LabeledWorthOverview from '@/components/LabeledWorthOverview'
import withModal from '@/features/modals/withModal'
import SideModal from '@/modals/SideModal'
Expand All @@ -19,6 +20,7 @@ const TokenDetailsModal = withModal<TokenDetailsModalProps>(({ id, tokenId }) =>
</LabeledWorthOverview>
<Content>
<AnimatedBackground anchorPosition="top" verticalOffset={-300} opacity={0.5} />
<ShortcutButtonsGroupToken tokenId={tokenId} analyticsOrigin="token_details" />
</Content>
</SideModal>
)
Expand Down