From dbfe4dd15bd8070ca139979dc3d068040ee6074d Mon Sep 17 00:00:00 2001 From: Hayden Briese Date: Mon, 10 Jul 2023 19:01:49 +1000 Subject: [PATCH] Add generic address labelling --- api/schema.graphql | 5 +++++ api/src/features/contacts/contacts.input.ts | 6 ++++++ .../features/contacts/contacts.resolver.ts | 7 ++++++- api/src/features/contacts/contacts.service.ts | 19 +++++++++++++++++++ app/src/components/address/AddressLabel.tsx | 17 +++++++++++++++-- 5 files changed, 51 insertions(+), 3 deletions(-) diff --git a/api/schema.graphql b/api/schema.graphql index aed9f06e1..82b949068 100644 --- a/api/schema.graphql +++ b/api/schema.graphql @@ -183,6 +183,10 @@ The `JSON` scalar type represents JSON values as specified by [ECMA-404](http:// """ scalar JSON @specifiedBy(url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf") +input LabelInput { + address: Address! +} + type Mutation { approve(input: ApproveInput!): TransactionProposal! createAccount(input: CreateAccountInput!): Account! @@ -336,6 +340,7 @@ type Query { contacts: [Contact!]! contract(input: ContractInput!): Contract contractFunction(input: ContractFunctionInput!): ContractFunction + label(input: LabelInput!): String paymaster: Address! policies: [Policy!]! policy(input: UniquePolicyInput!): Policy diff --git a/api/src/features/contacts/contacts.input.ts b/api/src/features/contacts/contacts.input.ts index 47c3cb76f..c90a12188 100644 --- a/api/src/features/contacts/contacts.input.ts +++ b/api/src/features/contacts/contacts.input.ts @@ -19,3 +19,9 @@ export class UpsertContactInput { @Field(() => String) label: string; } + +@InputType() +export class LabelInput { + @AddressField() + address: Address; +} diff --git a/api/src/features/contacts/contacts.resolver.ts b/api/src/features/contacts/contacts.resolver.ts index 113155e4c..b9ce356db 100644 --- a/api/src/features/contacts/contacts.resolver.ts +++ b/api/src/features/contacts/contacts.resolver.ts @@ -1,6 +1,6 @@ import { ID, Info, Mutation, Query, Resolver } from '@nestjs/graphql'; import { GraphQLResolveInfo } from 'graphql'; -import { ContactInput, UpsertContactInput } from './contacts.input'; +import { ContactInput, LabelInput, UpsertContactInput } from './contacts.input'; import { Contact } from './contacts.model'; import { ContactsService } from './contacts.service'; import { getShape } from '../database/database.select'; @@ -21,6 +21,11 @@ export class ContactsResolver { return this.service.select(getShape(info)); } + @Query(() => String, { nullable: true }) + async label(@Input() { address }: LabelInput) { + return this.service.label(address); + } + @Mutation(() => Contact) async upsertContact(@Input() input: UpsertContactInput, @Info() info: GraphQLResolveInfo) { const id = await this.service.upsert(input); diff --git a/api/src/features/contacts/contacts.service.ts b/api/src/features/contacts/contacts.service.ts index 66fb51089..b4b405e8d 100644 --- a/api/src/features/contacts/contacts.service.ts +++ b/api/src/features/contacts/contacts.service.ts @@ -81,4 +81,23 @@ export class ContactsService { async delete(address: Address) { return this.db.query(e.delete(e.Contact, uniqueContact(address)).id); } + + async label(address: Address) { + const contact = e.select(e.Contact, () => ({ + filter_single: { user: e.global.current_user, address }, + label: true, + })).label; + + const account = e.select(e.Account, () => ({ + filter_single: { address }, + name: true, + })).name; + + const approver = e.select(e.Approver, () => ({ + filter_single: { address }, + label: true, + })).label; + + return this.db.query(e.select(e.op(e.op(contact, '??', account), '??', approver))); + } } diff --git a/app/src/components/address/AddressLabel.tsx b/app/src/components/address/AddressLabel.tsx index bda386785..403462ecd 100644 --- a/app/src/components/address/AddressLabel.tsx +++ b/app/src/components/address/AddressLabel.tsx @@ -2,13 +2,26 @@ import { Address } from 'lib'; import { useMemo } from 'react'; import { useMaybeToken } from '@token/useToken'; import { truncateAddr } from '~/util/format'; +import { gql } from '@api/gen'; +import { useSuspenseQuery } from '@apollo/client'; +import { AddressLabelQuery, AddressLabelQueryVariables } from '@api/gen/graphql'; + +const AddressLabelDoc = gql(/* GraphQL */ ` + query AddressLabel($address: Address!) { + label(input: { address: $address }) + } +`); export const useAddressLabel = (address: A) => { const token = useMaybeToken(address); + const label = useSuspenseQuery(AddressLabelDoc, { + variables: { address: address! }, + skip: !address, + }).data?.label; return useMemo( - () => (!address ? undefined : token?.name || truncateAddr(address)), - [address, token?.name], + () => (!address ? undefined : label || token?.name || truncateAddr(address)), + [label, address, token?.name], ) as A extends undefined ? string | undefined : string; };