ENS Resolving (#942)
* Refactor BaseNode to be an interface INode * Initial contract commit * Remove redundant fallback ABI function * First working iteration of Contract generator to be used in ENS branch * Hide abi to clean up logging output * Strip 0x prefix from output decode * Handle unnamed output params * Implement ability to supply output mappings to ABI functions * Fix null case in outputMapping * Add flow typing * Add .call method to functions * Partial commit for type refactor * Temp contract type fix -- waiting for NPM modularization * Remove empty files * Cleanup contract * Add call request to node interface * Fix output mapping types * Revert destructuring overboard * Add sendCallRequest to rpcNode class and add typing * Use enum for selecting ABI methods * Add transaction capability to contracts * Cleanup privaite/public members * Remove broadcasting step from a contract transaction * Cleanup uneeded types * Refactor ens-base to typescript and add typings for ENS smart contracts * Migrate ens-name-search to TS * Add IResolveDomainRequest * Fix rest of TSC errors * Add definition file for bn.js * Remove types-bn * Fix some typings * make isBN a static property * progress commit -- swap out bignumber.js for bn.js * Swap out bignumber for bn in vendor * Change modn to number return * Start to strip out units lib for a string manipulation based lib * Convert codebase to only base units * Get rid of useless component * Handle only wei in values * Use unit conversion in sidebar * Automatically strip hex prefix, and handle decimal edge case * Handle base 16 wei in transactions * Make a render callback component for dealing with unit conversion * Switch contracts to use bn.js, and get transaction values from signedTx instead of state * Get send transaction working with bn.js * Remove redundant hex stripping, return base value of tokens * Cleanup unit file * Re-implement toFixed for strings * Use formatNumber in codebase * Cleanup code * Undo package test changes * Update snapshot and remove console logs * Use TokenValue / Wei more consistently where applicable * Add typing to deterministicWallets, fix confirmation modal, make UnitDisplay more flexible * Split different ENS modes into their own components * Fix Abi typedef * Remove redundant moment type package * Add Aux helper component * Split out resolve components * Make 'to' parameter optional * Change import type * Change typing to be base domain request * Split handling of resolving into object handler * Fix countdown component * Adjust element spacing * Implement reveal search functionality * Add unit display for highest bidder * Fill out forbidden/NYA modes * ENS wallet component skeleton * Clean up prop handling in UnitDisplay * Change instanceof to typeof check, change boolean of displayBalance * Add ENS wallet component * Cleanup spacing * Convert ConfModal for bidding in ENS * Make ui component for placing bids * Fix destructure in placeBid * Pass through entire wallet * Remove text center * Display inline notification ENS isValid & add some ENS tests * Add export of Aux * Reformat with prettier * progress... * Add ENSUnlockLayout * Add RevealBid component * organize NameResolve components * Merge ENS with transaction-refactor changes * Fix address resolution * Update styles * convert ens name to lowercase before checking * Add overflow-y:scroll to table * update ens snapshots & tests * cast 'undefined' state argument as any for testing * clean up components * Connect unitconverter to redux state * remove unnecessary type assertion * fix spinner size * remove old bidmodal * validate bidmask before opening modal * progress... * Update styles * Add saga / actions for placing a bid * Update types & clean up dead code * Delete old test * Dispatch PlaceBidRequested acitons * Progress commit -- get ENS bidding ready for tx generation via sagas * Seperate ENS action creators and types * Add reducer & actions for ENS fields * Add preliminary sagas for bid mask and bid value * Fix ts errors * Get bidding fields connected with some validation * Clean up generate bid * Hook up generate bid to redux state * Get bid data generation working * Add support for bidding on already open auctions * Move bid generation states to redux, improve default field values * Remove generate bid component * Throttle bid generation * Progress commit -- Bid Modal * Hook bidmodal component up to bidding component * Update template modal to handle custom confirm behavior * Remove old redux bidding actions, add new one for downloaded bids * Save downloaded bids to local storage * Finish bidding modal * Fix gas estimation bug * Fix typing * Remove bidding related functionality * Get passing unit tests * Make previous test more comprehensive * Fix ts errors * Remove commented code * Fix invalid return * Remove implementation of revealing bid * Update snapshot * Fix tests * Delegate bidding to V3
This commit is contained in:
parent
8f3669cbc6
commit
2a4ad180d3
|
@ -1,5 +1,4 @@
|
|||
import { ResolveDomainAction } from './resolveDomain';
|
||||
|
||||
export * from './resolveDomain';
|
||||
|
||||
export type EnsAction = ResolveDomainAction;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export enum TypeKeys {
|
||||
ENS_RESOLVE_DOMAIN_REQUESTED = 'ENS_RESOLVE_DOMAIN_REQUESTED',
|
||||
ENS_RESOLVE_DOMAIN_SUCCEEDED = 'ENS_RESOLVE_DOMAIN_SUCCEEDED',
|
||||
ENS_RESOLVE_DOMAIN_CACHED = 'ENS_RESOLVE_DOMAIN_CACHED',
|
||||
ENS_RESOLVE_DOMAIN_FAILED = 'ENS_RESOLVE_DOMAIN_FAILED'
|
||||
ENS_RESOLVE_DOMAIN_FAILED = 'ENS_RESOLVE_DOMAIN_FAILED',
|
||||
ENS_RESOLVE_DOMAIN_CACHED = 'ENS_RESOLVE_DOMAIN_CACHED'
|
||||
}
|
||||
|
|
|
@ -69,12 +69,12 @@ class DeployClass extends Component<DispatchProps> {
|
|||
</div>
|
||||
<SigningStatus />
|
||||
<SendButtonFactory
|
||||
Modal={ConfirmationModal}
|
||||
withProps={({ onClick }) => (
|
||||
<button className="Deploy-submit btn btn-primary" onClick={onClick}>
|
||||
{translate('NAV_DeployContract')}
|
||||
</button>
|
||||
)}
|
||||
Modal={ConfirmationModal}
|
||||
/>
|
||||
</main>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import React from 'react';
|
||||
import NameInputHoc from './NameInputHOC';
|
||||
|
||||
interface Props {
|
||||
isValidDomain: boolean;
|
||||
domainToCheck: string;
|
||||
onClick(ev: React.FormEvent<HTMLButtonElement>): void;
|
||||
onChange(ev: React.FormEvent<HTMLInputElement>): void;
|
||||
}
|
||||
class ENSNameInput extends React.Component<Props, {}> {
|
||||
public render() {
|
||||
const { onChange, onClick, isValidDomain, domainToCheck } = this.props;
|
||||
return (
|
||||
<article className="row">
|
||||
<section className="col-xs-12 col-sm-6 col-sm-offset-3 text-center">
|
||||
<div className="input-group">
|
||||
<input
|
||||
className={`form-control ${
|
||||
domainToCheck === '' ? '' : isValidDomain ? 'is-valid' : 'is-invalid'
|
||||
}`}
|
||||
type="text"
|
||||
placeholder="myetherwallet"
|
||||
onChange={onChange}
|
||||
/>
|
||||
<div className="input-group-btn">
|
||||
<a className="btn btn-default">.eth</a>
|
||||
</div>
|
||||
</div>
|
||||
{isValidDomain ? null : <p>Use at least 7 characters</p>}
|
||||
<button className="btn btn-primary " onClick={onClick}>
|
||||
Check ENS Name
|
||||
</button>
|
||||
</section>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default NameInputHoc(ENSNameInput);
|
|
@ -0,0 +1,42 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
import { isValidENSName } from 'libs/validators';
|
||||
interface State {
|
||||
domainToCheck: string;
|
||||
isValidDomain: boolean;
|
||||
}
|
||||
interface Props {
|
||||
resolveDomainRequested(domain: string): void;
|
||||
}
|
||||
|
||||
const NameInputHoc = PassedComponent =>
|
||||
class HOC extends Component<Props, State> {
|
||||
public state = {
|
||||
isValidDomain: false,
|
||||
domainToCheck: ''
|
||||
};
|
||||
//add delay to namehash computation / getting the availability
|
||||
public onChange = (event: React.FormEvent<HTMLButtonElement>) => {
|
||||
const domainToCheck: string = event.currentTarget.value.toLowerCase();
|
||||
this.setState({ domainToCheck });
|
||||
const isValidName: boolean = isValidENSName(domainToCheck);
|
||||
this.setState({ isValidDomain: isValidName });
|
||||
};
|
||||
public onClick = () => {
|
||||
const { isValidDomain, domainToCheck } = this.state;
|
||||
const { resolveDomainRequested } = this.props;
|
||||
return isValidDomain && resolveDomainRequested(domainToCheck);
|
||||
};
|
||||
public render() {
|
||||
const { onChange, onClick } = this;
|
||||
const { isValidDomain, domainToCheck } = this.state;
|
||||
const props = {
|
||||
onChange,
|
||||
onClick,
|
||||
isValidDomain,
|
||||
domainToCheck
|
||||
};
|
||||
return <PassedComponent {...props} />;
|
||||
}
|
||||
};
|
||||
export default NameInputHoc;
|
|
@ -0,0 +1 @@
|
|||
export { default as NameInput } from './components/NameInput';
|
|
@ -0,0 +1,40 @@
|
|||
.auction-info {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.ens-title {
|
||||
margin: 2rem 0;
|
||||
h2 {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ens-panel-wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
.ens-panel {
|
||||
flex-grow: 1;
|
||||
color: white;
|
||||
padding: 1rem;
|
||||
background-color: #185475;
|
||||
|
||||
p,
|
||||
h4 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&-light {
|
||||
background-color: #1e92ba;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
@media (max-width: 820px) {
|
||||
.ens-panel {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
import React from 'react';
|
||||
import { IBaseDomainRequest } from 'libs/ens';
|
||||
import ENSTime from './components/ENSTime';
|
||||
import moment from 'moment';
|
||||
import { NewTabLink } from 'components/ui';
|
||||
|
||||
const getDeadlines = (registrationDate: string) => {
|
||||
// Get the time to reveal bids, and the time when the action closes
|
||||
const time = moment(+registrationDate * 1000);
|
||||
const auctionCloseTime = +time;
|
||||
const revealBidTime = +time.subtract(2, 'days');
|
||||
return { auctionCloseTime, revealBidTime };
|
||||
};
|
||||
|
||||
export const NameAuction: React.SFC<IBaseDomainRequest> = props => {
|
||||
const { registrationDate, name } = props;
|
||||
const { auctionCloseTime, revealBidTime } = getDeadlines(registrationDate);
|
||||
return (
|
||||
<section className="row">
|
||||
<div className="auction-info text-center">
|
||||
<div className="ens-title">
|
||||
<h1>
|
||||
An auction has started for <strong>{name}.eth</strong>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="ens-panel-wrapper">
|
||||
<section className="ens-panel">
|
||||
<ENSTime text="Reveal Bids On" time={revealBidTime} />
|
||||
</section>
|
||||
<section className="ens-panel ens-panel-light">
|
||||
<ENSTime text="Auction Closes On" time={auctionCloseTime} />
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<NewTabLink
|
||||
content={`Do you want to place a bid on ${name}.eth? You'll need to bid on MyCrypto V3 by clicking here: `}
|
||||
href="https://mycrypto.com/#ens"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
import React from 'react';
|
||||
import { IBaseDomainRequest } from 'libs/ens';
|
||||
|
||||
export const NameForbidden: React.SFC<IBaseDomainRequest> = props => (
|
||||
<h1>{props.name}.eth is forbidden</h1>
|
||||
);
|
|
@ -0,0 +1,6 @@
|
|||
import React from 'react';
|
||||
import { IBaseDomainRequest } from 'libs/ens';
|
||||
|
||||
export const NameNotYetAvailable: React.SFC<IBaseDomainRequest> = props => (
|
||||
<h1>{props.name}.eth is not yet available</h1>
|
||||
);
|
|
@ -0,0 +1,23 @@
|
|||
import React from 'react';
|
||||
import { IBaseDomainRequest } from 'libs/ens';
|
||||
import { NewTabLink } from 'components/ui';
|
||||
|
||||
export const NameOpen: React.SFC<IBaseDomainRequest> = props => (
|
||||
<section className="row">
|
||||
<section className="auction-info text-center">
|
||||
<div className="ens-title">
|
||||
<h1>
|
||||
<strong>{props.name}.eth</strong> is available!
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<NewTabLink
|
||||
className="text-center"
|
||||
content={`Do you want ${
|
||||
props.name
|
||||
}.eth? You'll need open an auction on MyCrypto V3 by clicking here`}
|
||||
href="https://mycrypto.com/#ens"
|
||||
/>
|
||||
</section>
|
||||
</section>
|
||||
);
|
|
@ -0,0 +1,59 @@
|
|||
import React from 'react';
|
||||
import { IOwnedDomainRequest } from 'libs/ens';
|
||||
import { NewTabLink } from 'components/ui';
|
||||
const lookupLink = name => `https://etherscan.io/enslookup?q=${name}`;
|
||||
|
||||
type ChildrenProps = any;
|
||||
|
||||
const MonoTd = ({ children }: ChildrenProps) => <td className="mono">{children}</td>;
|
||||
|
||||
export const NameOwned: React.SFC<IOwnedDomainRequest> = ({
|
||||
highestBid,
|
||||
labelHash,
|
||||
nameHash,
|
||||
resolvedAddress,
|
||||
ownerAddress,
|
||||
name
|
||||
}) => (
|
||||
<section>
|
||||
<div className="ens-title">
|
||||
<h1 className="text-center">
|
||||
<strong>{name}.eth</strong> is already owned
|
||||
</h1>
|
||||
</div>
|
||||
<div className="table-wrapper">
|
||||
<table className="table table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Name: </td>
|
||||
<MonoTd>
|
||||
<NewTabLink content={`${name}.eth`} href={lookupLink(`${name}.eth`)} />
|
||||
</MonoTd>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Labelhash ({name}): </td>
|
||||
<MonoTd>{labelHash}</MonoTd>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Namehash ({name}.eth): </td>
|
||||
<MonoTd>{nameHash}</MonoTd>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Owner:</td>
|
||||
<MonoTd>{ownerAddress}</MonoTd>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Highest Bidder (Deed Owner): </td>
|
||||
<MonoTd>
|
||||
<span>{highestBid}</span>
|
||||
</MonoTd>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Resolved Address: </td>
|
||||
<MonoTd>{resolvedAddress}</MonoTd>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
);
|
|
@ -0,0 +1,43 @@
|
|||
import React from 'react';
|
||||
import { IRevealDomainRequest } from 'libs/ens';
|
||||
import ENSTime from './components/ENSTime';
|
||||
import { UnitDisplay, NewTabLink } from 'components/ui';
|
||||
import { Wei } from 'libs/units';
|
||||
|
||||
export const NameReveal: React.SFC<IRevealDomainRequest> = props => (
|
||||
<section className="row text-center">
|
||||
<div className="auction-info text-center">
|
||||
<div className="ens-title">
|
||||
<h2>
|
||||
<p>
|
||||
It's time to reveal the bids for <strong>{props.name}.eth.</strong>{' '}
|
||||
</p>
|
||||
<p>
|
||||
Current Highest bid is{' '}
|
||||
<strong>
|
||||
<UnitDisplay
|
||||
value={Wei(props.highestBid)}
|
||||
unit="ether"
|
||||
symbol="ETH"
|
||||
displayShortBalance={false}
|
||||
checkOffline={false}
|
||||
/>
|
||||
</strong>
|
||||
</p>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="ens-panel-wrapper">
|
||||
<section className="ens-panel">
|
||||
<ENSTime text="Auction closes on" time={+props.registrationDate * 1000} />
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<NewTabLink
|
||||
content={`Did you you bid on ${
|
||||
props.name
|
||||
}.eth? You must reveal your bid now. You'll need reveal your bid on MyCrypto V3 by clicking here`}
|
||||
href="https://mycrypto.com/#ens"
|
||||
/>
|
||||
</section>
|
||||
);
|
|
@ -0,0 +1,72 @@
|
|||
import React, { Component } from 'react';
|
||||
import moment from 'moment';
|
||||
|
||||
interface Props {
|
||||
initialTime: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
currentTime: number;
|
||||
}
|
||||
|
||||
class CountDown extends Component<Props, State> {
|
||||
public state = { currentTime: 0 };
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.startCountDown();
|
||||
this.state = { currentTime: 0 };
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { currentTime } = this.state;
|
||||
return <p>{this.humanizeTime(currentTime)}</p>;
|
||||
}
|
||||
|
||||
private humanizeTime = (time: number) => {
|
||||
let timeRemaining = time;
|
||||
const floorTime = unit => Math.floor(timeRemaining / unit);
|
||||
const pad = (num: number) => num.toString().padStart(2, '0');
|
||||
const second = 1000;
|
||||
const minute = second * 60;
|
||||
const hour = minute * 60;
|
||||
const day = hour * 24;
|
||||
|
||||
const days = floorTime(day);
|
||||
timeRemaining -= days * day;
|
||||
const hours = floorTime(hour);
|
||||
timeRemaining -= hours * hour;
|
||||
const minutes = floorTime(minute);
|
||||
timeRemaining -= minutes * minute;
|
||||
const seconds = floorTime(second);
|
||||
|
||||
return `${pad(days)} Days ${pad(hours)} Hours ${pad(minutes)} Minutes ${pad(seconds)} Seconds `;
|
||||
};
|
||||
|
||||
private startCountDown = () => {
|
||||
const intervalId = window.setInterval(() => {
|
||||
const nextTime = +moment(this.props.initialTime).diff(+moment(), 'ms');
|
||||
|
||||
if (nextTime < 0) {
|
||||
return clearInterval(intervalId);
|
||||
}
|
||||
|
||||
this.setState({ currentTime: nextTime });
|
||||
}, 1000);
|
||||
};
|
||||
}
|
||||
|
||||
interface ITime {
|
||||
text: string;
|
||||
time: number;
|
||||
}
|
||||
|
||||
const ENSTime: React.SFC<ITime> = ({ text, time }) => (
|
||||
<section className="sm-6 col-xs-12 order-info">
|
||||
<p>{text}</p>
|
||||
<h4>{moment(time).toString()}</h4>
|
||||
<CountDown initialTime={time} />
|
||||
</section>
|
||||
);
|
||||
|
||||
export default ENSTime;
|
|
@ -0,0 +1,6 @@
|
|||
export { NameAuction } from './NameAuction';
|
||||
export { NameForbidden } from './NameForbidden';
|
||||
export { NameNotYetAvailable } from './NameNotYetAvailable';
|
||||
export { NameOpen } from './NameOpen';
|
||||
export { NameOwned } from './NameOwned';
|
||||
export { NameReveal } from './NameReveal';
|
|
@ -0,0 +1,38 @@
|
|||
import React from 'react';
|
||||
import { AppState } from 'reducers';
|
||||
import { NameState } from 'libs/ens';
|
||||
import {
|
||||
NameOwned,
|
||||
NameAuction,
|
||||
NameForbidden,
|
||||
NameNotYetAvailable,
|
||||
NameOpen,
|
||||
NameReveal
|
||||
} from './components';
|
||||
import './NameResolve.scss';
|
||||
import { Spinner } from 'components/ui';
|
||||
|
||||
type Props = AppState['ens'];
|
||||
|
||||
const modeResult = {
|
||||
[NameState.Auction]: NameAuction,
|
||||
[NameState.Forbidden]: NameForbidden,
|
||||
[NameState.NotYetAvailable]: NameNotYetAvailable,
|
||||
[NameState.Open]: NameOpen,
|
||||
[NameState.Owned]: NameOwned,
|
||||
[NameState.Reveal]: NameReveal
|
||||
};
|
||||
|
||||
export const NameResolve: React.SFC<Props> = props => {
|
||||
const { domainRequests, domainSelector } = props;
|
||||
const { currentDomain } = domainSelector;
|
||||
|
||||
if (!currentDomain || !domainRequests[currentDomain] || domainRequests[currentDomain].error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const domainData = domainRequests[currentDomain].data! || false;
|
||||
const Component = domainData ? modeResult[domainData.mode] : Spinner;
|
||||
|
||||
return <Component {...domainData} />;
|
||||
};
|
|
@ -1,47 +0,0 @@
|
|||
@import "common/sass/variables";
|
||||
|
||||
.UnfinishedBanner {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: $brand-warning;
|
||||
overflow: auto;
|
||||
z-index: 10000;
|
||||
|
||||
&-content {
|
||||
text-align: center;
|
||||
margin-top: 20%;
|
||||
@media screen and (min-width: 2150px) {
|
||||
margin-top: 15%;
|
||||
}
|
||||
color: #fff;
|
||||
text-shadow: 1px 1px 1px rgba(#000, 0.12);
|
||||
overflow: auto;
|
||||
|
||||
h2 {
|
||||
font-size: 52px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 30px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
// Fade out
|
||||
&.is-fading {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
background: #fff;
|
||||
transition: all 500ms ease 400ms;
|
||||
|
||||
.UnfinishedBanner-content {
|
||||
opacity: 0;
|
||||
transform: translateY(15px);
|
||||
transition: all 500ms ease;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
import React from 'react';
|
||||
import './index.scss';
|
||||
|
||||
interface State {
|
||||
isFading: boolean;
|
||||
hasAcknowledged: boolean;
|
||||
}
|
||||
export default class UnfinishedBanner extends React.Component<{}, State> {
|
||||
public state = {
|
||||
isFading: false,
|
||||
hasAcknowledged: false
|
||||
};
|
||||
|
||||
public render() {
|
||||
if (this.state.hasAcknowledged) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isFading = this.state.isFading ? 'is-fading' : '';
|
||||
|
||||
return (
|
||||
<div className={`UnfinishedBanner ${isFading}`} onClick={this.continue}>
|
||||
<div className="UnfinishedBanner-content">
|
||||
<h2>Under Contruction</h2>
|
||||
<p>The ENS section is still under contruction</p>
|
||||
<p>Expect unfinished components</p>
|
||||
<h3>Click to continue</h3>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private continue = () => {
|
||||
this.setState({ isFading: true });
|
||||
|
||||
setTimeout(() => {
|
||||
this.setState({ hasAcknowledged: true });
|
||||
}, 1000);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export { GeneralInfoPanel } from './GeneralInfoPanel';
|
||||
export { NameInput } from './NameInput';
|
||||
export { NameResolve } from './NameResolve';
|
|
@ -1,11 +1,13 @@
|
|||
import React from 'react';
|
||||
import { GeneralInfoPanel } from './components/GeneralInfoPanel';
|
||||
import UnfinishedBanner from './components/UnfinishedBanner';
|
||||
import { GeneralInfoPanel, NameInput, NameResolve } from './components';
|
||||
import TabSection from 'containers/TabSection';
|
||||
import { Route, Switch, RouteComponentProps } from 'react-router';
|
||||
import { RouteNotFound } from 'components/RouteNotFound';
|
||||
import { NewTabLink } from 'components/ui';
|
||||
import translate from 'translations';
|
||||
import { connect } from 'react-redux';
|
||||
import { resolveDomainRequested, TResolveDomainRequested } from 'actions/ens';
|
||||
import { AppState } from 'reducers';
|
||||
|
||||
const ENSDocsLink = () => (
|
||||
<NewTabLink
|
||||
|
@ -26,7 +28,17 @@ const ENSTitle = () => (
|
|||
</article>
|
||||
);
|
||||
|
||||
class ENSClass extends React.Component<RouteComponentProps<{}>> {
|
||||
interface StateProps {
|
||||
ens: AppState['ens'];
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
resolveDomainRequested: TResolveDomainRequested;
|
||||
}
|
||||
|
||||
type Props = StateProps & DispatchProps;
|
||||
|
||||
class ENSClass extends React.Component<RouteComponentProps<any> & Props> {
|
||||
public render() {
|
||||
const { match } = this.props;
|
||||
const currentPath = match.url;
|
||||
|
@ -39,8 +51,9 @@ class ENSClass extends React.Component<RouteComponentProps<{}>> {
|
|||
path={currentPath}
|
||||
render={() => (
|
||||
<section role="main" className="row">
|
||||
<UnfinishedBanner />
|
||||
<ENSTitle />
|
||||
<NameInput resolveDomainRequested={this.props.resolveDomainRequested} />
|
||||
<NameResolve {...this.props.ens} />
|
||||
<GeneralInfoPanel />
|
||||
</section>
|
||||
)}
|
||||
|
@ -53,4 +66,7 @@ class ENSClass extends React.Component<RouteComponentProps<{}>> {
|
|||
}
|
||||
}
|
||||
|
||||
export default ENSClass;
|
||||
const mapStateToProps = (state: AppState): StateProps => ({ ens: state.ens });
|
||||
const mapDispatchToProps: DispatchProps = { resolveDomainRequested };
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ENSClass);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import {
|
||||
EnsAction,
|
||||
ResolveDomainRequested,
|
||||
ResolveDomainFailed,
|
||||
ResolveDomainSucceeded,
|
||||
ResolveDomainCached
|
||||
ResolveDomainCached,
|
||||
ResolveDomainAction
|
||||
} from 'actions/ens';
|
||||
import { DomainRequest } from 'libs/ens';
|
||||
import { TypeKeys } from 'actions/ens/constants';
|
||||
|
@ -66,7 +66,7 @@ const resolveDomainFailed = (state: State, action: ResolveDomainFailed): State =
|
|||
return { ...state, [domain]: nextDomain };
|
||||
};
|
||||
|
||||
export default (state: State = INITIAL_STATE, action: EnsAction): State => {
|
||||
export default (state: State = INITIAL_STATE, action: ResolveDomainAction): State => {
|
||||
switch (action.type) {
|
||||
case TypeKeys.ENS_RESOLVE_DOMAIN_REQUESTED:
|
||||
return resolveDomainRequested(state, action);
|
||||
|
|
|
@ -8,4 +8,4 @@ export interface State {
|
|||
domainRequests: DRState;
|
||||
}
|
||||
|
||||
export const ens = combineReducers({ domainSelector, domainRequests });
|
||||
export const ens = combineReducers<State>({ domainSelector, domainRequests });
|
||||
|
|
|
@ -12,6 +12,7 @@ import { call, put, select, all, actionChannel, take, fork, race } from 'redux-s
|
|||
import { showNotification } from 'actions/notifications';
|
||||
import { resolveDomainRequest } from './modeMap';
|
||||
import { getCurrentDomainName, getCurrentDomainData } from 'selectors/ens';
|
||||
import { IBaseDomainRequest } from 'libs/ens';
|
||||
|
||||
function* shouldResolveDomain(domain: string) {
|
||||
const currentDomainName = yield select(getCurrentDomainName);
|
||||
|
@ -43,18 +44,19 @@ function* resolveDomain(): SagaIterator {
|
|||
}
|
||||
|
||||
const node: INode = yield select(getNodeLib);
|
||||
const result = yield race({
|
||||
|
||||
const result: { domainData: IBaseDomainRequest; error } = yield race({
|
||||
domainData: call(resolveDomainRequest, domain, node),
|
||||
err: call(delay, 4000)
|
||||
});
|
||||
|
||||
const { domainData } = result;
|
||||
|
||||
if (!domainData) {
|
||||
throw Error();
|
||||
}
|
||||
const domainSuccessAction = resolveDomainSucceeded(domain, domainData);
|
||||
yield put(domainSuccessAction);
|
||||
yield;
|
||||
} catch (e) {
|
||||
const domainFailAction = resolveDomainFailed(domain, e);
|
||||
yield put(domainFailAction);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { IDomainData, NameState, getNameHash } from 'libs/ens';
|
||||
import { IDomainData, NameState, getNameHash, IBaseDomainRequest } from 'libs/ens';
|
||||
import ENS from 'libs/ens/contracts';
|
||||
import { SagaIterator } from 'redux-saga';
|
||||
import { call } from 'redux-saga/effects';
|
||||
|
@ -48,7 +48,7 @@ function* nameStateReveal({ deedAddress }: IDomainData<NameState.Reveal>): SagaI
|
|||
data: ENS.deed.owner.encodeInput(),
|
||||
decoder: ENS.deed.owner.decodeOutput
|
||||
});
|
||||
return ownerAddress;
|
||||
return { ownerAddress };
|
||||
}
|
||||
|
||||
interface IModeMap {
|
||||
|
@ -83,11 +83,12 @@ export function* resolveDomainRequest(name: string): SagaIterator {
|
|||
const nameStateHandler = modeMap[domainData.mode];
|
||||
const result = yield call(nameStateHandler, domainData, nameHash);
|
||||
|
||||
return {
|
||||
const returnValue: IBaseDomainRequest = {
|
||||
name,
|
||||
...domainData,
|
||||
...result,
|
||||
labelHash: hash.toString('hex'),
|
||||
nameHash
|
||||
};
|
||||
return returnValue;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import { composeWithDevTools } from 'redux-devtools-extension';
|
|||
import { createLogger } from 'redux-logger';
|
||||
import createSagaMiddleware from 'redux-saga';
|
||||
import { loadStatePropertyOrEmptyObject, saveState } from 'utils/localStorage';
|
||||
import RootReducer from './reducers';
|
||||
import RootReducer, { AppState } from './reducers';
|
||||
import promiseMiddleware from 'redux-promise-middleware';
|
||||
import { getNodeConfigFromId } from 'utils/node';
|
||||
import { getNetworkConfigFromId } from 'utils/network';
|
||||
|
@ -111,7 +111,6 @@ const configureStore = () => {
|
|||
// ONLY LOAD SWAP STATE FROM LOCAL STORAGE IF STEP WAS 3
|
||||
swap: swapState
|
||||
};
|
||||
|
||||
// if 'web3' has persisted as node selection, reset to app default
|
||||
// necessary because web3 is only initialized as a node upon MetaMask / Mist unlock
|
||||
if (persistedInitialState.config.nodeSelection === 'web3') {
|
||||
|
@ -127,14 +126,13 @@ const configureStore = () => {
|
|||
|
||||
store.subscribe(
|
||||
throttle(() => {
|
||||
const state = store.getState();
|
||||
const state: AppState = store.getState();
|
||||
saveState({
|
||||
config: {
|
||||
nodeSelection: state.config.nodeSelection,
|
||||
languageSelection: state.config.languageSelection,
|
||||
customNodes: state.config.customNodes,
|
||||
customNetworks: state.config.customNetworks,
|
||||
setGasLimit: state.config.setGasLimit
|
||||
customNetworks: state.config.customNetworks
|
||||
},
|
||||
transaction: {
|
||||
fields: {
|
||||
|
|
|
@ -1,20 +1,45 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`snapshot test ENS component 1`] = `
|
||||
<Connect(TabSection)
|
||||
isUnavailableOffline={true}
|
||||
>
|
||||
<section
|
||||
className="container"
|
||||
>
|
||||
<Switch>
|
||||
<Route
|
||||
exact={true}
|
||||
path="/ens"
|
||||
render={[Function]}
|
||||
/>
|
||||
<Component />
|
||||
</Switch>
|
||||
</section>
|
||||
</Connect(TabSection)>
|
||||
<ENSClass
|
||||
history={
|
||||
Object {
|
||||
"action": "PUSH",
|
||||
"block": [Function],
|
||||
"createHref": [Function],
|
||||
"go": [Function],
|
||||
"goBack": [Function],
|
||||
"goForward": [Function],
|
||||
"length": 2,
|
||||
"listen": [Function],
|
||||
"location": Object {
|
||||
"hash": "",
|
||||
"key": "e08jz7",
|
||||
"pathname": "/ens",
|
||||
"search": "",
|
||||
"state": Object {},
|
||||
},
|
||||
"push": [Function],
|
||||
"replace": [Function],
|
||||
}
|
||||
}
|
||||
location={
|
||||
Object {
|
||||
"hash": "",
|
||||
"key": "e08jz7",
|
||||
"pathname": "/ens",
|
||||
"search": "",
|
||||
"state": Object {},
|
||||
}
|
||||
}
|
||||
match={
|
||||
Object {
|
||||
"isExact": false,
|
||||
"params": Object {},
|
||||
"path": "/ens",
|
||||
"url": "/ens",
|
||||
}
|
||||
}
|
||||
resolveDomainRequested={[Function]}
|
||||
/>
|
||||
`;
|
||||
|
|
Loading…
Reference in New Issue