diff --git a/main/api/contacts/index.ts b/main/api/contacts/index.ts index 07de7294..459595a3 100644 --- a/main/api/contacts/index.ts +++ b/main/api/contacts/index.ts @@ -34,11 +34,98 @@ const schema: Schema<{ }, }; -export const store = new Store({ schema, name: "contacts" }); +class ContactsUtil { + store = new Store({ schema, name: "contacts" }); + contactsByAddress: Map = new Map(); + + constructor() { + this.init(); + } + + init() { + const contacts = this.store.get("contacts", []); + const deduped = contacts.filter((contact) => { + if (this.contactsByAddress.has(contact.address)) { + return false; + } + this.contactsByAddress.set(contact.address, contact); + return true; + }); + this.store.set("contacts", deduped); + } + + getContacts() { + return this.store.get("contacts", []); + } + + getContactByAddress(address: string) { + return this.contactsByAddress.get(address) ?? null; + } + + addContact({ name, address }: { name: string; address: string }) { + if (this.contactsByAddress.has(address)) { + throw new Error("DUPLICATE_CONTACT_ERROR"); + } + + const contacts = this.getContacts(); + + contacts.push({ + id: uuidv4(), + name, + address, + order: contacts.length, + }); + + this.store.set("contacts", contacts); + + return contacts; + } + + editContact({ + id, + name, + address, + }: { + id: string; + name: string; + address: string; + }) { + const contacts = this.getContacts(); + const index = contacts.findIndex((contact) => contact.id === id); + + if (index > -1) { + contacts[index] = { + id, + name, + address, + order: contacts[index].order, + }; + this.store.set("contacts", contacts); + } + + return contacts; + } + + deleteContact(id: string) { + const contacts = this.getContacts(); + const index = contacts.findIndex((contact) => contact.id === id); + + if (index > -1) { + const contact = contacts[index]; + this.contactsByAddress.delete(contact.address); + contacts.splice(index, 1); + this.store.set("contacts", contacts); + } + + return contacts; + } +} + +const contactsUtil = new ContactsUtil(); export const contactsRouter = t.router({ getContacts: t.procedure.query(async () => { - return store.get("contacts", []); + return contactsUtil.getContacts(); }), getContactByAddress: t.procedure .input( @@ -47,11 +134,7 @@ export const contactsRouter = t.router({ }), ) .query(async (opts) => { - const contacts = store.get("contacts", []); - return ( - contacts.find((contact) => contact.address === opts.input.address) ?? - null - ); + return contactsUtil.getContactByAddress(opts.input.address); }), addContact: t.procedure .input( @@ -61,15 +144,7 @@ export const contactsRouter = t.router({ }), ) .mutation(async (opts) => { - const contacts = store.get("contacts", []); - contacts.push({ - id: uuidv4(), - name: opts.input.name, - address: opts.input.address, - order: contacts.length, - }); - store.set("contacts", contacts); - return contacts; + return contactsUtil.addContact(opts.input); }), editContact: t.procedure .input( @@ -80,22 +155,7 @@ export const contactsRouter = t.router({ }), ) .mutation(async (opts) => { - const contacts = store.get("contacts", []); - const index = contacts.findIndex( - (contact) => contact.id === opts.input.id, - ); - - if (index > -1) { - contacts[index] = { - id: opts.input.id, - name: opts.input.name, - address: opts.input.address, - order: contacts[index].order, - }; - store.set("contacts", contacts); - } - - return contacts; + return contactsUtil.editContact(opts.input); }), deleteContact: t.procedure .input( @@ -104,16 +164,6 @@ export const contactsRouter = t.router({ }), ) .mutation(async (opts) => { - const contacts = store.get("contacts", []); - const index = contacts.findIndex( - (contact) => contact.id === opts.input.id, - ); - - if (index > -1) { - contacts.splice(index, 1); - store.set("contacts", contacts); - } - - return contacts; + return contactsUtil.deleteContact(opts.input.id); }), }); diff --git a/renderer/components/AddContactModal/AddContactModal.tsx b/renderer/components/AddContactModal/AddContactModal.tsx index c036f481..6aa7eb46 100644 --- a/renderer/components/AddContactModal/AddContactModal.tsx +++ b/renderer/components/AddContactModal/AddContactModal.tsx @@ -54,6 +54,9 @@ const messages = defineMessages({ address: { defaultMessage: "Address", }, + duplicateContactError: { + defaultMessage: "A contact with that address already exists.", + }, }); type Props = { @@ -83,6 +86,7 @@ export function AddContactModal({ isOpen, onClose }: Props) { isError, isSuccess, reset: resetMutation, + error, } = trpcReact.addContact.useMutation(); const { @@ -150,7 +154,11 @@ export function AddContactModal({ isOpen, onClose }: Props) { {formatMessage(messages.addContactError)} - {formatMessage(messages.somethingWentWrong)} + {formatMessage( + error.message === "DUPLICATE_CONTACT_ERROR" + ? messages.duplicateContactError + : messages.somethingWentWrong, + )} )} diff --git a/renderer/intl/locales/en-US.json b/renderer/intl/locales/en-US.json index badf2444..7ae98f23 100644 --- a/renderer/intl/locales/en-US.json +++ b/renderer/intl/locales/en-US.json @@ -445,6 +445,9 @@ "X+BJ/8": { "message": "Other Assets" }, + "XIdw3B": { + "message": "A contact with that address already exists." + }, "XLvNHQ": { "message": "Please write down this recovery phrase and keep it in a safe place. You will need it to recover your account." },