From fe77c8ee4c2d1a8c59d5cb9be18f3d419b294279 Mon Sep 17 00:00:00 2001 From: Hayden Briese Date: Thu, 22 Aug 2024 17:18:11 +1000 Subject: [PATCH] feat(app): address sheet and pressable address label --- app/src/app/(sheet)/address/[address].tsx | 105 ++++++++++++++++++ app/src/components/QrModal.tsx | 6 +- .../activity/IncomingTransferItem.tsx | 10 +- app/src/components/address/AddressLabel.tsx | 38 ++++--- app/src/components/policy/ApproverItem.tsx | 8 +- .../transaction/OperationDetails.tsx | 8 +- .../transaction/PendingApprovalItem.tsx | 5 +- app/src/util/theme/icons.tsx | 2 +- 8 files changed, 150 insertions(+), 32 deletions(-) create mode 100644 app/src/app/(sheet)/address/[address].tsx diff --git a/app/src/app/(sheet)/address/[address].tsx b/app/src/app/(sheet)/address/[address].tsx new file mode 100644 index 000000000..236d793de --- /dev/null +++ b/app/src/app/(sheet)/address/[address].tsx @@ -0,0 +1,105 @@ +import { useAddressLabel } from '#/address/AddressLabel'; +import { AddressIcon } from '#/Identicon/AddressIcon'; +import { PressableOpacity } from '#/PressableOpacity'; +import { Scrollable } from '#/Scrollable'; +import { Sheet } from '#/sheet/Sheet'; +import { ContactOutlineIcon, OutboundIcon, ShareIcon, WebIcon } from '@theme/icons'; +import { CORNER, ICON_SIZE } from '@theme/paper'; +import { createStyles, useStyles } from '@theme/styles'; +import { CHAINS } from 'chains'; +import { Link } from 'expo-router'; +import { asAddress, asChain } from 'lib'; +import { View } from 'react-native'; +import { Text } from 'react-native-paper'; +import { z } from 'zod'; +import { useLocalParams } from '~/hooks/useLocalParams'; +import { useSelectedAccount } from '~/hooks/useSelectedAccount'; +import { share } from '~/lib/share'; +import { zUAddress } from '~/lib/zod'; + +const Params = z.object({ address: zUAddress() }); + +export default function AddressSheet() { + const { styles } = useStyles(stylesheet); + const { address } = useLocalParams(Params); + const account = useSelectedAccount(); + + const explorer = CHAINS[asChain(address)].blockExplorers?.native.url; + + return ( + + + + + {useAddressLabel(address)} + + + {asAddress(address)} + + + + + share({ url: address })}> + + Share + + + {explorer && ( + + + + Explorer + + + )} + + + + + Contact + + + + {account && ( + + + + Send + + + )} + + + ); +} + +const stylesheet = createStyles(({ colors }) => ({ + container: { + paddingBottom: 16, + }, + headerContainer: { + alignItems: 'center', + gap: 8, + marginHorizontal: 16, + marginVertical: 8, + }, + address: { + color: colors.onSurfaceVariant, + }, + actions: { + justifyContent: 'center', + gap: 16, + paddingHorizontal: 16, + marginVertical: 8, + }, + action: { + alignItems: 'center', + gap: 8, + width: 80, + paddingVertical: 8, + borderRadius: CORNER.m, + }, +})); diff --git a/app/src/components/QrModal.tsx b/app/src/components/QrModal.tsx index 84dfbcb90..1e07bbec1 100644 --- a/app/src/components/QrModal.tsx +++ b/app/src/components/QrModal.tsx @@ -35,9 +35,9 @@ export function QrModal({ address, actions }: QrModalProps) { - - {isUAddress(address) && } - + {isUAddress(address) && ( + + )} diff --git a/app/src/components/activity/IncomingTransferItem.tsx b/app/src/components/activity/IncomingTransferItem.tsx index 7f3d19f97..a416df689 100644 --- a/app/src/components/activity/IncomingTransferItem.tsx +++ b/app/src/components/activity/IncomingTransferItem.tsx @@ -1,4 +1,4 @@ -import { useAddressLabel } from '#/address/AddressLabel'; +import { AddressLabel } from '#/address/AddressLabel'; import { Timestamp } from '#/format/Timestamp'; import { ListItem, ListItemProps } from '#/list/ListItem'; import { ListItemSkeleton } from '#/list/ListItemSkeleton'; @@ -40,7 +40,6 @@ export interface IncomingTransferItemProps extends Partial { function IncomingTransferItem_(props: IncomingTransferItemProps) { const transfer = useFragment(Transfer, props.transfer); - const from = useAddressLabel(asUAddress(transfer.from, transfer.account.chain)); const amount = useTokenAmount({ token: transfer.token, amount: transfer.amount }); return ( @@ -58,7 +57,12 @@ function IncomingTransferItem_(props: IncomingTransferItemProps) { } - headline={`Received ${amount} from ${from}`} + headline={ + <> + {`Received ${amount} from `} + + + } supporting={} trailing={({ Text }) => transfer.value !== null && transfer.value !== undefined ? ( diff --git a/app/src/components/address/AddressLabel.tsx b/app/src/components/address/AddressLabel.tsx index f02b0c73b..d9defd8aa 100644 --- a/app/src/components/address/AddressLabel.tsx +++ b/app/src/components/address/AddressLabel.tsx @@ -3,26 +3,38 @@ import { useLazyQuery } from '~/api'; import { graphql } from 'relay-runtime'; import { AddressLabelQuery } from '~/api/__generated__/AddressLabelQuery.graphql'; import { truncateAddr } from '~/util/format'; +import { Text, TextProps } from 'react-native-paper'; +import { useRouter } from 'expo-router'; const Query = graphql` - query AddressLabelQuery($address: UAddress!, $skip: Boolean!) { - label(address: $address) @skip(if: $skip) + query AddressLabelQuery($address: UAddress!) { + label(address: $address) } `; -export const useAddressLabel = (address: A) => { - const { label } = useLazyQuery(Query, { - address: address!, - skip: !address, - }); +export function useAddressLabel(address: UAddress) { + const { label } = useLazyQuery( + Query, + { address }, + { fetchPolicy: 'store-or-network' }, + ); - return (address ? label || truncateAddr(address) : undefined) as A extends undefined - ? string | undefined - : string; -}; + return label || truncateAddr(address); +} -export interface AddressLabelProps { +export interface AddressLabelProps extends Omit, 'children'> { address: UAddress; } -export const AddressLabel = ({ address }: AddressLabelProps) => useAddressLabel(address); +export function AddressLabel({ address, ...textProps }: AddressLabelProps) { + const router = useRouter(); + + return ( + router.push({ pathname: `/address/[address]`, params: { address } })} + > + {useAddressLabel(address)} + + ); +} diff --git a/app/src/components/policy/ApproverItem.tsx b/app/src/components/policy/ApproverItem.tsx index 48edf2c98..70ec39bbb 100644 --- a/app/src/components/policy/ApproverItem.tsx +++ b/app/src/components/policy/ApproverItem.tsx @@ -1,7 +1,6 @@ import { UAddress } from 'lib'; -import { useAddressLabel } from '#/address/AddressLabel'; +import { AddressLabel } from '#/address/AddressLabel'; import { ListItem, ListItemProps } from '#/list/ListItem'; -import { truncateAddr } from '~/util/format'; import { I18nManager } from 'react-native'; import { RectButton, Swipeable } from 'react-native-gesture-handler'; import { CloseIcon, DeleteIcon } from '@theme/icons'; @@ -17,8 +16,6 @@ export interface ApproverItemProps extends Partial { export function ApproverItem({ address, remove, ...props }: ApproverItemProps) { const { styles } = useStyles(stylesheet); - const label = useAddressLabel(address); - const truncated = truncateAddr(address); const ref = useRef(null); @@ -46,8 +43,7 @@ export function ApproverItem({ address, remove, ...props }: ApproverItemProps) { > } - headline={label} - supporting={label !== truncated ? truncated : undefined} + headline={} trailing={} containerStyle={styles.itemContainer} {...props} diff --git a/app/src/components/transaction/OperationDetails.tsx b/app/src/components/transaction/OperationDetails.tsx index 14cfd1d7f..77264037b 100644 --- a/app/src/components/transaction/OperationDetails.tsx +++ b/app/src/components/transaction/OperationDetails.tsx @@ -2,7 +2,7 @@ import { ClockOutlineIcon } from '@theme/icons'; import { UAddress, asChain, asUAddress } from 'lib'; import { DateTime } from 'luxon'; import { match } from 'ts-pattern'; -import { AddressLabel, useAddressLabel } from '#/address/AddressLabel'; +import { AddressLabel } from '#/address/AddressLabel'; import { useTimestamp } from '#/format/Timestamp'; import { ListItem } from '#/list/ListItem'; import { AddressIcon } from '#/Identicon/AddressIcon'; @@ -85,7 +85,7 @@ function TransferOp({ f, chain }: PropsFor<'TransferOp'>) { } overline="To" - headline={useAddressLabel(asUAddress(f.to, chain))} + headline={} /> } @@ -136,7 +136,7 @@ function TransferApprovalOp({ f, chain }: PropsFor<'TransferApprovalOp'>) { } overline="Spender" - headline={useAddressLabel(asUAddress(f.to, chain))} + headline={} /> } @@ -186,7 +186,7 @@ function Other({ op, chain }: OtherProps) { } overline={op.data ? 'Contract' : 'To'} - headline={useAddressLabel(asUAddress(op.to, chain))} + headline={} /> ); } diff --git a/app/src/components/transaction/PendingApprovalItem.tsx b/app/src/components/transaction/PendingApprovalItem.tsx index c37b8654d..e45cda463 100644 --- a/app/src/components/transaction/PendingApprovalItem.tsx +++ b/app/src/components/transaction/PendingApprovalItem.tsx @@ -8,7 +8,8 @@ import { useFragment } from 'react-relay'; import { PendingApprovalItem_user$key } from '~/api/__generated__/PendingApprovalItem_user.graphql'; import { PendingApprovalItem_approver$key } from '~/api/__generated__/PendingApprovalItem_approver.graphql'; import { PendingApprovalItem_proposal$key } from '~/api/__generated__/PendingApprovalItem_proposal.graphql'; -import { truncateAddr } from '~/util/format'; +import { AddressLabel } from '#/address/AddressLabel'; +import { asUAddress } from 'lib'; const User = graphql` fragment PendingApprovalItem_user on User { @@ -50,7 +51,7 @@ export function PendingApprovalItem(props: PendingApprovalItemProps) { return ( } - headline={approver.label || truncateAddr(approver.address)} + headline={} {...(approve && { trailing: ({ size }) => ( diff --git a/app/src/util/theme/icons.tsx b/app/src/util/theme/icons.tsx index 42a2b3f30..783be2fbd 100644 --- a/app/src/util/theme/icons.tsx +++ b/app/src/util/theme/icons.tsx @@ -39,7 +39,7 @@ export const materialCommunityIcon = icon('materialCommunity'); export const HomeIcon = materialIcon('home'); export const OutboundIcon = materialCommunityIcon('arrow-top-right'); -export const UserOutlineIcon = materialIcon('person-outline'); +export const ContactOutlineIcon = materialIcon('person-outline'); export const ContactsIcon = materialIcon('people'); export const ContactsOutlineIcon = materialIcon('people-outline'); export const AddIcon = materialCommunityIcon('plus');