diff --git a/src/components/addresses/picker/PickerHints.tsx b/src/components/addresses/picker/PickerHints.tsx index c32ba49..ac48913 100644 --- a/src/components/addresses/picker/PickerHints.tsx +++ b/src/components/addresses/picker/PickerHints.tsx @@ -40,8 +40,6 @@ hasExactName?: boolean, suppressUpdates?: boolean ): PickerHintsRes { - debug("using picker hints for %s", value); - // Used for clean-up const isMounted = useRef(true); diff --git a/src/components/transactions/SendTransactionModalLink.tsx b/src/components/transactions/SendTransactionModalLink.tsx index 6a98bc2..47bf761 100644 --- a/src/components/transactions/SendTransactionModalLink.tsx +++ b/src/components/transactions/SendTransactionModalLink.tsx @@ -1,7 +1,7 @@ // 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 { FC, useState } from "react"; +import { FC, useState, useCallback } from "react"; import { AuthorisedAction } from "@comp/auth/AuthorisedAction"; import { SendTransactionModal } from "@pages/transactions/send/SendTransactionModal"; @@ -52,11 +52,11 @@ const [visible, setVisible] = useState(false); const [fromTo, setFromTo] = useState({}); - function open(from?: Wallet | string, to?: string) { + const open = useCallback((from?: Wallet | string, to?: string) => { setFromTo({ from, to }); setVisible(true); - if (!opened) setOpened(true); - } + setOpened(true); + }, []); const modal = opened ? { Modal.confirm({ icon: , @@ -39,9 +39,9 @@ okType: "danger", cancelText: t("dialog.no") }); - } + }, [t, tStr, contact]); - return [ @@ -86,5 +86,9 @@ > {/* Edit button */} - ; + , [ + tStr, contact, showContactDeleteConfirm, openEditContact, openSendTx + ]); + + return memoDropdown; } diff --git a/src/pages/contacts/ContactEditButton.tsx b/src/pages/contacts/ContactEditButton.tsx index f6577ed..1683998 100644 --- a/src/pages/contacts/ContactEditButton.tsx +++ b/src/pages/contacts/ContactEditButton.tsx @@ -1,7 +1,7 @@ // 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, { useState, FC } from "react"; +import React, { useState, useCallback, FC } from "react"; import { AddContactModal } from "./AddContactModal"; @@ -48,11 +48,11 @@ const [visible, setVisible] = useState(false); const [contact, setContact] = useState(); - function open(contact: Contact) { + const open = useCallback((contact: Contact) => { setContact(contact); setVisible(true); - if (!opened) setOpened(true); - } + setOpened(true); + }, []); const modal = opened ? diff --git a/src/pages/names/NamesPage.tsx b/src/pages/names/NamesPage.tsx index 8e4b6cd..532ab91 100644 --- a/src/pages/names/NamesPage.tsx +++ b/src/pages/names/NamesPage.tsx @@ -18,9 +18,11 @@ import { NamePurchaseModalLink } from "./mgmt/NamePurchaseModalLink"; +import { useNameEditModal } from "./mgmt/NameEditModalLink"; +import { useSendTransactionModal } from "@comp/transactions/SendTransactionModalLink"; + import { useWallets } from "@wallets"; import { useBooleanSetting } from "@utils/settings"; -import { useLinkedPagination } from "@utils/table"; import "./NamesPage.less"; @@ -69,6 +71,9 @@ // invalid address), the table will bubble it up to here const [error, setError] = useState(); + const [openNameEdit, nameEditModal] = useNameEditModal(); + const [openSendTx, sendTxModal] = useSendTransactionModal(); + // Used to handle memoisation and auto-refreshing const { joinedAddressList } = useWallets(); const lastNameTransactionID = useSelector((s: RootState) => s.node.lastNameTransactionID); @@ -95,8 +100,13 @@ sortNew={sortNew} addresses={usedAddresses?.split(",")} setError={setError} + + openNameEdit={openNameEdit} + openSendTx={openSendTx} /> - ), [usedAddresses, sortNew, usedRefreshID, setError]); + ), [ + usedAddresses, sortNew, usedRefreshID, setError, openSendTx, openNameEdit + ]); const siteTitle = getSiteTitle(t, listingType, address); const subTitle = listingType === ListingType.NETWORK_ADDRESS @@ -133,7 +143,12 @@ invalidParameterSubTitleKey="names.resultInvalid" />; else if (isEmpty) return ; - else return memoTable; + else return <> + {memoTable} + + {nameEditModal} + {sendTxModal} + ; })()} ; } diff --git a/src/pages/names/NamesTable.tsx b/src/pages/names/NamesTable.tsx index 5bd1146..ae2ef6d 100644 --- a/src/pages/names/NamesTable.tsx +++ b/src/pages/names/NamesTable.tsx @@ -14,6 +14,8 @@ import { useWallets } from "@wallets"; import { NameActions } from "./mgmt/NameActions"; +import { OpenEditNameFn } from "./mgmt/NameEditModalLink"; +import { OpenSendTxFn } from "@comp/transactions/SendTransactionModalLink"; import { useNameTableLock } from "./tableLock"; import { KristNameLink } from "@comp/names/KristNameLink"; @@ -34,9 +36,23 @@ addresses?: string[]; setError?: Dispatch>; setPagination?: Dispatch>; + + openNameEdit: OpenEditNameFn; + openSendTx: OpenSendTxFn; } -export function NamesTable({ refreshingID, sortNew, addresses, setError, setPagination }: Props): JSX.Element { +export function NamesTable({ + refreshingID, + + sortNew, + + addresses, + setError, + setPagination, + + openNameEdit, + openSendTx +}: Props): JSX.Element { const { t } = useTranslation(); const [loading, setLoading] = useState(true); @@ -188,6 +204,9 @@ ) } diff --git a/src/pages/names/mgmt/NameActions.tsx b/src/pages/names/mgmt/NameActions.tsx index 291ed6f..7d2765d 100644 --- a/src/pages/names/mgmt/NameActions.tsx +++ b/src/pages/names/mgmt/NameActions.tsx @@ -1,6 +1,7 @@ // 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 { useMemo } from "react"; import { Button, Dropdown, Menu } from "antd"; import { DownOutlined, SwapOutlined, SendOutlined, EditOutlined } from "@ant-design/icons"; @@ -9,73 +10,97 @@ import { KristName } from "@api/types"; import { useNameSuffix } from "@utils/currency"; -import { SendTransactionModalLink } from "@comp/transactions/SendTransactionModalLink"; +import { AuthorisedAction } from "@comp/auth/AuthorisedAction"; +import { OpenEditNameFn } from "./NameEditModalLink"; +import { OpenSendTxFn } from "@comp/transactions/SendTransactionModalLink"; import { NameEditModalLink } from "./NameEditModalLink"; interface Props { name: KristName; isOwn: boolean; + + openNameEdit: OpenEditNameFn; + openSendTx: OpenSendTxFn; } -export function NameActions({ name, isOwn }: Props): JSX.Element { +export function NameActions({ + name, + isOwn, + + openNameEdit, + openSendTx +}: Props): JSX.Element { const { t } = useTranslation(); const nameSuffix = useNameSuffix(); const nameWithSuffix = `${name.name}.${nameSuffix}`; // The dropdown menu, used if we own the name - const buttonMenu = isOwn ? - {/* Transfer Krist button */} - - -
{t("names.actionsTransferKrist")}
-
-
+ const buttonMenu = useMemo(() => isOwn + ? + {/* Transfer Krist button */} + + openSendTx(undefined, nameWithSuffix)} + popoverPlacement="left" + > +
{t("names.actionsTransferKrist")}
+
+
- + - {/* Update A record */} - - -
{t("names.actionsUpdateARecord")}
-
-
+ {/* Update A record */} + + openNameEdit("update", name.name, name.a)} + popoverPlacement="left" + > +
{t("names.actionsUpdateARecord")}
+
+
- {/* Transfer name */} - - -
{t("names.actionsTransferName")}
-
-
-
: undefined; + {/* Transfer name */} + + openNameEdit("transfer", name.name)} + popoverPlacement="left" + > +
{t("names.actionsTransferName")}
+
+
+
+ : undefined, + [t, isOwn, name.a, name.name, nameWithSuffix, openSendTx, openNameEdit]); - if (isOwn) { - // Actions dropdown (own name) - return - - ; - } else { - // Send transaction button (not own name) - return - - ; - } + + + ) + : ( + // Send transaction button (not own name) + openSendTx(undefined, nameWithSuffix)} + popoverPlacement="left" + > + + + ), + [t, buttonMenu, isOwn, nameWithSuffix, openSendTx]); + + return actions; } diff --git a/src/pages/names/mgmt/NameEditForm.tsx b/src/pages/names/mgmt/NameEditForm.tsx index b89c827..6dbad65 100644 --- a/src/pages/names/mgmt/NameEditForm.tsx +++ b/src/pages/names/mgmt/NameEditForm.tsx @@ -1,7 +1,7 @@ // 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 { useState } from "react"; +import { useState, useEffect, useMemo } from "react"; import { Form, FormInstance } from "antd"; import { TFns } from "@utils/i18n"; @@ -64,6 +64,19 @@ setRecipient(undefined); } + const initialValues: FormValues = useMemo(() => ({ + names: name ? [name] : [], + + // Start with an initial A record if this is the update name modal + ...(mode === "update" && aRecord ? { aRecord } : {}) + }), [mode, name, aRecord]); + + // If the initial values change, refresh the form + useEffect(() => { + if (!formInstance || !mode) return; + formInstance.setFieldsValue(initialValues); + }, [formInstance, mode, initialValues]); + const form =
diff --git a/src/pages/names/mgmt/NameEditModalLink.tsx b/src/pages/names/mgmt/NameEditModalLink.tsx index 1f9dbb4..69349c1 100644 --- a/src/pages/names/mgmt/NameEditModalLink.tsx +++ b/src/pages/names/mgmt/NameEditModalLink.tsx @@ -1,7 +1,7 @@ // 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 { FC, useState } from "react"; +import { FC, useState, useCallback } from "react"; import { AuthorisedAction } from "@comp/auth/AuthorisedAction"; import { NameEditModal, Mode } from "./NameEditModal"; @@ -35,3 +35,50 @@ /> ; }; + + +export type OpenEditNameFn = ( + mode: Mode, + name?: string, + aRecord?: string | null +) => void; + +export type NameEditHookRes = [ + OpenEditNameFn, + JSX.Element | null, + (visible: boolean) => void +]; + +interface EditState { + mode: Mode; + name?: string; + aRecord?: string | null; +} + +export function useNameEditModal(): NameEditHookRes { + // The modal will only be rendered if it is opened at least once + const [opened, setOpened] = useState(false); + const [visible, setVisible] = useState(false); + const [editState, setEditState] = useState(); + + const open: OpenEditNameFn = useCallback((mode, name, aRecord) => { + setEditState({ mode, name, aRecord }); + setVisible(true); + setOpened(true); + }, []); + + const modal = opened + ? ( + + ) + : null; + + return [open, modal, setVisible]; +} diff --git a/src/pages/wallets/WalletActions.tsx b/src/pages/wallets/WalletActions.tsx index a2cd598..e4a0468 100644 --- a/src/pages/wallets/WalletActions.tsx +++ b/src/pages/wallets/WalletActions.tsx @@ -1,7 +1,7 @@ // 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 React, { useCallback, useMemo } from "react"; import { Modal, Dropdown, Menu } from "antd"; import { MenuClickEventHandler } from "rc-menu/lib/interface"; import { @@ -33,7 +33,7 @@ }: Props): JSX.Element { const { t } = useTranslation(); - function showWalletDeleteConfirm(): void { + const showWalletDeleteConfirm = useCallback((): void => { Modal.confirm({ icon: , @@ -45,9 +45,9 @@ okType: "danger", cancelText: t("dialog.no") }); - } + }, [t, wallet]); - const onMenuClick: MenuClickEventHandler = e => { + const onMenuClick: MenuClickEventHandler = useCallback(e => { switch (e.key) { // "Wallet info" button case "2": @@ -57,9 +57,9 @@ case "3": return showWalletDeleteConfirm(); } - }; + }, [wallet, openWalletInfo, showWalletDeleteConfirm]); - return [ @@ -113,5 +113,9 @@ > {/* Edit button */} - ; + , [ + t, wallet, onMenuClick, openEditWallet, openSendTx + ]); + + return memoDropdown; } diff --git a/src/pages/wallets/WalletEditButton.tsx b/src/pages/wallets/WalletEditButton.tsx index 29aae05..fb6aaef 100644 --- a/src/pages/wallets/WalletEditButton.tsx +++ b/src/pages/wallets/WalletEditButton.tsx @@ -1,7 +1,7 @@ // 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 { useState, FC } from "react"; +import { useState, useCallback, FC } from "react"; import { AuthorisedAction } from "@comp/auth/AuthorisedAction"; import { AddWalletModal } from "./AddWalletModal"; @@ -41,11 +41,11 @@ const [visible, setVisible] = useState(false); const [wallet, setWallet] = useState(); - function open(wallet: Wallet) { + const open = useCallback((wallet: Wallet) => { setWallet(wallet); setVisible(true); - if (!opened) setOpened(true); - } + setOpened(true); + }, []); const modal = opened ? diff --git a/src/pages/wallets/info/WalletInfoModal.tsx b/src/pages/wallets/info/WalletInfoModal.tsx index 991abb5..70d29c5 100644 --- a/src/pages/wallets/info/WalletInfoModal.tsx +++ b/src/pages/wallets/info/WalletInfoModal.tsx @@ -1,7 +1,7 @@ // 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 { useState, Dispatch, SetStateAction } from "react"; +import { useState, useCallback, Dispatch, SetStateAction } from "react"; import { Modal, Button, DescriptionsProps } from "antd"; import { useTranslation } from "react-i18next"; @@ -74,11 +74,11 @@ const [visible, setVisible] = useState(false); const [wallet, setWallet] = useState(); - function open(wallet: Wallet) { + const open = useCallback((wallet: Wallet) => { setWallet(wallet); setVisible(true); - if (!opened) setOpened(true); - } + setOpened(true); + }, []); const modal = opened && wallet ?