diff --git a/public/locales/en.json b/public/locales/en.json index 0d4e6f0..2d4d133 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -197,7 +197,15 @@ "contactCount_plural": "{{count, number}} contacts", "contactCountEmpty": "No contacts", - "buttonAddContact": "Add contact" + "buttonAddContact": "Add contact", + + "columnLabel": "Label", + "columnAddress": "Address", + + "actionsEditTooltip": "Edit contact", + "actionsSendTransaction": "Send Krist", + "actionsDelete": "Delete contact", + "actionsDeleteConfirm": "Are you sure you want to delete this contact?" }, "myTransactions": { diff --git a/src/pages/contacts/ContactActions.tsx b/src/pages/contacts/ContactActions.tsx new file mode 100644 index 0000000..9154024 --- /dev/null +++ b/src/pages/contacts/ContactActions.tsx @@ -0,0 +1,90 @@ +// Copyright (c) 2020-2021 Drew Lemmy +// This file is part of KristWeb 2 under AGPL-3.0. +// Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt +import React from "react"; +import { Modal, Dropdown, Menu } from "antd"; +import { + EditOutlined, DeleteOutlined, ExclamationCircleOutlined, SendOutlined +} from "@ant-design/icons"; + +import { useTFns } from "@utils/i18n"; + +import { AuthorisedAction } from "@comp/auth/AuthorisedAction"; +import { OpenEditContactFn } from "./ContactEditButton"; +import { OpenSendTxFn } from "@comp/transactions/SendTransactionModalLink"; + +import { Contact, deleteContact } from "@contacts"; + +interface Props { + contact: Contact; + openEditContact: OpenEditContactFn; + openSendTx: OpenSendTxFn; +} + +export function ContactActions({ + contact, + openEditContact, + openSendTx, +}: Props): JSX.Element { + const { t, tStr } = useTFns("addressBook."); + + function showContactDeleteConfirm(): void { + Modal.confirm({ + icon: , + + title: tStr("actionsDeleteConfirm"), + + onOk: () => deleteContact(contact), + okText: t("dialog.yes"), + okType: "danger", + cancelText: t("dialog.no") + }); + } + + return [ + // Edit contact button + openEditContact(contact)} + popoverPlacement="left" + > + {React.cloneElement(leftButton as React.ReactElement, { + className: "ant-btn-left" + })} + , + + // Dropdown button + rightButton + ]} + + trigger={["click"]} + + overlay={( + + {/* Send tx button */} + + openSendTx(undefined, contact.address)} + popoverPlacement="left" + > +
{tStr("actionsSendTransaction")}
+
+
+ + + + {/* Delete button */} + showContactDeleteConfirm()}> + {tStr("actionsDelete")} + +
+ )} + > + {/* Edit button */} + +
; +} diff --git a/src/pages/contacts/ContactEditButton.tsx b/src/pages/contacts/ContactEditButton.tsx index d419a61..f6577ed 100644 --- a/src/pages/contacts/ContactEditButton.tsx +++ b/src/pages/contacts/ContactEditButton.tsx @@ -35,3 +35,28 @@ /> ; }; + +export type OpenEditContactFn = (contact: Contact) => void; +export type ContactEditHookRes = [ + OpenEditContactFn, + JSX.Element | null, + (visible: boolean) => void +]; + +export function useEditContactModal(): ContactEditHookRes { + const [opened, setOpened] = useState(false); + const [visible, setVisible] = useState(false); + const [contact, setContact] = useState(); + + function open(contact: Contact) { + setContact(contact); + setVisible(true); + if (!opened) setOpened(true); + } + + const modal = opened + ? + : null; + + return [open, modal, setVisible]; +} diff --git a/src/pages/contacts/ContactsPage.tsx b/src/pages/contacts/ContactsPage.tsx index 5acb1ff..075ac8e 100644 --- a/src/pages/contacts/ContactsPage.tsx +++ b/src/pages/contacts/ContactsPage.tsx @@ -10,8 +10,12 @@ import { PageLayout } from "@layout/PageLayout"; import { useContacts } from "@contacts"; +import { ContactsTable } from "./ContactsTable"; import { AddContactModal } from "./AddContactModal"; +import { useEditContactModal } from "./ContactEditButton"; +import { useSendTransactionModal } from "@comp/transactions/SendTransactionModalLink"; + /** Contact count subtitle */ function ContactsPageSubtitle(): JSX.Element { const { t, tStr, tKey } = useTFns("addressBook."); @@ -44,10 +48,20 @@ } export function ContactsPage(): JSX.Element { + const [openEditContact, editContactModal] = useEditContactModal(); + const [openSendTx, sendTxModal] = useSendTransactionModal(); + return } extra={} > + + + {editContactModal} + {sendTxModal} ; } diff --git a/src/pages/contacts/ContactsTable.tsx b/src/pages/contacts/ContactsTable.tsx new file mode 100644 index 0000000..3c8bbc8 --- /dev/null +++ b/src/pages/contacts/ContactsTable.tsx @@ -0,0 +1,78 @@ +// Copyright (c) 2020-2021 Drew Lemmy +// This file is part of KristWeb 2 under AGPL-3.0. +// Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt +import { Table } from "antd"; + +import { useTFns } from "@utils/i18n"; + +import { ContextualAddress } from "@comp/addresses/ContextualAddress"; + +import { useContacts } from "@contacts"; +import { ContactActions } from "./ContactActions"; +import { OpenEditContactFn } from "./ContactEditButton"; +import { OpenSendTxFn } from "@comp/transactions/SendTransactionModalLink"; + +import { keyedNullSort } from "@utils"; + +interface Props { + openEditContact: OpenEditContactFn; + openSendTx: OpenSendTxFn; +} + +export function ContactsTable({ + openEditContact, + openSendTx +}: Props): JSX.Element { + const { tStr } = useTFns("addressBook."); + const { contacts } = useContacts(); + + return ( + + ), + sorter: (a, b) => a.address.localeCompare(b.address) + }, + + // Actions + { + key: "actions", + width: 80, + + render: (_, contact) => ( + + ) + } + ]} + />; +} + diff --git a/src/pages/wallets/WalletActions.tsx b/src/pages/wallets/WalletActions.tsx index 27b5f9b..a2cd598 100644 --- a/src/pages/wallets/WalletActions.tsx +++ b/src/pages/wallets/WalletActions.tsx @@ -109,8 +109,8 @@ {t("myWallets.actionsDelete")} - )}> - + )} + > {/* Edit button */} ;