diff --git a/packages/bierzo-wallet/src/communication/requestgenerators.ts b/packages/bierzo-wallet/src/communication/requestgenerators.ts index 8b8d729ed..c84adb248 100644 --- a/packages/bierzo-wallet/src/communication/requestgenerators.ts +++ b/packages/bierzo-wallet/src/communication/requestgenerators.ts @@ -2,6 +2,8 @@ import { Address, Amount, Identity, SendTransaction, UnsignedTransaction } from import { bnsCodec, ChainAddressPair, + DeleteAccountTx, + DeleteDomainTx, RegisterAccountTx, RegisterDomainTx, RegisterUsernameTx, @@ -158,10 +160,42 @@ export const generateTransferDomainTxWithFee = async ( return await withChainFee(regDomainTx, creatorAddress); }; -export const generateRegisterAccountTxWithFee = async ( +export const generateDeleteDomainTxWithFee = async ( + creator: Identity, + domain: string, +): Promise => { + const creatorAddress = bnsCodec.identityToAddress(creator); + + const transaction: DeleteDomainTx = { + kind: "bns/delete_domain", + chainId: creator.chainId, + domain: domain, + }; + + return await withChainFee(transaction, creatorAddress); +}; + +export const generateDeleteAccountTxWithFee = async ( creator: Identity, + name: string, domain: string, +): Promise => { + const creatorAddress = bnsCodec.identityToAddress(creator); + + const transaction: DeleteAccountTx = { + kind: "bns/delete_account", + chainId: creator.chainId, + name: name, + domain: domain, + }; + + return await withChainFee(transaction, creatorAddress); +}; + +export const generateRegisterAccountTxWithFee = async ( + creator: Identity, name: string, + domain: string, owner: Address, targets: readonly ChainAddressPair[], ): Promise => { @@ -196,15 +230,15 @@ export const generateTransferAccountTxWithFee = async ( export const generateReplaceAccountTargetsTxWithFee = async ( creator: Identity, - domain: string, name: string, + domain: string, newTargets: readonly ChainAddressPair[], ): Promise => { const regAccountTx: ReplaceAccountTargetsTx = { kind: "bns/replace_account_targets", chainId: creator.chainId, + name: name ? name : undefined, domain: domain, - name: name, newTargets: newTargets, }; @@ -284,25 +318,44 @@ export const generateTransferAccountTxRequest = async ( return generateJsonPrcRequest(creator, transactionWithFee); }; -export const generateRegisterAccountTxRequest = async ( +export const generateDeleteDomainTxRequest = async ( creator: Identity, domain: string, +): Promise => { + const transactionWithFee = await generateDeleteDomainTxWithFee(creator, domain); + + return generateJsonPrcRequest(creator, transactionWithFee); +}; + +export const generateDeleteAccountTxRequest = async ( + creator: Identity, name: string, + domain: string, +): Promise => { + const transactionWithFee = await generateDeleteAccountTxWithFee(creator, name, domain); + + return generateJsonPrcRequest(creator, transactionWithFee); +}; + +export const generateRegisterAccountTxRequest = async ( + creator: Identity, + name: string, + domain: string, owner: Address, targets: readonly ChainAddressPair[], ): Promise => { - const transactionWithFee = await generateRegisterAccountTxWithFee(creator, domain, name, owner, targets); + const transactionWithFee = await generateRegisterAccountTxWithFee(creator, name, domain, owner, targets); return generateJsonPrcRequest(creator, transactionWithFee); }; export const generateReplaceAccountTargetsTxRequest = async ( creator: Identity, - domain: string, name: string, + domain: string, newTargets: readonly ChainAddressPair[], ): Promise => { - const transactionWithFee = await generateReplaceAccountTargetsTxWithFee(creator, domain, name, newTargets); + const transactionWithFee = await generateReplaceAccountTargetsTxWithFee(creator, name, domain, newTargets); return generateJsonPrcRequest(creator, transactionWithFee); }; diff --git a/packages/bierzo-wallet/src/components/AccountDelete/index.stories.tsx b/packages/bierzo-wallet/src/components/AccountDelete/index.stories.tsx new file mode 100644 index 000000000..424a22ac7 --- /dev/null +++ b/packages/bierzo-wallet/src/components/AccountDelete/index.stories.tsx @@ -0,0 +1,83 @@ +import { Address, Algorithm, ChainId, Identity, PubkeyBytes, Token, TokenTicker } from "@iov/bcp"; +import { JsonRpcRequest } from "@iov/jsonrpc"; +import { action } from "@storybook/addon-actions"; +import { storiesOf } from "@storybook/react"; +import { Typography } from "medulas-react-components"; +import React from "react"; +import { stringToAmount } from "ui-logic"; + +import { extensionRpcEndpoint } from "../../communication/extensionRpcEndpoint"; +import { generateDeleteDomainTxRequest } from "../../communication/requestgenerators"; +import { ChainAddressPairWithName } from "../../components/AddressesTable"; +import DecoratedStorybook, { bierzoRoot } from "../../utils/storybook"; +import { BwAccountWithChainName } from "../AccountManage"; +import AccountTransfer from "."; + +const chainAddresses: ChainAddressPairWithName[] = [ + { + chainId: "local-iov-devnet" as ChainId, + address: "tiov1dcg3fat5zrvw00xezzjk3jgedm7pg70y222af3" as Address, + chainName: "IOV Devnet", + }, + { + chainId: "lisk-198f2b61a8" as ChainId, + address: "1349293588603668134L" as Address, + chainName: "Lisk Devnet", + }, + { + chainId: "ethereum-eip155-5777" as ChainId, + address: "0xD383382350F9f190Bd2608D6381B15b4e1cec0f3" as Address, + chainName: "Ganache", + }, +]; + +const account: BwAccountWithChainName = { + name: "albert", + domain: "iov", + expiryDate: new Date("June 5, 2120 03:00:00"), + owner: "tiov1dcg3fat5zrvw00xezzjk3jgedm7pg70y222af3" as Address, + addresses: chainAddresses, +}; + +export const ACCOUNT_DELETE_STORY_PATH = `${bierzoRoot}/Account Delete`; +export const ACCOUNT_DELETE_SAMPLE_STORY_PATH = "Delete sample"; + +const iov: Pick = { + fractionalDigits: 9, + tokenTicker: "IOV" as TokenTicker, +}; + +const bnsIdentity: Identity = { + chainId: "local-iov-devnet" as ChainId, + pubkey: { + algo: Algorithm.Ed25519, + data: new Uint8Array([]) as PubkeyBytes, + }, +}; + +storiesOf(ACCOUNT_DELETE_STORY_PATH, module) + .addParameters({ viewport: { defaultViewport: "responsive" } }) + .add(ACCOUNT_DELETE_SAMPLE_STORY_PATH, () => ( + + => { + action("getRequest")(); + return await generateDeleteDomainTxRequest(bnsIdentity, account.domain); + }} + onCancel={action("Transfer cancel")} + getFee={async () => { + action("get fee")(); + return { tokens: stringToAmount("5", iov) }; + }} + bnsChainId={"local-iov-devnet" as ChainId} + rpcEndpoint={extensionRpcEndpoint} + setTransactionId={value => { + action("setTransactionId")(value); + }} + > + Some additional description + + + )); diff --git a/packages/bierzo-wallet/src/components/AccountDelete/index.tsx b/packages/bierzo-wallet/src/components/AccountDelete/index.tsx new file mode 100644 index 000000000..9b42f94eb --- /dev/null +++ b/packages/bierzo-wallet/src/components/AccountDelete/index.tsx @@ -0,0 +1,73 @@ +import { ChainId, Fee, TransactionId } from "@iov/bcp"; +import { JsonRpcRequest } from "@iov/jsonrpc"; +import { Typography } from "medulas-react-components"; +import React from "react"; + +import { RpcEndpoint } from "../../communication/rpcEndpoint"; +import { AccountModuleMixedType, isAccountData } from "../AccountManage"; +import AccountOperation from "../AccountOperation"; + +interface HeaderProps { + readonly account: AccountModuleMixedType; +} + +const Header: React.FunctionComponent = ({ account }): JSX.Element => ( + + + You are deleting{" "} + + + {isAccountData(account) ? `${account.name}*${account.domain}` : account.username} + + +); + +interface Props { + readonly id: string; + readonly account: AccountModuleMixedType; + readonly children: React.ReactNode; + readonly bnsChainId: ChainId; + readonly onCancel: () => void; + readonly getFee: () => Promise; + readonly getRequest: () => Promise; + readonly rpcEndpoint: RpcEndpoint; + readonly setTransactionId: React.Dispatch>; +} + +const AccountDelete = ({ + account, + id, + onCancel, + getFee, + getRequest, + bnsChainId, + children, + rpcEndpoint, + setTransactionId, +}: Props): JSX.Element => { + const getDeleteRequest = async (): Promise => { + return await getRequest(); + }; + + const getDeleteFee = async (): Promise => { + return await getFee(); + }; + + return ( + } + > + {children} + + ); +}; + +export default AccountDelete; diff --git a/packages/bierzo-wallet/src/components/AccountOperation/index.stories.tsx b/packages/bierzo-wallet/src/components/AccountOperation/index.stories.tsx new file mode 100644 index 000000000..de605fce3 --- /dev/null +++ b/packages/bierzo-wallet/src/components/AccountOperation/index.stories.tsx @@ -0,0 +1,103 @@ +import { Address, Algorithm, ChainId, Identity, PubkeyBytes, Token, TokenTicker } from "@iov/bcp"; +import { JsonRpcRequest } from "@iov/jsonrpc"; +import { action } from "@storybook/addon-actions"; +import { storiesOf } from "@storybook/react"; +import { FormValues, Typography } from "medulas-react-components"; +import React from "react"; +import { stringToAmount } from "ui-logic"; + +import { extensionRpcEndpoint } from "../../communication/extensionRpcEndpoint"; +import { generateTransferDomainTxRequest } from "../../communication/requestgenerators"; +import { ChainAddressPairWithName } from "../../components/AddressesTable"; +import DecoratedStorybook, { bierzoRoot } from "../../utils/storybook"; +import { BwAccountWithChainName } from "../AccountManage"; +import AccountOperation, { RECEPIENT_ADDRESS } from "."; + +const chainAddresses: ChainAddressPairWithName[] = [ + { + chainId: "local-iov-devnet" as ChainId, + address: "tiov1dcg3fat5zrvw00xezzjk3jgedm7pg70y222af3" as Address, + chainName: "IOV Devnet", + }, + { + chainId: "lisk-198f2b61a8" as ChainId, + address: "1349293588603668134L" as Address, + chainName: "Lisk Devnet", + }, + { + chainId: "ethereum-eip155-5777" as ChainId, + address: "0xD383382350F9f190Bd2608D6381B15b4e1cec0f3" as Address, + chainName: "Ganache", + }, +]; + +const account: BwAccountWithChainName = { + name: "albert", + domain: "iov", + expiryDate: new Date("June 5, 2120 03:00:00"), + owner: "tiov1dcg3fat5zrvw00xezzjk3jgedm7pg70y222af3" as Address, + addresses: chainAddresses, +}; + +export const ACCOUNT_TRANSFER_STORY_PATH = `${bierzoRoot}/Account Transfer`; +export const ACCOUNT_TRANSFER_SAMPLE_STORY_PATH = "Transfer sample"; + +const iov: Pick = { + fractionalDigits: 9, + tokenTicker: "IOV" as TokenTicker, +}; + +const bnsIdentity: Identity = { + chainId: "local-iov-devnet" as ChainId, + pubkey: { + algo: Algorithm.Ed25519, + data: new Uint8Array([]) as PubkeyBytes, + }, +}; + +const Header: React.FunctionComponent = (): JSX.Element => ( + + + You are makeing operation with{" "} + + + albert*iov + + + {" "} + account + + +); + +storiesOf(`${bierzoRoot}/Account Operation`, module) + .addParameters({ viewport: { defaultViewport: "responsive" } }) + .add("Sample", () => ( + + => { + action("getRequest")(formValues); + return await generateTransferDomainTxRequest( + bnsIdentity, + account.domain, + formValues[RECEPIENT_ADDRESS] as Address, + ); + }} + onCancel={action("Transfer cancel")} + getFee={async newOwner => { + action("get fee")(newOwner); + return { tokens: stringToAmount("5", iov) }; + }} + bnsChainId={"local-iov-devnet" as ChainId} + rpcEndpoint={extensionRpcEndpoint} + setTransactionId={value => { + action("setTransactionId")(value); + }} + header={
} + > + Some additional description + + + )); diff --git a/packages/bierzo-wallet/src/components/AccountOperation/index.tsx b/packages/bierzo-wallet/src/components/AccountOperation/index.tsx new file mode 100644 index 000000000..2e6045895 --- /dev/null +++ b/packages/bierzo-wallet/src/components/AccountOperation/index.tsx @@ -0,0 +1,178 @@ +import { ChainId, Fee, TransactionId } from "@iov/bcp"; +import { JsonRpcRequest } from "@iov/jsonrpc"; +import { Paper, Theme } from "@material-ui/core"; +import { useTheme } from "@material-ui/styles"; +import { FormApi } from "final-form"; +import { + Back, + BillboardContext, + Block, + Button, + Form, + FormValues, + makeStyles, + ToastContext, + useForm, +} from "medulas-react-components"; +import React from "react"; +import { amountToString } from "ui-logic"; + +import { RpcEndpoint } from "../../communication/rpcEndpoint"; +import { submitTransaction } from "../../utils/transaction"; +import LedgerBillboardMessage from "../BillboardMessage/LedgerBillboardMessage"; +import NeumaBillboardMessage from "../BillboardMessage/NeumaBillboardMessage"; + +export const RECEPIENT_ADDRESS = "account-recepient-address"; + +const useMessagePaper = makeStyles({ + rounded: { + borderRadius: "5px", + border: "1px solid #F3F3F3", + }, + elevation1: { + boxShadow: "none", + }, +}); + +interface Props { + readonly id: string; + readonly submitCaption: string; + readonly children: React.ReactNode; + readonly subSection?: (form: FormApi) => React.FunctionComponent; + readonly header?: React.ReactNode; + readonly bnsChainId: ChainId; + readonly onCancel: () => void; + readonly getFee: (values: FormValues) => Promise; + readonly getRequest: (values: FormValues) => Promise; + readonly rpcEndpoint: RpcEndpoint; + readonly setTransactionId: React.Dispatch>; +} + +const AccountOperation: React.FunctionComponent = ({ + id, + onCancel, + getFee, + getRequest, + children, + rpcEndpoint, + setTransactionId, + subSection, + submitCaption, + header, +}): JSX.Element => { + const [transferFee, setTransferFee] = React.useState(); + const messagePaperClasses = useMessagePaper(); + const theme = useTheme(); + const billboard = React.useContext(BillboardContext); + const toast = React.useContext(ToastContext); + + const getSubmitButtonCaption = (fee: Fee | undefined): string => { + if (fee && fee.tokens) { + return `${submitCaption} for ${amountToString(fee.tokens)}`; + } + + return submitCaption; + }; + + const onSubmit = async (values: object): Promise => { + const formValues = values as FormValues; + + const request = await getRequest(formValues); + await submitTransaction( + request, + billboard, + toast, + rpcEndpoint, + , + , + (transactionId: TransactionId) => setTransactionId(transactionId), + ); + }; + + const { form, handleSubmit, invalid, submitting, values } = useForm({ + onSubmit, + }); + + const SubSection = React.useMemo(() => { + if (subSection) { + return subSection(form); + } + + return undefined; + }, [subSection, form]); + + React.useEffect(() => { + let isSubscribed = true; + + async function setFee(): Promise { + const fee = await getFee(values as FormValues); + if (isSubscribed) { + setTransferFee(fee); + } + } + + if (!invalid) { + setFee(); + } else { + setTransferFee(undefined); + } + + return () => { + isSubscribed = false; + }; + }, [getFee, invalid, values]); + + return ( + + + + + {header} + + {children} + + + + + + +
+ {SubSection && } + + + + + + Cancel + + + + +
+
+ ); +}; + +export default AccountOperation; diff --git a/packages/bierzo-wallet/src/components/AccountTransfer/index.stories.tsx b/packages/bierzo-wallet/src/components/AccountTransfer/index.stories.tsx index d7d98a9ad..c765f9c5a 100644 --- a/packages/bierzo-wallet/src/components/AccountTransfer/index.stories.tsx +++ b/packages/bierzo-wallet/src/components/AccountTransfer/index.stories.tsx @@ -34,9 +34,9 @@ const chainAddresses: ChainAddressPairWithName[] = [ const account: BwAccountWithChainName = { name: "albert", domain: "iov", - expiryDate: new Date(), + expiryDate: new Date("June 5, 2120 03:00:00"), owner: "tiov1dcg3fat5zrvw00xezzjk3jgedm7pg70y222af3" as Address, - addresses: [chainAddresses[0], chainAddresses[1]], + addresses: chainAddresses, }; export const ACCOUNT_TRANSFER_STORY_PATH = `${bierzoRoot}/Account Transfer`; diff --git a/packages/bierzo-wallet/src/components/AccountTransfer/index.tsx b/packages/bierzo-wallet/src/components/AccountTransfer/index.tsx index 5c3f87afc..befcf9f9e 100644 --- a/packages/bierzo-wallet/src/components/AccountTransfer/index.tsx +++ b/packages/bierzo-wallet/src/components/AccountTransfer/index.tsx @@ -1,48 +1,28 @@ import { Address, ChainId, Fee, TransactionId } from "@iov/bcp"; import { bnsCodec } from "@iov/bns"; import { JsonRpcRequest } from "@iov/jsonrpc"; -import { Paper, Theme } from "@material-ui/core"; -import { useTheme } from "@material-ui/styles"; -import { FieldValidator } from "final-form"; +import { Paper } from "@material-ui/core"; +import { FieldValidator, FormApi } from "final-form"; import { - Back, - BillboardContext, Block, - Button, composeValidators, FieldInputValue, - Form, FormValues, makeStyles, required, TextField, - ToastContext, Typography, - useForm, } from "medulas-react-components"; import React from "react"; -import { amountToString } from "ui-logic"; import { RpcEndpoint } from "../../communication/rpcEndpoint"; import { isValidName, lookupRecipientAddressByName } from "../../logic/account"; import { getConnectionForBns } from "../../logic/connection"; -import { submitTransaction } from "../../utils/transaction"; import { AccountModuleMixedType, isAccountData } from "../AccountManage"; -import LedgerBillboardMessage from "../BillboardMessage/LedgerBillboardMessage"; -import NeumaBillboardMessage from "../BillboardMessage/NeumaBillboardMessage"; +import AccountOperation from "../AccountOperation"; export const RECEPIENT_ADDRESS = "account-recepient-address"; -const useMessagePaper = makeStyles({ - rounded: { - borderRadius: "5px", - border: "1px solid #F3F3F3", - }, - elevation1: { - boxShadow: "none", - }, -}); - const usePromptPaper = makeStyles({ rounded: { borderRadius: "5px", @@ -54,14 +34,6 @@ const usePromptPaper = makeStyles({ }, }); -function getTransferButtonCaption(fee: Fee | undefined): string { - if (fee && fee.tokens) { - return `Transfer for ${amountToString(fee.tokens)}`; - } - - return "Transfer"; -} - const recipientValidator: FieldValidator = async (value): Promise => { if (typeof value !== "string") throw new Error("Input must be a string"); @@ -87,6 +59,25 @@ const recipientValidator: FieldValidator = async (value): Promi return undefined; }; +interface HeaderProps { + readonly account: AccountModuleMixedType; +} + +const Header: React.FunctionComponent = ({ account }): JSX.Element => ( + + + You are transferring{" "} + + + {isAccountData(account) ? `${account.name}*${account.domain}` : account.username} + + + {" "} + from your domain + + +); + const validator = composeValidators(required, recipientValidator); interface Props { @@ -114,134 +105,70 @@ const AccountTransfer = ({ setTransactionId, transferPrompt, }: Props): JSX.Element => { - const [transferFee, setTransferFee] = React.useState(); - const messagePaperClasses = useMessagePaper(); const promptPaperClasses = usePromptPaper(); - const theme = useTheme(); - const billboard = React.useContext(BillboardContext); - const toast = React.useContext(ToastContext); - const onSubmit = async (values: object): Promise => { - const formValues = values as FormValues; + const subSection = (form: FormApi): React.FunctionComponent => (): JSX.Element => { + return ( + + + + Who will you be transferring it to? + + + {transferPrompt} + + + + + + ); + }; - let newOwner: Address = formValues[RECEPIENT_ADDRESS] as Address; + const getNewOwnerAddress = async (newOwner: string): Promise
=> { if (isValidName(newOwner) === "valid") { const lookupResult = await lookupRecipientAddressByName(newOwner, bnsChainId); - newOwner = lookupResult as Address; + return lookupResult as Address; } - const request = await getRequest(newOwner); - await submitTransaction( - request, - billboard, - toast, - rpcEndpoint, - , - , - (transactionId: TransactionId) => setTransactionId(transactionId), - ); + return newOwner as Address; }; - const { form, handleSubmit, invalid, pristine, submitting, values } = useForm({ - onSubmit, - }); + const getTransferRequest = async (values: FormValues): Promise => { + const newOwner = await getNewOwnerAddress(values[RECEPIENT_ADDRESS]); - React.useEffect(() => { - let isSubscribed = true; + return await getRequest(newOwner); + }; - async function setFee(): Promise { - const fee = await getFee(values[RECEPIENT_ADDRESS] as Address); - if (isSubscribed) { - setTransferFee(fee); - } - } + const getTransferFee = async (values: FormValues): Promise => { + if (!values[RECEPIENT_ADDRESS]) return undefined; - if (!invalid && values[RECEPIENT_ADDRESS]) { - setFee(); - } + const newOwner = await getNewOwnerAddress(values[RECEPIENT_ADDRESS]); - return () => { - isSubscribed = false; - }; - }, [getFee, invalid, values]); + return await getFee(newOwner); + }; return ( - } + subSection={subSection} > - - - - - You are transferring{" "} - - - {isAccountData(account) ? `${account.name}*${account.domain}` : account.username} - - - {" "} - from your domain - - - {children} - - - - - - -
- - - - Who will you be transferring it to? - - - {transferPrompt} - - - - - - - - - - - Cancel - - - -
-
-
+ {children} + ); }; diff --git a/packages/bierzo-wallet/src/logic/transactions/index.ts b/packages/bierzo-wallet/src/logic/transactions/index.ts index 49f28e055..d50316b69 100644 --- a/packages/bierzo-wallet/src/logic/transactions/index.ts +++ b/packages/bierzo-wallet/src/logic/transactions/index.ts @@ -1,5 +1,7 @@ import { Address, ChainId, Identity, UnsignedTransaction } from "@iov/bcp"; import { + isDeleteAccountTx, + isDeleteDomainTx, isRegisterAccountTx, isRegisterDomainTx, isRegisterUsernameTx, @@ -100,6 +102,14 @@ async function mayDispatchAccount( dispatch(removeAccountAction(`*${accountTx.domain}`)); } } + + if (isDeleteDomainTx(accountTx)) { + dispatch(removeAccountAction(`*${accountTx.domain}`)); + } + + if (isDeleteAccountTx(accountTx)) { + dispatch(removeAccountAction(`${accountTx.name}*${accountTx.domain}`)); + } } let txsSubscriptions: Subscription[] = []; diff --git a/packages/bierzo-wallet/src/routes/account/delete/components/ConfirmDelete.tsx b/packages/bierzo-wallet/src/routes/account/delete/components/ConfirmDelete.tsx new file mode 100644 index 000000000..905457b37 --- /dev/null +++ b/packages/bierzo-wallet/src/routes/account/delete/components/ConfirmDelete.tsx @@ -0,0 +1,86 @@ +import { TransactionId } from "@iov/bcp"; +import clipboardCopy from "clipboard-copy"; +import { + Block, + Button, + Image, + makeStyles, + ToastContext, + ToastVariant, + Typography, +} from "medulas-react-components"; +import React from "react"; + +import copySvg from "../../../../assets/copy.svg"; +import tickSvg from "../../../../assets/tick.svg"; +import PageContent from "../../../../components/PageContent"; + +export const DELETE_CONFIRMATION_VIEW_ID = "delete-confirmation-view-id"; + +const useClasses = makeStyles({ + txId: { + whiteSpace: "pre-wrap", + wordWrap: "break-word", + }, + copyButton: { + cursor: "pointer", + }, +}); + +const tickIcon = Tick; +const copyIcon = Copy; + +interface Props { + readonly transactionId: TransactionId; + readonly onSeeTrasactions: () => void; +} + +const ConfirmDelete = ({ transactionId, onSeeTrasactions }: Props): JSX.Element => { + const toast = React.useContext(ToastContext); + const classes = useClasses(); + + const buttons = ( + + + + + + ); + + const copyTxId = (): void => { + clipboardCopy(transactionId); + toast.show("Address has been copied to clipboard.", ToastVariant.INFO); + }; + + return ( + + + Your delete request was successfully signed and sent to the network. + + + + Transaction ID + + + + + {transactionId} + + + {copyIcon} + + + + ); +}; + +export default ConfirmDelete; diff --git a/packages/bierzo-wallet/src/routes/account/delete/components/NameDelete.tsx b/packages/bierzo-wallet/src/routes/account/delete/components/NameDelete.tsx new file mode 100644 index 000000000..985bb7b01 --- /dev/null +++ b/packages/bierzo-wallet/src/routes/account/delete/components/NameDelete.tsx @@ -0,0 +1,90 @@ +import { Fee, Identity, TransactionId } from "@iov/bcp"; +import { JsonRpcRequest } from "@iov/jsonrpc"; +import { List, ListItem, makeStyles, Typography } from "medulas-react-components"; +import React from "react"; + +import { history } from "../../.."; +import { + generateDeleteAccountTxRequest, + generateDeleteAccountTxWithFee, +} from "../../../../communication/requestgenerators"; +import { RpcEndpoint } from "../../../../communication/rpcEndpoint"; +import AccountDelete from "../../../../components/AccountDelete"; +import { BwAccountWithChainName } from "../../../../components/AccountManage"; +import { NAME_MANAGE_ROUTE } from "../../../paths"; + +const NAME_DELETE_ID = "name-delete-id"; + +const useList = makeStyles({ + root: { + backgroundColor: "inherit", + border: "none", + listStyle: "disc inside", + fontSize: "1.6rem", + color: "#1C1C1C", + }, +}); + +const useListItem = makeStyles({ + root: { + display: "list-item", + }, +}); + +interface Props { + readonly bnsIdentity: Identity; + readonly rpcEndpoint: RpcEndpoint; + readonly setTransactionId: React.Dispatch>; +} + +const NameAccountDelete = ({ setTransactionId, bnsIdentity, rpcEndpoint }: Props): JSX.Element => { + const listClasses = useList(); + const listItemClasses = useListItem(); + + const account: BwAccountWithChainName = history.location.state; + + const onReturnToManage = (): void => { + history.push(NAME_MANAGE_ROUTE, account); + }; + + const getFee = async (): Promise => { + return (await generateDeleteAccountTxWithFee(bnsIdentity, account.name, account.domain)).fee; + }; + + const getRequest = async (): Promise => { + return await generateDeleteAccountTxRequest(bnsIdentity, account.name, account.domain); + }; + + return ( + + + + + Deleting this name removes it from your account. + + + + + You can create it again but all the associated addresses will be unlinked. + + + + + No one will be able to send you funds to this name. + + + + + ); +}; + +export default NameAccountDelete; diff --git a/packages/bierzo-wallet/src/routes/account/delete/components/StarnameDelete.tsx b/packages/bierzo-wallet/src/routes/account/delete/components/StarnameDelete.tsx new file mode 100644 index 000000000..e81cc51e6 --- /dev/null +++ b/packages/bierzo-wallet/src/routes/account/delete/components/StarnameDelete.tsx @@ -0,0 +1,91 @@ +import { Fee, Identity, TransactionId } from "@iov/bcp"; +import { JsonRpcRequest } from "@iov/jsonrpc"; +import { List, ListItem, makeStyles, Typography } from "medulas-react-components"; +import React from "react"; + +import { history } from "../../.."; +import { + generateDeleteDomainTxRequest, + generateDeleteDomainTxWithFee, +} from "../../../../communication/requestgenerators"; +import { RpcEndpoint } from "../../../../communication/rpcEndpoint"; +import AccountDelete from "../../../../components/AccountDelete"; +import { BwAccountWithChainName } from "../../../../components/AccountManage"; +import { STARNAME_MANAGE_ROUTE } from "../../../paths"; + +const STARNAME_DELETE_ID = "starname-delete-id"; + +const useList = makeStyles({ + root: { + backgroundColor: "inherit", + border: "none", + listStyle: "disc inside", + fontSize: "1.6rem", + color: "#1C1C1C", + }, +}); + +const useListItem = makeStyles({ + root: { + display: "list-item", + }, +}); + +interface Props { + readonly bnsIdentity: Identity; + readonly rpcEndpoint: RpcEndpoint; + readonly setTransactionId: React.Dispatch>; +} + +const StarnameAccountDelete = ({ setTransactionId, bnsIdentity, rpcEndpoint }: Props): JSX.Element => { + const listClasses = useList(); + const listItemClasses = useListItem(); + + const account: BwAccountWithChainName = history.location.state; + + const onReturnToManage = (): void => { + history.push(STARNAME_MANAGE_ROUTE, account); + }; + + const getFee = async (): Promise => { + return (await generateDeleteDomainTxWithFee(bnsIdentity, account.domain)).fee; + }; + + const getRequest = async (): Promise => { + return await generateDeleteDomainTxRequest(bnsIdentity, account.domain); + }; + + return ( + + + + + Deleting this starname removes it from your account. + + + + + All it’s associated names will become inactive this meaning even the names you have trasfered to + other people. + + + + + You may not be able to register this starname after you have deleted it. + + + + + ); +}; + +export default StarnameAccountDelete; diff --git a/packages/bierzo-wallet/src/routes/account/delete/index.stories.tsx b/packages/bierzo-wallet/src/routes/account/delete/index.stories.tsx new file mode 100644 index 000000000..d7c335421 --- /dev/null +++ b/packages/bierzo-wallet/src/routes/account/delete/index.stories.tsx @@ -0,0 +1,86 @@ +import { Address, Algorithm, ChainId, Identity, PubkeyBytes, Token, TokenTicker } from "@iov/bcp"; +import { JsonRpcRequest } from "@iov/jsonrpc"; +import { action } from "@storybook/addon-actions"; +import { linkTo } from "@storybook/addon-links"; +import { storiesOf } from "@storybook/react"; +import { Typography } from "medulas-react-components"; +import React from "react"; +import { stringToAmount } from "ui-logic"; + +import { extensionRpcEndpoint } from "../../../communication/extensionRpcEndpoint"; +import { generateDeleteDomainTxRequest } from "../../../communication/requestgenerators"; +import AccountDelete from "../../../components/AccountDelete"; +import { ACCOUNT_DELETE_STORY_PATH } from "../../../components/AccountDelete/index.stories"; +import { BwAccountWithChainName } from "../../../components/AccountManage"; +import { ACCOUNT_MANAGE_STORY_PATH } from "../../../components/AccountManage/index.stories"; +import { ChainAddressPairWithName } from "../../../components/AddressesTable"; +import DecoratedStorybook from "../../../utils/storybook"; +import { ACCOUNT_MANAGE_IOVNAMES_STORY_PATH } from "../manage/index.stories"; + +const chainAddresses: ChainAddressPairWithName[] = [ + { + chainId: "local-iov-devnet" as ChainId, + address: "tiov1dcg3fat5zrvw00xezzjk3jgedm7pg70y222af3" as Address, + chainName: "IOV Devnet", + }, + { + chainId: "lisk-198f2b61a8" as ChainId, + address: "1349293588603668134L" as Address, + chainName: "Lisk Devnet", + }, + { + chainId: "ethereum-eip155-5777" as ChainId, + address: "0xD383382350F9f190Bd2608D6381B15b4e1cec0f3" as Address, + chainName: "Ganache", + }, +]; + +const account: BwAccountWithChainName = { + name: "test2", + domain: "iov", + expiryDate: new Date("June 5, 2120 03:00:00"), + owner: "tiov1dcg3fat5zrvw00xezzjk3jgedm7pg70y222af3" as Address, + addresses: chainAddresses, +}; + +const iov: Pick = { + fractionalDigits: 9, + tokenTicker: "IOV" as TokenTicker, +}; + +const bnsIdentity: Identity = { + chainId: "local-iov-devnet" as ChainId, + pubkey: { + algo: Algorithm.Ed25519, + data: new Uint8Array([]) as PubkeyBytes, + }, +}; + +export const ACCOUNT_DELETE_STARNAME_STORY_PATH = "Delete starname"; + +storiesOf(ACCOUNT_DELETE_STORY_PATH, module) + .addParameters({ viewport: { defaultViewport: "responsive" } }) + .add(ACCOUNT_DELETE_STARNAME_STORY_PATH, () => ( + + => { + action("getRequest")(); + return await generateDeleteDomainTxRequest(bnsIdentity, account.domain); + }} + onCancel={linkTo(ACCOUNT_MANAGE_STORY_PATH, ACCOUNT_MANAGE_IOVNAMES_STORY_PATH)} + getFee={async () => { + action("get fee")(); + return { tokens: stringToAmount("5", iov) }; + }} + bnsChainId={"local-iov-devnet" as ChainId} + rpcEndpoint={extensionRpcEndpoint} + setTransactionId={value => { + action("setTransactionId")(value); + }} + > + Some additional description + + + )); diff --git a/packages/bierzo-wallet/src/routes/account/delete/index.tsx b/packages/bierzo-wallet/src/routes/account/delete/index.tsx new file mode 100644 index 000000000..9afeb8751 --- /dev/null +++ b/packages/bierzo-wallet/src/routes/account/delete/index.tsx @@ -0,0 +1,61 @@ +import { TransactionId } from "@iov/bcp"; +import { Block } from "medulas-react-components"; +import React from "react"; +import * as ReactRedux from "react-redux"; + +import { AccountProps } from ".."; +import { history } from "../.."; +import PageMenu from "../../../components/PageMenu"; +import { RootState } from "../../../store/reducers"; +import { getBnsIdentity } from "../../../utils/tokens"; +import { TRANSACTIONS_ROUTE } from "../../paths"; +import ConfirmDelete from "./components/ConfirmDelete"; +import NameAccountDelete from "./components/NameDelete"; +import StarnameAccountDelete from "./components/StarnameDelete"; + +function onSeeTrasactions(): void { + history.push(TRANSACTIONS_ROUTE); +} + +const AccountDelete = ({ entity }: AccountProps): JSX.Element => { + const [transactionId, setTransactionId] = React.useState(null); + const rpcEndpoint = ReactRedux.useSelector((state: RootState) => state.rpcEndpoint); + const identities = ReactRedux.useSelector((state: RootState) => state.identities); + const bnsIdentity = getBnsIdentity(identities); + + if (!bnsIdentity) throw new Error("No BNS identity available."); + if (!rpcEndpoint) throw new Error("RPC endpoint not set in redux store. This is a bug."); + + return ( + + {transactionId ? ( + + ) : ( + + {entity === "starname" && ( + + )} + {entity === "name" && ( + + )} + + )} + + ); +}; + +export default AccountDelete; diff --git a/packages/bierzo-wallet/src/routes/account/index.ts b/packages/bierzo-wallet/src/routes/account/index.ts index 02e335d30..04e2033d5 100644 --- a/packages/bierzo-wallet/src/routes/account/index.ts +++ b/packages/bierzo-wallet/src/routes/account/index.ts @@ -1,3 +1,4 @@ +import AccountDelete from "./delete"; import AccountManage from "./manage"; import AccountRegister from "./register"; import AccountUpdate from "./update"; @@ -8,4 +9,4 @@ export interface AccountProps { entity: AccountEntity; } -export { AccountManage, AccountRegister, AccountUpdate }; +export { AccountManage, AccountRegister, AccountUpdate, AccountDelete }; diff --git a/packages/bierzo-wallet/src/routes/account/manage/components/AssociatedNamesList.tsx b/packages/bierzo-wallet/src/routes/account/manage/components/AssociatedNamesList.tsx index 94beac9cc..25ebfd48c 100644 --- a/packages/bierzo-wallet/src/routes/account/manage/components/AssociatedNamesList.tsx +++ b/packages/bierzo-wallet/src/routes/account/manage/components/AssociatedNamesList.tsx @@ -6,7 +6,7 @@ import React from "react"; import { history } from "../../.."; import AccountManage, { BwAccountWithChainName } from "../../../../components/AccountManage"; -import { NAME_EDIT_ROUTE, NAME_TRANSFER_ROUTE } from "../../../paths"; +import { NAME_DELETE_ROUTE, NAME_EDIT_ROUTE, NAME_TRANSFER_ROUTE } from "../../../paths"; import arrowDown from "../assets/arrow-down.svg"; import arrowUp from "../assets/arrow-up.svg"; @@ -92,8 +92,7 @@ const AssociatedNamesList: React.FunctionComponent = ({ { title: "Transfer name", action: () => history.push(NAME_TRANSFER_ROUTE, name) }, // eslint-disable-next-line no-console { title: "Transfer it back to me", action: () => console.log("Transfer it back to me") }, - // eslint-disable-next-line no-console - { title: "Delete name", action: () => console.log("Delete name") }, + { title: "Delete name", action: () => history.push(NAME_DELETE_ROUTE, name) }, ]; return ( diff --git a/packages/bierzo-wallet/src/routes/account/manage/components/IovnameForm.tsx b/packages/bierzo-wallet/src/routes/account/manage/components/IovnameForm.tsx index 67f14b198..11a082988 100644 --- a/packages/bierzo-wallet/src/routes/account/manage/components/IovnameForm.tsx +++ b/packages/bierzo-wallet/src/routes/account/manage/components/IovnameForm.tsx @@ -9,12 +9,7 @@ const IovnameAccountManage = (): JSX.Element => { const account: BwUsernameWithChainName = history.location.state; const menuItems: readonly ActionMenuItem[] = [ - // eslint-disable-next-line no-console - { title: "Renew", action: () => console.log("Renew") }, - // eslint-disable-next-line no-console { title: "Transfer iovname", action: () => history.push(IOVNAME_TRANSFER_ROUTE, account) }, - // eslint-disable-next-line no-console - { title: "Delete iovname", action: () => console.log("Delete iovname") }, ]; const onEdit = (): void => { diff --git a/packages/bierzo-wallet/src/routes/account/manage/components/NameForm.tsx b/packages/bierzo-wallet/src/routes/account/manage/components/NameForm.tsx index ddfc64ada..078ed4189 100644 --- a/packages/bierzo-wallet/src/routes/account/manage/components/NameForm.tsx +++ b/packages/bierzo-wallet/src/routes/account/manage/components/NameForm.tsx @@ -6,7 +6,7 @@ import AccountManage, { BwAccountWithChainName } from "../../../../components/Ac import { NAME_EDIT_ROUTE } from "../../../paths"; // eslint-disable-next-line no-console -const menuItems: readonly ActionMenuItem[] = [{ title: "Renew", action: () => console.log("Delete") }]; +const menuItems: readonly ActionMenuItem[] = [{ title: "Renew", action: () => console.log("Renew") }]; const NameAccountManage = (): JSX.Element => { const account: BwAccountWithChainName = history.location.state; diff --git a/packages/bierzo-wallet/src/routes/account/manage/components/StarnameForm.tsx b/packages/bierzo-wallet/src/routes/account/manage/components/StarnameForm.tsx index 289a69d85..9faff1871 100644 --- a/packages/bierzo-wallet/src/routes/account/manage/components/StarnameForm.tsx +++ b/packages/bierzo-wallet/src/routes/account/manage/components/StarnameForm.tsx @@ -5,7 +5,12 @@ import { history } from "../../.."; import AccountManage, { BwAccountWithChainName } from "../../../../components/AccountManage"; import { getChainName } from "../../../../config"; import { getConnectionForBns } from "../../../../logic/connection"; -import { NAME_EDIT_ROUTE, NAME_REGISTER_ROUTE, STARNAME_TRANSFER_ROUTE } from "../../../paths"; +import { + NAME_EDIT_ROUTE, + NAME_REGISTER_ROUTE, + STARNAME_DELETE_ROUTE, + STARNAME_TRANSFER_ROUTE, +} from "../../../paths"; import AssociatedNamesList from "./AssociatedNamesList"; const StarnameAccountManage = (): JSX.Element => { @@ -16,8 +21,7 @@ const StarnameAccountManage = (): JSX.Element => { // eslint-disable-next-line no-console { title: "Renew", action: () => console.log("Renew") }, { title: "Transfer starname", action: () => history.push(STARNAME_TRANSFER_ROUTE, account) }, - // eslint-disable-next-line no-console - { title: "Delete starname", action: () => console.log("Delete starname") }, + { title: "Delete starname", action: () => history.push(STARNAME_DELETE_ROUTE, account) }, ]; React.useEffect(() => { diff --git a/packages/bierzo-wallet/src/routes/account/register/components/NameForm.tsx b/packages/bierzo-wallet/src/routes/account/register/components/NameForm.tsx index dc6e85d2f..c882896c7 100644 --- a/packages/bierzo-wallet/src/routes/account/register/components/NameForm.tsx +++ b/packages/bierzo-wallet/src/routes/account/register/components/NameForm.tsx @@ -107,15 +107,15 @@ const NameForm = ({ if (iovnameAddresses) { request = await generateReplaceAccountTargetsTxRequest( bnsIdentity, - domain, name, + domain, addressesToRegister, ); } else { request = await generateRegisterAccountTxRequest( bnsIdentity, - domain, name, + domain, bnsCodec.identityToAddress(bnsIdentity), addressesToRegister, ); diff --git a/packages/bierzo-wallet/src/routes/account/update/components/NameForm.tsx b/packages/bierzo-wallet/src/routes/account/update/components/NameForm.tsx index f4b6ceb4a..ef0079d50 100644 --- a/packages/bierzo-wallet/src/routes/account/update/components/NameForm.tsx +++ b/packages/bierzo-wallet/src/routes/account/update/components/NameForm.tsx @@ -59,8 +59,8 @@ const NameAccountUpdate = ({ try { const request = await generateReplaceAccountTargetsTxRequest( bnsIdentity, - account.domain, account.name, + account.domain, addressesToRegister, ); diff --git a/packages/bierzo-wallet/src/routes/index.tsx b/packages/bierzo-wallet/src/routes/index.tsx index 8f840f3a2..5addf8522 100644 --- a/packages/bierzo-wallet/src/routes/index.tsx +++ b/packages/bierzo-wallet/src/routes/index.tsx @@ -3,7 +3,7 @@ import React from "react"; import { Route, Router, Switch } from "react-router"; import RequireLogin from "../components/RequireLogin"; -import { AccountManage, AccountRegister, AccountUpdate } from "./account"; +import { AccountDelete, AccountManage, AccountRegister, AccountUpdate } from "./account"; import AccountTransfer from "./account/transfer"; import Addresses from "./addresses"; import Balance from "./balance"; @@ -16,12 +16,14 @@ import { IOVNAME_REGISTER_ROUTE, IOVNAME_TRANSFER_ROUTE, LOGIN_ROUTE, + NAME_DELETE_ROUTE, NAME_EDIT_ROUTE, NAME_MANAGE_ROUTE, NAME_REGISTER_ROUTE, NAME_TRANSFER_ROUTE, PAYMENT_ROUTE, POLICY_ROUTE, + STARNAME_DELETE_ROUTE, STARNAME_MANAGE_ROUTE, STARNAME_REGISTER_ROUTE, STARNAME_TRANSFER_ROUTE, @@ -43,17 +45,25 @@ const Routes = (): JSX.Element => ( + } /> } /> } /> + } /> } /> } /> + } /> } /> + } /> } /> } /> + + } /> + } /> + diff --git a/packages/bierzo-wallet/src/routes/paths.ts b/packages/bierzo-wallet/src/routes/paths.ts index a72ba2eaf..eec290dc8 100644 --- a/packages/bierzo-wallet/src/routes/paths.ts +++ b/packages/bierzo-wallet/src/routes/paths.ts @@ -13,10 +13,12 @@ export const NAME_MANAGE_ROUTE = "/name/manage"; export const NAME_EDIT_ROUTE = "/name/edit"; export const NAME_REGISTER_ROUTE = "/name/register"; export const NAME_TRANSFER_ROUTE = "/name/transfer"; +export const NAME_DELETE_ROUTE = "/name/delete"; // Account: Starnames paths export const STARNAME_MANAGE_ROUTE = "/starname/manage"; export const STARNAME_REGISTER_ROUTE = "/starname/register"; export const STARNAME_TRANSFER_ROUTE = "/starname/transfer"; +export const STARNAME_DELETE_ROUTE = "/starname/delete"; export const TERMS_ROUTE = "/terms"; export const POLICY_ROUTE = "/policy"; diff --git a/packages/sanes-browser-extension/src/extension/background/model/persona/index.ts b/packages/sanes-browser-extension/src/extension/background/model/persona/index.ts index 7fdc60bdb..be3066621 100644 --- a/packages/sanes-browser-extension/src/extension/background/model/persona/index.ts +++ b/packages/sanes-browser-extension/src/extension/background/model/persona/index.ts @@ -4,7 +4,11 @@ import { BnsConnection, BnsUsernameNft, CreateProposalTx, + DeleteAccountTx, + DeleteDomainTx, isCreateProposalTx, + isDeleteAccountTx, + isDeleteDomainTx, isRegisterAccountTx, isRegisterDomainTx, isRegisterUsernameTx, @@ -52,6 +56,8 @@ function isNonUndefined(t: T | undefined): t is T { */ export type SupportedTransaction = | SendTransaction + | DeleteAccountTx + | DeleteDomainTx | RegisterUsernameTx | UpdateTargetsOfUsernameTx | TransferUsernameTx @@ -66,6 +72,8 @@ export type SupportedTransaction = export function isSupportedTransaction(tx: UnsignedTransaction): tx is SupportedTransaction { return ( isSendTransaction(tx) || + isDeleteDomainTx(tx) || + isDeleteAccountTx(tx) || isRegisterUsernameTx(tx) || isUpdateTargetsOfUsernameTx(tx) || isTransferUsernameTx(tx) || diff --git a/packages/valdueza-storybook/src/__snapshots__/Storyshots.test.js.snap b/packages/valdueza-storybook/src/__snapshots__/Storyshots.test.js.snap index 546d39def..58eb6be4b 100644 --- a/packages/valdueza-storybook/src/__snapshots__/Storyshots.test.js.snap +++ b/packages/valdueza-storybook/src/__snapshots__/Storyshots.test.js.snap @@ -1686,6 +1686,212 @@ exports[`Storyshots Bierzo Wallet Terms 1`] = ` `; +exports[`Storyshots Bierzo Wallet/Account Delete Delete sample 1`] = ` +
+
+
+
+
+ You are deleting + +
+
+ albert*iov +
+
+

+ Some additional description +

+
+
+
+
+
+
+
+ + +
+
+
+
+
+`; + +exports[`Storyshots Bierzo Wallet/Account Delete Delete starname 1`] = ` +
+
+
+
+
+ You are deleting + +
+
+ test2*iov +
+
+

+ Some additional description +

+
+
+
+
+
+
+
+ + +
+
+
+
+
+`; + exports[`Storyshots Bierzo Wallet/Account Manage Associated names list 1`] = `
`; +exports[`Storyshots Bierzo Wallet/Account Operation Sample 1`] = ` +
+
+
+
+
+ You are makeing operation with + +
+
+ albert*iov +
+
+ + account +
+
+

+ Some additional description +

+
+
+
+
+
+
+
+ + +
+
+
+
+
+`; + exports[`Storyshots Bierzo Wallet/Account Transfer Transfer iovname 1`] = `