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

Refactor: useKownAddresses #82

Merged
merged 11 commits into from
Oct 15, 2024
3 changes: 3 additions & 0 deletions apps/multisig/src/components/AddressInput/AccountDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type Props = {
hideAddress?: boolean
identiconSize?: number
breakLine?: boolean
isNameLoading?: boolean
}

export const AccountDetails: React.FC<Props> = ({
Expand All @@ -33,6 +34,7 @@ export const AccountDetails: React.FC<Props> = ({
breakLine,
hideIdenticon = false,
hideAddress = false,
isNameLoading = false,
}) => {
const { copy, copied } = useCopied()

Expand All @@ -54,6 +56,7 @@ export const AccountDetails: React.FC<Props> = ({
nameOrAddressOnly={nameOrAddressOnly}
breakLine={breakLine}
hideAddress={hideAddress}
isNameLoading={isNameLoading}
/>
{!disableCopy && (
<div
Expand Down
26 changes: 21 additions & 5 deletions apps/multisig/src/components/AddressInput/NameAndAddress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useOnchainIdentity } from '@domains/identity/useOnchainIdentity'
import { Address } from '@util/addresses'
import { Check } from 'lucide-react'
import { useEffect, useMemo, useState } from 'react'
import { CircularProgressIndicator } from '@talismn/ui'

export const NameAndAddress: React.FC<{
address: Address
Expand All @@ -12,7 +13,8 @@ export const NameAndAddress: React.FC<{
nameOrAddressOnly?: boolean
breakLine?: boolean
hideAddress?: boolean
}> = ({ address, name, chain, nameOrAddressOnly, breakLine, hideAddress }) => {
isNameLoading?: boolean
}> = ({ address, name, chain, nameOrAddressOnly, breakLine, hideAddress, isNameLoading = false }) => {
const { resolve } = useAzeroID()
const [azeroId, setAzeroId] = useState<string | undefined>()
const onchainIdentity = useOnchainIdentity(address, chain)
Expand Down Expand Up @@ -55,12 +57,26 @@ export const NameAndAddress: React.FC<{
return null
}, [address, azeroId, chain, name, nameOrAddressOnly, onchainIdentityUi])

if (!secondaryText)
if (!secondaryText) {
return (
<p className="text-offWhite overflow-hidden text-ellipsis mt-[3px] w-full max-w-max whitespace-nowrap">
{primaryText}
</p>
<div className="flex flex-col gap-1">
{isNameLoading && !name ? (
<>
<CircularProgressIndicator size={16} />
{!nameOrAddressOnly && (
<p className="text-gray-200 text-[12px] leading-[1] whitespace-nowrap overflow-hidden text-ellipsis max-w-max w-full pt-[3px]">
{primaryText}
</p>
)}
</>
) : (
<p className="text-offWhite overflow-hidden text-ellipsis mt-[3px] w-full max-w-max whitespace-nowrap">
{primaryText}
</p>
)}
</div>
)
}

return (
<div
Expand Down
9 changes: 8 additions & 1 deletion apps/multisig/src/components/MemberRow/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import { css } from '@emotion/css'
import { ExternalLink, Trash } from '@talismn/icons'
import { AccountDetails } from '@components/AddressInput/AccountDetails'

const MemberRow = (props: { member: AugmentedAccount; chain: Chain; onDelete?: () => void; truncate?: boolean }) => {
const MemberRow = (props: {
member: AugmentedAccount
chain: Chain
onDelete?: () => void
truncate?: boolean
isNameLoading?: boolean
}) => {
return (
<div
className={css`
Expand All @@ -27,6 +33,7 @@ const MemberRow = (props: { member: AugmentedAccount; chain: Chain; onDelete?: (
disableCopy
withAddressTooltip
chain={props.chain}
isNameLoading={props.isNameLoading}
/>
{props.member.you ? <span className="text-offWhite text-[14px]"> (You)</span> : null}
</div>
Expand Down
3 changes: 2 additions & 1 deletion apps/multisig/src/components/ScanVaults/VaultCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ const VaultCard: React.FC<{ vault: ScannedVault; onAdded?: () => void }> = ({ on
const { toast } = useToast()
const setImportedTeams = useSetRecoilState(importedTeamsState)
const navigate = useNavigate()
const { contactByAddress } = useKnownAddresses()
const signerAddresses = vault.multisig.signers.map(s => s.toSs58())
const { contactByAddress } = useKnownAddresses({ addresses: showMultisig ? signerAddresses : [] })
useEffect(() => {
if (add && showMultisig) setShowMultisig(false)
}, [add, showMultisig])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useEffect, useState } from 'react'
export const AddressParamInput: ParamInputComponent<string> = ({ chain, onChange, arg }) => {
const [query, setQuery] = useState('')
const [selectedMultisig] = useSelectedMultisig()
const { addresses } = useKnownAddresses(selectedMultisig.orgId, { includeSelectedMultisig: true })
const { addresses } = useKnownAddresses({ orgId: selectedMultisig.orgId, includeSelectedMultisig: true })

useEffect(() => {
if (!arg) setQuery('')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@ import { Address } from '@util/addresses'
import { useSelectedMultisig } from '@domains/multisig'

export const TransactionSidesheetApprovals: React.FC<{ t: Transaction }> = ({ t }) => {
const { contactByAddress } = useKnownAddresses(t.multisig.orgId)
const [{ isEthereumAccount }] = useSelectedMultisig()
const approversAddresses = Object.keys(t.approvals).reduce<string[]>((acc, address) => {
const decodedAddress = isEthereumAccount ? Address.fromSs58(address) : Address.fromPubKey(address)
if (decodedAddress) {
acc.push(decodedAddress.toSs58())
}
return acc
}, [])

const { contactByAddress, isLoading } = useKnownAddresses({ orgId: t.multisig.orgId, addresses: approversAddresses })
return (
<div css={{ display: 'grid', gap: '14px' }}>
{Object.entries(t.approvals).map(([address, approval]) => {
Expand All @@ -24,6 +32,7 @@ export const TransactionSidesheetApprovals: React.FC<{ t: Transaction }> = ({ t
<MemberRow
member={{ address: decodedAddress, nickname: contact?.name, you: contact?.extensionName !== undefined }}
chain={t.multisig.chain}
isNameLoading={isLoading}
/>
</div>
{(t.rawPending || t.executedAt) && (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { requestSignetBackend } from '@domains/offchain-data/hasura'
import { selectedAccountState } from '@domains/auth'
import { SignedInAccount } from '@domains/auth'
import { useRecoilValue } from 'recoil'
import { useQuery } from '@tanstack/react-query'
import { ADDRESSES_BY_ORG_ID_AND_ADDRESS } from '@domains/offchain-data/address-book/queries/queries'
import { useSelectedMultisig } from '@domains/multisig'
import { Address } from '@util/addresses'
import { Contact } from '../address-book'

export type ContactAddress = Omit<Contact, 'orgId'> & { team_id?: string; org_id: string }
export type ContactAddressIO = Omit<ContactAddress, 'address'> & { address: string }

export type PaginatedAddresses = {
rows: ContactAddress[]
pageCount: number
rowCount: number
}

const fetchGraphQLData = async ({
orgId,
addresses,
selectedAccount,
}: {
orgId: string
addresses: string[]
selectedAccount: SignedInAccount
}): Promise<ContactAddress[]> => {
const { data } = await requestSignetBackend(
ADDRESSES_BY_ORG_ID_AND_ADDRESS,
{
orgId,
addresses,
},
selectedAccount
)

return (
data.address?.map((contact: ContactAddressIO) => ({ ...contact, address: Address.fromSs58(contact.address) })) ?? []
)
}

const useGetAddressesByOrgIdAndAddress = (addresses: string[]) => {
const selectedAccount = useRecoilValue(selectedAccountState)
const [selectedMultisig] = useSelectedMultisig()
return useQuery({
queryKey: ['addresses', selectedMultisig.id, addresses],
queryFn: async () =>
fetchGraphQLData({ orgId: selectedMultisig.orgId, addresses, selectedAccount: selectedAccount! }),
UrbanWill marked this conversation as resolved.
Show resolved Hide resolved
enabled: !!selectedAccount && addresses.length > 0,
})
}

export default useGetAddressesByOrgIdAndAddress
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,25 @@ export const PAGINATED_SUB_CATEGORIES_BY_ORG_ID = gql`
}
`

export const ADDRESSES_BY_ORG_ID_AND_ADDRESS = gql`
query AddressesByOrgIdAndAddress($orgId: uuid!, $addresses: [String!]!) {
address(where: { org_id: { _eq: $orgId }, address: { _in: $addresses } }) {
id
org_id
name
address
category {
id
name
}
sub_category {
id
name
}
}
}
`

export const UPSERT_ADDRESSES = gql`
mutation UpsertAddressesMutation($orgId: String!, $teamId: String!, $addressesInput: [AddressInput!]!) {
UpsertAddresses(addressesInput: { addresses: $addressesInput, org_id: $orgId, team_id: $teamId }) {
Expand Down
32 changes: 21 additions & 11 deletions apps/multisig/src/hooks/useKnownAddresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,30 @@ import { useMemo } from 'react'
import { useSelectedMultisig } from '@domains/multisig'
import { useSmartContracts } from '../domains/offchain-data/smart-contract'
import { Contact } from '../domains/offchain-data/address-book/address-book'
import useGetAddressesByOrgIdAndAddress from '../domains/offchain-data/address-book/hooks/useGetAddressesByOrgIdAndAddress'

type ContactWithNameAndCategory = Partial<Contact> & AddressWithName

export const useKnownAddresses = (
teamId?: string,
{
includeSelectedMultisig = false,
includeContracts = false,
}: { includeSelectedMultisig?: boolean; includeContracts?: boolean } = {}
): { addresses: ContactWithNameAndCategory[]; contactByAddress: Record<string, ContactWithNameAndCategory> } => {
export const useKnownAddresses = ({
orgId,
includeSelectedMultisig,
includeContracts,
addresses,
}: {
orgId?: string
includeSelectedMultisig?: boolean
includeContracts?: boolean
addresses?: string[]
} = {}): {
addresses: ContactWithNameAndCategory[]
contactByAddress: Record<string, ContactWithNameAndCategory>
isLoading: boolean
} => {
const extensionAccounts = useRecoilValue(accountsState)
const addressBookByOrgId = useRecoilValue(addressBookByOrgIdState)
const [multisig] = useSelectedMultisig()
const { contracts } = useSmartContracts()
const { data: addressBookData, isLoading } = useGetAddressesByOrgIdAndAddress(addresses ?? [])

const extensionContacts = extensionAccounts.reduce<AddressWithName[]>(
(acc, { address, meta: { name = '' } = {} }) => {
Expand All @@ -37,9 +47,9 @@ export const useKnownAddresses = (
)

const addressBookContacts = useMemo(() => {
if (!teamId) return []
if (!orgId || !addressBookData?.length) return []

const addresses = addressBookByOrgId[teamId ?? ''] ?? []
const addresses = [...(addressBookByOrgId[orgId ?? ''] ?? []), ...(addressBookData ?? [])]

return addresses.reduce<ContactWithNameAndCategory[]>((acc, { address, name, category, sub_category }) => {
if (multisig.isEthereumAccount === address.isEthereum) {
Expand All @@ -54,7 +64,7 @@ export const useKnownAddresses = (
}
return acc
}, [])
}, [addressBookByOrgId, multisig.isEthereumAccount, teamId])
}, [addressBookByOrgId, addressBookData, multisig.isEthereumAccount, orgId])

const combinedList = useMemo(() => {
let list = extensionContacts
Expand Down Expand Up @@ -130,5 +140,5 @@ export const useKnownAddresses = (
}, {} as Record<string, ContactWithNameAndCategory>)
}, [combinedList])

return { addresses: combinedList, contactByAddress }
return { addresses: combinedList, contactByAddress, isLoading }
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const AddCollaboratorModal: React.FC<Props> = ({ isOpen, onClose }) => {
const [selectedMultisig] = useSelectedMultisig()
const [address, setAddress] = useState<Address | undefined>()
const [error, setError] = useState<boolean>(false)
const { addresses } = useKnownAddresses(selectedMultisig.orgId)
const { addresses } = useKnownAddresses({ orgId: selectedMultisig.orgId })
const { addCollaborator, adding } = useAddOrgCollaborator()

const handleAddressChange = (address: Address | undefined) => {
Expand Down
13 changes: 11 additions & 2 deletions apps/multisig/src/layouts/Collaborators/CollaboratorRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ export const CollaboratorRow: React.FC<{ orgId: string; userId: string; address:
userId,
address,
}) => {
const { contactByAddress } = useKnownAddresses(orgId, { includeContracts: true })
const { contactByAddress, isLoading } = useKnownAddresses({
orgId,
includeContracts: true,
addresses: [address.toSs58()],
})
const { deleteCollaborator, deleting } = useDeleteCollaborator()
const { isCollaborator } = useUser()

Expand All @@ -25,7 +29,12 @@ export const CollaboratorRow: React.FC<{ orgId: string; userId: string; address:
return (
<div className="w-full bg-gray-900 p-[16px] rounded-[12px] flex items-center justify-between">
<div>
<AccountDetails address={address} name={contactByAddress?.[address.toSs58()]?.name} withAddressTooltip />
<AccountDetails
address={address}
name={contactByAddress?.[address.toSs58()]?.name}
withAddressTooltip
isNameLoading={isLoading}
/>
</div>
{!isCollaborator && (
<Tooltip content="Remove collaborator">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const MultiSend = () => {
const apiLoadable = useRecoilValueLoadable(pjsApiSelector(multisig.chain.genesisHash))
const { toast } = useToast()
const permissions = hasPermission(multisig, 'transfer')
const { addresses } = useKnownAddresses(multisig.orgId)
const { addresses } = useKnownAddresses({ orgId: multisig.orgId })
const newSends = useRecoilValue(multisendSendsAtom)
const unit = useRecoilValue(multisendAmountUnitAtom)
const token = useRecoilValue(multisendTokenAtom)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const DetailsForm: React.FC<Props> = ({
}) => {
const [addressError, setAddressError] = useState<boolean>(false)
const [multisig] = useSelectedMultisig()
const { addresses } = useKnownAddresses(multisig.orgId)
const { addresses } = useKnownAddresses({ orgId: multisig.orgId })
const { hasDelayedPermission, hasNonDelayedPermission } = hasPermission(multisig, 'transfer')
const vestingConsts = useRecoilValueLoadable(vestingConstsSelector(multisig.chain.genesisHash))

Expand Down
Loading