Skip to content

Commit

Permalink
feat(app): address sheet and pressable address label
Browse files Browse the repository at this point in the history
  • Loading branch information
hbriese committed Aug 22, 2024
1 parent 6a5ae28 commit fe77c8e
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 32 deletions.
105 changes: 105 additions & 0 deletions app/src/app/(sheet)/address/[address].tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Sheet contentContainerStyle={styles.container}>
<View style={styles.headerContainer}>
<AddressIcon address={address} size={ICON_SIZE.large} />

<Text variant="headlineSmall">{useAddressLabel(address)}</Text>

<Text variant="bodyMedium" style={styles.address}>
{asAddress(address)}
</Text>
</View>

<Scrollable horizontal contentContainerStyle={styles.actions}>
<PressableOpacity style={styles.action} onPress={() => share({ url: address })}>
<ShareIcon />
<Text variant="labelLarge">Share</Text>
</PressableOpacity>

{explorer && (
<Link href={`${explorer}address/${asAddress(address)}`} asChild>
<PressableOpacity style={styles.action}>
<WebIcon />
<Text variant="labelLarge">Explorer</Text>
</PressableOpacity>
</Link>
)}

<Link href={{ pathname: `/(nav)/contacts/[address]`, params: { address } }} asChild>
<PressableOpacity style={styles.action}>
<ContactOutlineIcon />
<Text variant="labelLarge">Contact</Text>
</PressableOpacity>
</Link>

{account && (
<Link
href={{ pathname: `/(nav)/[account]/(home)/send`, params: { account, to: address } }}
asChild
>
<PressableOpacity style={styles.action}>
<OutboundIcon />
<Text variant="labelLarge">Send</Text>
</PressableOpacity>
</Link>
)}
</Scrollable>
</Sheet>
);
}

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,
},
}));
6 changes: 3 additions & 3 deletions app/src/components/QrModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ export function QrModal({ address, actions }: QrModalProps) {
<IconButton mode="contained-tonal" icon={CloseIcon} size={styles.iconButton.width} />
</Link>

<Text variant="displaySmall" style={styles.name}>
{isUAddress(address) && <AddressLabel address={address} />}
</Text>
{isUAddress(address) && (
<AddressLabel address={address} variant="displaySmall" style={styles.name} />
)}

<View style={styles.iconButton} />
</View>
Expand Down
10 changes: 7 additions & 3 deletions app/src/components/activity/IncomingTransferItem.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -40,7 +40,6 @@ export interface IncomingTransferItemProps extends Partial<ListItemProps> {

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 (
Expand All @@ -58,7 +57,12 @@ function IncomingTransferItem_(props: IncomingTransferItemProps) {
<OverlayIcon icon={ReceiveIcon} />
</View>
}
headline={`Received ${amount} from ${from}`}
headline={
<>
{`Received ${amount} from `}
<AddressLabel address={asUAddress(transfer.from, transfer.account.chain)} />
</>
}
supporting={<Timestamp timestamp={transfer.timestamp} />}
trailing={({ Text }) =>
transfer.value !== null && transfer.value !== undefined ? (
Expand Down
38 changes: 25 additions & 13 deletions app/src/components/address/AddressLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <A extends UAddress | undefined>(address: A) => {
const { label } = useLazyQuery<AddressLabelQuery>(Query, {
address: address!,
skip: !address,
});
export function useAddressLabel(address: UAddress) {
const { label } = useLazyQuery<AddressLabelQuery>(
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<TextProps<string>, 'children'> {
address: UAddress;
}

export const AddressLabel = ({ address }: AddressLabelProps) => useAddressLabel(address);
export function AddressLabel({ address, ...textProps }: AddressLabelProps) {
const router = useRouter();

return (
<Text
{...textProps}
onPress={() => router.push({ pathname: `/address/[address]`, params: { address } })}
>
{useAddressLabel(address)}
</Text>
);
}
8 changes: 2 additions & 6 deletions app/src/components/policy/ApproverItem.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -17,8 +16,6 @@ export interface ApproverItemProps extends Partial<ListItemProps> {

export function ApproverItem({ address, remove, ...props }: ApproverItemProps) {
const { styles } = useStyles(stylesheet);
const label = useAddressLabel(address);
const truncated = truncateAddr(address);

const ref = useRef<Swipeable | null>(null);

Expand Down Expand Up @@ -46,8 +43,7 @@ export function ApproverItem({ address, remove, ...props }: ApproverItemProps) {
>
<ListItem
leading={<AddressIcon address={address} />}
headline={label}
supporting={label !== truncated ? truncated : undefined}
headline={<AddressLabel address={address} />}
trailing={<CloseIcon onPress={remove} />}
containerStyle={styles.itemContainer}
{...props}
Expand Down
8 changes: 4 additions & 4 deletions app/src/components/transaction/OperationDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -85,7 +85,7 @@ function TransferOp({ f, chain }: PropsFor<'TransferOp'>) {
<ListItem
leading={<AddressIcon address={f.to} />}
overline="To"
headline={useAddressLabel(asUAddress(f.to, chain))}
headline={<AddressLabel address={asUAddress(f.to, chain)} />}
/>
<ListItem
leading={<LazyTokenIcon token={asUAddress(f.token, chain)} />}
Expand Down Expand Up @@ -136,7 +136,7 @@ function TransferApprovalOp({ f, chain }: PropsFor<'TransferApprovalOp'>) {
<ListItem
leading={<AddressIcon address={f.to} />}
overline="Spender"
headline={useAddressLabel(asUAddress(f.to, chain))}
headline={<AddressLabel address={asUAddress(f.to, chain)} />}
/>
<ListItem
leading={<LazyTokenIcon token={asUAddress(f.token, chain)} />}
Expand Down Expand Up @@ -186,7 +186,7 @@ function Other({ op, chain }: OtherProps) {
<ListItem
leading={<AddressIcon address={op.to} />}
overline={op.data ? 'Contract' : 'To'}
headline={useAddressLabel(asUAddress(op.to, chain))}
headline={<AddressLabel address={asUAddress(op.to, chain)} />}
/>
);
}
5 changes: 3 additions & 2 deletions app/src/components/transaction/PendingApprovalItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -50,7 +51,7 @@ export function PendingApprovalItem(props: PendingApprovalItemProps) {
return (
<ListItem
leading={<AddressIcon address={approver.address} />}
headline={approver.label || truncateAddr(approver.address)}
headline={<AddressLabel address={asUAddress(approver.address, proposal.account.chain)} />}
{...(approve && {
trailing: ({ size }) => (
<IconButton mode="contained-tonal" icon={CheckIcon} size={size} onPress={approve} />
Expand Down
2 changes: 1 addition & 1 deletion app/src/util/theme/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down

0 comments on commit fe77c8e

Please sign in to comment.