Skip to content

Commit

Permalink
Merge pull request #1034 from iov-one/feature/#1006-list-registered-n…
Browse files Browse the repository at this point in the history
…ames

Feature #1006 - List registered "names"
  • Loading branch information
albertandrejev authored Mar 4, 2020
2 parents 07c0c61 + df5a7f1 commit e366bbf
Show file tree
Hide file tree
Showing 27 changed files with 589 additions and 709 deletions.
7 changes: 7 additions & 0 deletions packages/bierzo-wallet/src/assets/iovname-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions packages/bierzo-wallet/src/assets/starname-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 77 additions & 15 deletions packages/bierzo-wallet/src/logic/transactions/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,86 @@
import { ChainId, Identity, isFailedTransaction } from "@iov/bcp";
import { isRegisterUsernameTx, isUpdateTargetsOfUsernameTx } from "@iov/bns";
import { ChainId, Identity, isFailedTransaction, UnsignedTransaction } from "@iov/bcp";
import {
isRegisterAccountTx,
isRegisterDomainTx,
isRegisterUsernameTx,
isReplaceAccountTargetsTx,
isUpdateTargetsOfUsernameTx,
} from "@iov/bns";
import { Dispatch } from "redux";
import { Subscription } from "xstream";

import { getConfig } from "../../config";
import { addAccountsAction, BwAccount } from "../../store/accounts";
import { addTransaction } from "../../store/notifications";
import { addUsernamesAction, BwUsername } from "../../store/usernames";
import { getCodec } from "../codec";
import { getConnectionForChainId } from "../connection";
import { getConnectionForBns, getConnectionForChainId } from "../connection";
import { BwParserFactory } from "./types/BwParserFactory";

function mayDispatchUsername(dispatch: Dispatch, usernameTx: UnsignedTransaction): void {
if (isRegisterUsernameTx(usernameTx) || isUpdateTargetsOfUsernameTx(usernameTx)) {
const username: BwUsername = {
username: usernameTx.username,
addresses: usernameTx.targets,
};

dispatch(addUsernamesAction([username]));
}
}

async function mayDispatchAccount(dispatch: Dispatch, accountTx: UnsignedTransaction): Promise<void> {
if (isRegisterDomainTx(accountTx)) {
const nowInMs = new Date().getTime();
const accountRenewInMs = accountTx.accountRenew * 1000;
// TODO not sure if precise, since getting from current time
const expiryDate = new Date(nowInMs + accountRenewInMs);

const account: BwAccount = {
name: "",
domain: accountTx.domain,
expiryDate: expiryDate,
addresses: [],
};

dispatch(addAccountsAction([account]));
}

if (isRegisterAccountTx(accountTx)) {
const connection = await getConnectionForBns();
const domains = await connection.getDomains({ name: accountTx.domain });
if (domains.length !== 1) throw Error("Did not find unique domain");

const nowInMs = new Date().getTime();
const accountRenewInMs = domains[0].accountRenew * 1000;
// TODO not sure if precise, since getting from current time
const expiryDate = new Date(nowInMs + accountRenewInMs);

const account: BwAccount = {
name: accountTx.name,
domain: accountTx.domain,
expiryDate: expiryDate,
addresses: accountTx.targets,
};

dispatch(addAccountsAction([account]));
}

if (isReplaceAccountTargetsTx(accountTx)) {
const connection = await getConnectionForBns();
const accounts = await connection.getAccounts({ name: accountTx.name, domain: accountTx.domain });
if (accounts.length !== 1) throw Error("Did not find unique account");

const account: BwAccount = {
name: accounts[0].name || "",
domain: accounts[0].domain,
expiryDate: new Date(accounts[0].validUntil * 1000),
addresses: accounts[0].targets,
};

dispatch(addAccountsAction([account]));
}
}

let txsSubscriptions: Subscription[] = [];

export async function subscribeTransaction(
Expand Down Expand Up @@ -38,18 +109,9 @@ export async function subscribeTransaction(
const bwTransaction = BwParserFactory.getBwTransactionFrom(tx);
const parsedTx = await bwTransaction.parse(connection, tx, address);

if (
!isFailedTransaction(tx) &&
(isRegisterUsernameTx(parsedTx.original) || isUpdateTargetsOfUsernameTx(parsedTx.original))
) {
const usernameTx = parsedTx.original;
const usernames: BwUsername[] = [
{
username: usernameTx.username,
addresses: usernameTx.targets,
},
];
dispatch(addUsernamesAction(usernames));
if (!isFailedTransaction(tx)) {
mayDispatchUsername(dispatch, parsedTx.original);
await mayDispatchAccount(dispatch, parsedTx.original);
}
dispatch(addTransaction(parsedTx));
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ function TabItem({ children, selected, onChangeTab }: TabItemProps): JSX.Element

function AddressesTab({
chainAddresses,
usernames,
iovnames,
starnames,
onRegisterUsername,
onRegisterIovname,
onRegisterStarname,
rpcEndpointType,
}: AddressesTableProps & IovnamesProps & StarnamesProps): JSX.Element {
Expand Down Expand Up @@ -90,8 +90,8 @@ function AddressesTab({
)}
{selectedTab === "iovnames" && (
<Iovnames
usernames={usernames}
onRegisterUsername={onRegisterUsername}
iovnames={iovnames}
onRegisterIovname={onRegisterIovname}
rpcEndpointType={rpcEndpointType}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
import { Block } from "medulas-react-components";
import React from "react";

import { BwUsernameWithChainName } from "..";
import { RpcEndpointType } from "../../../communication/rpcEndpoint";
import { BwUsername } from "../../../store/usernames";
import IovnamesExists from "./IovnamesExists";
import IovnamesNotExists from "./IovnamesNotExists";

export const iovnamesViewId = "iovnames-view-id";

export interface IovnamesProps {
readonly usernames: readonly BwUsernameWithChainName[];
readonly onRegisterUsername: () => void;
readonly iovnames: readonly BwUsername[];
readonly onRegisterIovname: () => void;
readonly rpcEndpointType: RpcEndpointType;
}

const Iovnames = ({ usernames, rpcEndpointType, onRegisterUsername }: IovnamesProps): JSX.Element => {
const hasIovnames = usernames.length > 0;
const Iovnames = ({ iovnames, rpcEndpointType, onRegisterIovname }: IovnamesProps): JSX.Element => {
const hasIovnames = iovnames.length > 0;

return (
<Block marginTop={3} id={iovnamesViewId}>
<Block margin={2} />
{!hasIovnames && (
<IovnamesNotExists rpcEndpointType={rpcEndpointType} onRegisterUsername={onRegisterUsername} />
<IovnamesNotExists rpcEndpointType={rpcEndpointType} onRegisterIovname={onRegisterIovname} />
)}
{hasIovnames && <IovnamesExists usernames={usernames} onRegisterUsername={onRegisterUsername} />}
{hasIovnames && <IovnamesExists iovnames={iovnames} onRegisterIovname={onRegisterIovname} />}
</Block>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,121 +1,95 @@
import Paper from "@material-ui/core/Paper";
import clipboardCopy from "clipboard-copy";
import {
Block,
Image,
makeStyles,
ToastContext,
ToastVariant,
Tooltip,
Typography,
} from "medulas-react-components";
import { Block, Image, makeStyles, Typography } from "medulas-react-components";
import React from "react";

import { BwUsernameWithChainName } from "..";
import { history } from "../..";
import AddressesTable from "../../../components/AddressesTable";
import copy from "../../../components/AddressesTable/assets/copy.svg";
import iovnameLogo from "../../../assets/iovname-logo.svg";
import { BwUsername } from "../../../store/usernames";
import { REGISTER_IOVNAME_ROUTE } from "../../paths";
import { AddressesTooltipHeader, TooltipContent } from "../../register";

interface Props {
readonly usernames: readonly BwUsernameWithChainName[];
readonly onRegisterUsername: () => void;
readonly iovnames: readonly BwUsername[];
readonly onRegisterIovname: () => void;
}

const usePaper = makeStyles({
rounded: {
borderRadius: "5px",
height: "100%",
},
elevation1: {
boxShadow: "none",
},
});

const useStyles = makeStyles({
link: {
cursor: "pointer",
},
});

function IovnamesExists({ usernames, onRegisterUsername }: Props): JSX.Element {
function IovnamesExists({ iovnames, onRegisterIovname }: Props): JSX.Element {
const paperClasses = usePaper();
const classes = useStyles();
const toast = React.useContext(ToastContext);

return (
<React.Fragment>
<Typography
id={REGISTER_IOVNAME_ROUTE}
link
color="primary"
align="center"
onClick={onRegisterUsername}
>
+ Create new iovname
</Typography>
{usernames.map(username => {
const onIovnameCopy = (): void => {
clipboardCopy(username.username);
toast.show("Iovname has been copied to clipboard.", ToastVariant.INFO);
};

const onEdit = (): void => {
history.push(REGISTER_IOVNAME_ROUTE, username);
};
<Paper classes={paperClasses}>
<Block
width={650}
padding={5}
border="1px solid #F3F3F3"
display="flex"
alignItems="center"
justifyContent="space-between"
>
<Block display="flex" alignItems="center">
<Image alt="Iovname Logo" src={iovnameLogo} />
<Block marginLeft={4} />
<Typography variant="h5" align="center">
Register a new iovname
</Typography>
</Block>
<Typography id={REGISTER_IOVNAME_ROUTE} link color="primary" onClick={onRegisterIovname}>
Register now
</Typography>
</Block>
</Paper>
{iovnames
.slice()
.sort((a, b) => {
if (a.username < b.username) return -1;
if (a.username > b.username) return 1;
return 0;
})
.map(iovname => {
const onManage = (): void => {
history.push(REGISTER_IOVNAME_ROUTE, iovname);
};

return (
<Block key={username.username} marginTop={1} width={650}>
<Paper classes={paperClasses}>
<Block
display="flex"
flexDirection="column"
width="100%"
marginTop={4}
padding={5}
border="1px solid #F3F3F3"
>
<Block display="flex" alignItems="center" alignSelf="center">
<Typography variant="h4" align="center">
{username.username}
</Typography>
<Block marginRight={2} />
<Block onClick={onIovnameCopy} className={classes.link}>
<Image src={copy} alt="Copy" width={20} />
</Block>
</Block>
<Block display="flex" alignItems="center" marginBottom={1} marginTop={4}>
<Typography variant="subtitle2" weight="semibold" inline>
{username.addresses.length > 0 ? "LINKED ADDRESSES" : "NO LINKED ADDRESSES"}
return (
<React.Fragment key={iovname.username}>
<Block marginTop={3} />
<Paper classes={paperClasses}>
<Block
width={650}
padding={5}
border="1px solid #F3F3F3"
display="flex"
alignItems="center"
justifyContent="space-between"
>
<Typography variant="h5" weight="semibold">
{iovname.username}
</Typography>
<Block marginRight={1} />
<Tooltip maxWidth={320}>
<TooltipContent header={<AddressesTooltipHeader />} title="Your linked addresses">
With IOV you can have an universal blockchain address that is linked to all your
addresses. Just give your friends your personalized address.
</TooltipContent>
</Tooltip>
<Block flexGrow={1} />
<Typography
variant="subtitle2"
weight="semibold"
inline
link
color="primary"
align="right"
onClick={onEdit}
onClick={onManage}
>
{username.addresses.length > 0 ? "Edit" : "Link Now"}
Manage
</Typography>
<Block marginLeft={1} />
</Block>
<Block marginTop={2} />
{username.addresses.length > 0 && <AddressesTable chainAddresses={username.addresses} />}
</Block>
</Paper>
</Block>
);
})}
</Paper>
</React.Fragment>
);
})}
</React.Fragment>
);
}
Expand Down
Loading

0 comments on commit e366bbf

Please sign in to comment.