diff --git a/package.json b/package.json
index fc8f00cb1..9dfa53d5f 100644
--- a/package.json
+++ b/package.json
@@ -9,8 +9,8 @@
"@emotion/styled": "^10.0.9",
"@ensdomains/ens": "^0.3.3",
"@ensdomains/ens-022": "npm:@ensdomains/ens@0.2.2",
- "@ensdomains/ethregistrar": "^1.1.4",
- "@ensdomains/resolver": "^0.1.4",
+ "@ensdomains/ethregistrar": "^1.2.2",
+ "@ensdomains/resolver": "^0.1.6",
"apollo-cache-inmemory": "^1.2.9",
"apollo-client": "^2.4.5",
"apollo-link": "^1.2.2",
@@ -34,6 +34,7 @@
"react-add-to-calendar": "^0.1.5",
"react-apollo": "^2.1.2",
"react-dom": "16.7.0-alpha.0",
+ "react-ga": "^2.5.7",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-scripts": "2.0.4",
diff --git a/src/App.js b/src/App.js
index f1e5bdaed..16b627282 100644
--- a/src/App.js
+++ b/src/App.js
@@ -20,6 +20,7 @@ import { NetworkError } from './components/Error/Errors'
import { CONFIRM } from './modals'
import DefaultLayout from './components/Layout/DefaultLayout'
+import Analytics from './utils/analytics'
const HomePageLayout = ({ children }) => {children}
@@ -28,6 +29,7 @@ const Route = ({
layout: Layout = DefaultLayout,
...rest
}) => {
+ Analytics.pageview()
return (
(
<>
{({ data }) => {
+ Analytics.setup()
if (data.error && data.error.message) {
return
} else {
diff --git a/src/api/registrar.js b/src/api/registrar.js
index 8dbc9e6e2..c78d32bec 100644
--- a/src/api/registrar.js
+++ b/src/api/registrar.js
@@ -241,9 +241,26 @@ export const transferOwner = async ({ to, name }) => {
const labelHash = web3.utils.sha3(nameArray[0])
const account = await getAccount()
const { permanentRegistrarRead: Registrar } = await getPermanentRegistrar()
- return Registrar.safeTransferFrom(account, to, labelHash).send({
- from: account
- })
+ return () =>
+ Registrar.safeTransferFrom(account, to, labelHash).send({
+ from: account
+ })
+ } catch (e) {
+ console.log('error getting permanentRegistrar contract', e)
+ }
+}
+
+export const reclaim = async ({ name, address }) => {
+ try {
+ const web3 = await getWeb3()
+ const nameArray = name.split('.')
+ const labelHash = web3.utils.sha3(nameArray[0])
+ const account = await getAccount()
+ const { permanentRegistrarRead: Registrar } = await getPermanentRegistrar()
+ return () =>
+ Registrar.reclaim(labelHash, address).send({
+ from: account
+ })
} catch (e) {
console.log('error getting permanentRegistrar contract', e)
}
@@ -305,6 +322,19 @@ export const register = async (label, duration, secret) => {
.send({ from: account, gas: 1000000, value: price })
}
+export const renew = async (label, duration) => {
+ const {
+ permanentRegistrarController
+ } = await getPermanentRegistrarController()
+ const account = await getAccount()
+ const price = await getRentPrice(label, duration)
+
+ return () =>
+ permanentRegistrarController
+ .renew(label, duration)
+ .send({ from: account, gas: 1000000, value: price })
+}
+
export const createSealedBid = async (name, bidAmount, secret) => {
const Registrar = await getLegacyAuctionRegistrar()
const web3 = await getWeb3()
@@ -353,7 +383,7 @@ export const transferRegistrars = async label => {
const web3 = await getWeb3()
const hash = web3.utils.sha3(label)
const tx = ethRegistrar.transferRegistrars(hash)
- const gas = await tx.estimateGas({from:account})
+ const gas = await tx.estimateGas({ from: account })
return () =>
tx.send({
from: account,
@@ -367,7 +397,7 @@ export const releaseDeed = async label => {
const web3 = await getWeb3()
const hash = web3.utils.sha3(label)
const tx = ethRegistrar.releaseDeed(hash)
- const gas = await tx.estimateGas({from:account})
+ const gas = await tx.estimateGas({ from: account })
return () =>
tx.send({
from: account,
diff --git a/src/api/registrar/resolvers.js b/src/api/registrar/resolvers.js
index 4bf369eb9..e0379dba3 100644
--- a/src/api/registrar/resolvers.js
+++ b/src/api/registrar/resolvers.js
@@ -6,9 +6,11 @@ import {
commit,
getMinimumCommitmentAge,
register,
+ renew,
transferRegistrars,
releaseDeed,
- transferOwner
+ transferOwner,
+ reclaim
} from '../registrar'
import { getOwner } from '../registry'
import modeNames from '../modes'
@@ -46,6 +48,14 @@ const resolvers = {
return sendHelper(tx)
},
+ async reclaim(_, { name, address }) {
+ const tx = await reclaim({ name, address })
+ return sendHelper(tx)
+ },
+ async renew(_, { label, duration }) {
+ const tx = await renew(label, duration)
+ return sendHelper(tx)
+ },
async getDomainAvailability(_, { name }, { cache }) {
try {
const {
@@ -89,7 +99,7 @@ const resolvers = {
}
},
async setRegistrant(_, { name, address }) {
- const tx = transferOwner({ name, to: address })
+ const tx = await transferOwner({ name, to: address })
return sendHelper(tx)
},
async transferRegistrars(_, { label }) {
diff --git a/src/components/Modal/Modal.js b/src/components/Modal/Modal.js
index 4522e3ad6..8393a2c03 100644
--- a/src/components/Modal/Modal.js
+++ b/src/components/Modal/Modal.js
@@ -34,21 +34,25 @@ const ModalContainer = styled('div')`
top: 0;
width: 100%;
height: 100%;
- padding: 20px;
+ padding: 0;
display: flex;
justify-content: center;
align-items: center;
z-index: 99999999;
background: rgba(0, 0, 0, 0.5);
+
+ ${mq.small`
+ padding: 20px;
+ `};
`
const ModalContent = styled('div')`
background: white;
- padding: 40px;
+ padding: 20px;
overflow-y: scroll;
- height: auto%;
- min-width: 850px;
+ height: auto;
${mq.medium`
+ padding: 40px;
width: 70%;
`};
diff --git a/src/components/SingleName/AddRecord.js b/src/components/SingleName/AddRecord.js
index 327ee1123..59a003c60 100644
--- a/src/components/SingleName/AddRecord.js
+++ b/src/components/SingleName/AddRecord.js
@@ -32,7 +32,8 @@ const Select = styled(DefaultSelect)`
const RecordsTitle = styled('h3')`
/* Pointers: */
- font-family: Overpass-Bold;
+ font-family: Overpass;
+ font-weight: 700;
font-size: 12px;
color: #adbbcd;
letter-spacing: 0.5px;
diff --git a/src/components/SingleName/Confirm.js b/src/components/SingleName/Confirm.js
index ac62ce8e4..b5bd86763 100644
--- a/src/components/SingleName/Confirm.js
+++ b/src/components/SingleName/Confirm.js
@@ -2,24 +2,37 @@ import React from 'react'
import styled from '@emotion/styled'
import Button from '../Forms/Button'
import warning from '../../assets/warning.svg'
-import write from '../../assets/Write.svg'
+import write from '../../assets/Write.svg'
+
+import mq from 'mediaQuery'
const ConfirmContainer = styled('div')`
&:before {
+ display: none;
background: url(${write});
content: '';
height: 43px;
width: 42px;
float: right;
+ ${mq.large`
+ display: block;
+ flex-direction: row;
+ `}
}
`
const Content = styled('div')`
display: flex;
justify-content: space-between;
+ flex-direction: column;
`
const Title = styled('h3')`
- margin:0 0 0 1.5em;
+ margin: 0 0 0 1.5em;
+ font-size: 16px;
+ font-weight: 300;
+ ${mq.small`
+ font-size: 22px;
+ `}
&:before {
background: url(${warning});
content: '';
@@ -32,15 +45,18 @@ const Title = styled('h3')`
`
const SubTitle = styled('p')`
- margin:0;
- margin-bottom:1em;
+ margin: 0;
+ margin-bottom: 1em;
+ font-size: 12px;
+ ${mq.small`
+ font-size: 14px;
+ `}
`
const Values = styled('ul')`
list-style-type: none;
- width:30em;
padding: 4px 0;
- margin:0;
+ margin: 0;
display: flex;
flex-direction: column;
justify-content: space-between;
@@ -53,11 +69,21 @@ const Value = styled('li')`
display: flex;
justify-content: space-between;
color: ${({ old }) => (old ? 'grey' : 'black')};
+ margin-bottom: 10px;
+
+ ${mq.large`
+ justify-content: flex-start;
+ `}
+
+ span:first-child {
+ width: 75px;
+ }
`
const Buttons = styled('div')`
- height:100%;
- margin-top:1em;
+ height: 100%;
+ margin-top: 1em;
+ display: flex;
`
const Action = styled(Button)``
@@ -79,24 +105,36 @@ const Confirm = ({
Are you sure you want to do this?
This action will modify the state of the blockchain.
- {explanation ? ({explanation}
): ''}
+ {explanation ? {explanation}
: ''}
{value || newValue ? (
-
- PREVIOUS{value}
- FUTURE{newValue}
-
- ): ''}
+
+
+ PREVIOUS
+ {value}
+
+
+ FUTURE
+ {newValue}
+
+
+ ) : (
+ ''
+ )}
- Cancel
+ Cancel
{disabled ? (
- Confirm
+ Confirm
) : (
- {
- mutation()
- cancel()
- }}>Confirm
+ {
+ mutation()
+ cancel()
+ }}
+ >
+ Confirm
+
)}
diff --git a/src/components/SingleName/DetailsItemEditable.js b/src/components/SingleName/DetailsItemEditable.js
index 6d801c049..7796fbcb1 100644
--- a/src/components/SingleName/DetailsItemEditable.js
+++ b/src/components/SingleName/DetailsItemEditable.js
@@ -1,14 +1,16 @@
-import React, { Component } from 'react'
+import React, { Component, useState } from 'react'
import styled from '@emotion/styled'
import { Mutation, Query } from 'react-apollo'
import { addressUtils } from '@0xproject/utils'
import PropTypes from 'prop-types'
import { Transition } from 'react-spring'
-import Tooltip from '../Tooltip/Tooltip'
+
import { GET_PUBLIC_RESOLVER } from '../../graphql/queries'
import mq from 'mediaQuery'
-import { useEditable } from '../hooks'
+import { useEditable, useEthPrice } from '../hooks'
+import { yearInSeconds, formatDate } from 'utils/dates'
+import Tooltip from '../Tooltip/Tooltip'
import { SingleNameBlockies } from './SingleNameBlockies'
import DefaultEtherScanLink from '../ExternalLinks/EtherScanLink'
import { DetailsItem, DetailsKey, DetailsValue } from './DetailsItem'
@@ -18,6 +20,7 @@ import Button from '../Forms/Button'
import Pencil from '../Forms/Pencil'
import DefaultInfo from '../Icons/Info'
import DefaultPendingTx from '../PendingTx'
+import DefaultPricer from './Pricer'
const EtherScanLink = styled(DefaultEtherScanLink)`
display: flex;
@@ -95,6 +98,8 @@ const DefaultResolverButton = styled('a')`
}
`
+const Pricer = styled(DefaultPricer)``
+
const Buttons = styled('div')`
display: flex;
justify-content: flex-end;
@@ -105,13 +110,82 @@ function getDefaultMessage(keyName) {
switch (keyName) {
case 'Resolver':
return ['No Resolver set', 'message']
- case 'Owner':
+ case 'Controller':
return ['Not owned yet', 'message']
default:
return ['No 0x message set', 'message']
}
}
+function getInputType(
+ keyName,
+ {
+ newValue,
+ updateValue,
+ isValid,
+ isInvalid,
+ name,
+ ethUsdPriceLoading,
+ ethUsdPrice,
+ years,
+ setYears,
+ duration,
+ expirationDate
+ }
+) {
+ switch (keyName) {
+ case 'Expiration Date':
+ return (
+ {
+ setYears(years)
+ updateValue(formatDate(expirationDate))
+ }}
+ ethUsdPriceLoading={ethUsdPriceLoading}
+ ethUsdPrice={ethUsdPrice}
+ expirationDate={expirationDate}
+ />
+ )
+ default:
+ return (
+ updateValue(e.target.value)}
+ valid={isValid}
+ invalid={isInvalid}
+ placeholder="Type in a new Ethereum address"
+ large
+ />
+ )
+ }
+}
+
+function getValidation(keyName, newValue) {
+ switch (keyName) {
+ case 'Expiration Date':
+ return true
+ default:
+ return addressUtils.isAddress(newValue)
+ }
+}
+
+function getVariables(keyName, { domain, variableName, newValue, duration }) {
+ if (keyName === 'Expiration Date') {
+ return {
+ label: domain.name.split('.')[0],
+ duration
+ }
+ } else {
+ return {
+ name: domain.name,
+ [variableName ? variableName : 'address']: newValue
+ }
+ }
+}
+
const Editable = ({
keyName,
value,
@@ -136,7 +210,19 @@ const Editable = ({
setConfirmed
} = actions
- const isValid = addressUtils.isAddress(newValue)
+ //only used with Expiration date
+ let duration
+ let expirationDate
+ const [years, setYears] = useState(1)
+ const { price: ethUsdPrice, loading: ethUsdPriceLoading } = useEthPrice(
+ keyName === 'Expiration Date'
+ )
+ if (keyName === 'Expiration Date') {
+ duration = parseFloat(years) * yearInSeconds
+ expirationDate = new Date(new Date(value).getTime() + duration * 1000)
+ }
+
+ const isValid = getValidation(keyName, newValue)
const isInvalid = !isValid && newValue.length > 0
return (
@@ -182,6 +268,8 @@ const Editable = ({
) : null}
{value}
+ ) : type === 'date' ? (
+ formatDate(value)
) : (
value
)}
@@ -223,14 +311,19 @@ const Editable = ({
(props => (
- updateValue(e.target.value)}
- valid={isValid}
- invalid={isInvalid}
- placeholder="Type in a new Ethereum address"
- large
- />
+ {getInputType(keyName, {
+ newValue,
+ updateValue,
+ isValid,
+ isInvalid,
+ years,
+ name: domain.name,
+ setYears,
+ ethUsdPrice,
+ ethUsdPriceLoading,
+ duration,
+ expirationDate
+ })}
{keyName === 'Resolver' && (
@@ -254,17 +347,24 @@ const Editable = ({
{
- const variables = {
- name: domain.name,
- [variableName ? variableName : 'address']: newValue
- }
-
- mutation({
- variables
+ const variables = getVariables(keyName, {
+ domain,
+ variableName,
+ newValue,
+ duration
})
+ mutation({ variables })
}}
- value={value}
- newValue={newValue}
+ value={
+ keyName === 'Expiration Date'
+ ? formatDate(value)
+ : value
+ }
+ newValue={
+ keyName === 'Expiration Date'
+ ? formatDate(expirationDate)
+ : newValue
+ }
mutationButton={mutationButton}
confirm={confirm}
isValid={isValid}
diff --git a/src/components/SingleName/NameDetails.js b/src/components/SingleName/NameDetails.js
index a07821e99..81fa22fcd 100644
--- a/src/components/SingleName/NameDetails.js
+++ b/src/components/SingleName/NameDetails.js
@@ -17,7 +17,9 @@ import {
SET_ADDRESS,
SET_CONTENT,
SET_CONTENTHASH,
- SET_REGISTRANT
+ SET_REGISTRANT,
+ RECLAIM,
+ RENEW
} from '../../graphql/mutations'
import NameClaimTestDomain from './NameClaimTestDomain'
@@ -149,7 +151,7 @@ class NameDetails extends Component {
<>
@@ -177,7 +179,7 @@ class NameDetails extends Component {
) : (
- Expiration Date
-
- {formatDate(domain.expiryTime)}
-
-
+ domain.isNewRegistrar && isOwner ? (
+
+ ) : (
+
+ Expiration Date
+
+ {formatDate(domain.expiryTime)}
+
+
+ )
) : (
''
)}
diff --git a/src/components/SingleName/NameRegister/NameRegister.js b/src/components/SingleName/NameRegister/NameRegister.js
index b8c095ba4..1bdb1dc97 100644
--- a/src/components/SingleName/NameRegister/NameRegister.js
+++ b/src/components/SingleName/NameRegister/NameRegister.js
@@ -2,46 +2,22 @@ import React, { useState, useReducer } from 'react'
import styled from '@emotion/styled'
import { Query } from 'react-apollo'
-import mq from 'mediaQuery'
-import { GET_MINIMUM_COMMITMENT_AGE, GET_RENT_PRICE } from 'graphql/queries'
+import { GET_MINIMUM_COMMITMENT_AGE } from 'graphql/queries'
import { useInterval, useEthPrice } from 'components/hooks'
import { registerMachine, registerReducer } from './registerReducer'
import { sendNotification } from './notification'
+import { yearInSeconds } from 'utils/dates'
import Loader from 'components/Loader'
import Explainer from './Explainer'
import CTA from './CTA'
-import Years from './Years'
-import Price from './Price'
import Progress from './Progress'
-import { ReactComponent as ChainDefault } from '../../Icons/chain.svg'
-
-const PricingContainer = styled('div')`
- display: grid;
- grid-template-columns: 1fr;
- margin-bottom: 20px;
- ${mq.medium`
- grid-template-columns:
- minmax(min-content, 200px) minmax(min-content, min-content)
- minmax(200px, 1fr);
- `}
-`
+import Pricer from '../Pricer'
const NameRegisterContainer = styled('div')`
padding: 20px 40px;
`
-const Chain = styled(ChainDefault)`
- display: none;
-
- ${mq.medium`
- display: block;
- margin-top: 20px;
- margin-left: 20px;
- margin-right: 20px;
- `}
-`
-
const NameRegister = ({ domain, waitTime, refetch }) => {
const [step, dispatch] = useReducer(
registerReducer,
@@ -68,7 +44,6 @@ const NameRegister = ({ domain, waitTime, refetch }) => {
)
const parsedYears = parseFloat(years)
- const yearInSeconds = 31556952
const duration = yearInSeconds * parsedYears
const oneMonthInSeconds = 2419200
const twentyEightDaysInYears = oneMonthInSeconds / yearInSeconds
@@ -78,31 +53,14 @@ const NameRegister = ({ domain, waitTime, refetch }) => {
return (
{step === 'PRICE_DECISION' && (
-
- {({ data, loading }) => {
- return (
-
-
-
-
-
- )
- }}
-
+
)}
{
@@ -36,7 +43,7 @@ const Price = ({ price, ethUsdPrice, ethUsdPriceLoading }) => {
return (
- {ethVal.toFixed(5)} ETH
+ {ethVal.toFixed(3)} ETH
{!ethUsdPriceLoading && (
${ethVal.mul(ethUsdPrice).toFixed(2)} USD
)}
diff --git a/src/components/SingleName/NameRegister/Years.js b/src/components/SingleName/NameRegister/Years.js
index b4c836393..47f2ffd28 100644
--- a/src/components/SingleName/NameRegister/Years.js
+++ b/src/components/SingleName/NameRegister/Years.js
@@ -50,6 +50,7 @@ const Amount = styled('div')`
align-self: center;
input {
+ background: transparent;
font-family: Overpass;
font-size: 28px;
font-weight: 100;
diff --git a/src/components/SingleName/Pricer.js b/src/components/SingleName/Pricer.js
new file mode 100644
index 000000000..92f944398
--- /dev/null
+++ b/src/components/SingleName/Pricer.js
@@ -0,0 +1,67 @@
+import React from 'react'
+import styled from '@emotion/styled'
+import { Query } from 'react-apollo'
+
+import { GET_RENT_PRICE } from 'graphql/queries'
+
+import Years from './NameRegister/Years'
+import Price from './NameRegister/Price'
+
+import mq from 'mediaQuery'
+
+import { ReactComponent as ChainDefault } from '../Icons/chain.svg'
+
+const PricingContainer = styled('div')`
+ display: grid;
+ grid-template-columns: 1fr;
+ margin-bottom: 20px;
+ ${mq.medium`
+ grid-template-columns:
+ minmax(min-content, 200px) minmax(min-content, min-content)
+ minmax(200px, 1fr);
+ `}
+`
+const Chain = styled(ChainDefault)`
+ display: none;
+
+ ${mq.medium`
+ display: block;
+ margin-top: 20px;
+ margin-left: 20px;
+ margin-right: 20px;
+ `}
+`
+
+export default function Pricer({
+ years,
+ setYears,
+ duration,
+ ethUsdPriceLoading,
+ ethUsdPrice,
+ name,
+ className
+}) {
+ return (
+
+ {({ data, loading }) => {
+ return (
+
+
+
+
+
+ )
+ }}
+
+ )
+}
diff --git a/src/components/hooks.js b/src/components/hooks.js
index 563b938f4..60c929a3d 100644
--- a/src/components/hooks.js
+++ b/src/components/hooks.js
@@ -97,16 +97,18 @@ export function useInterval(callback, delay) {
}, [delay])
}
-export function useEthPrice() {
+export function useEthPrice(enabled = true) {
const [loading, setLoading] = useState(true)
const [price, setPrice] = useState(undefined)
useEffect(() => {
- getEtherPrice().then(res => {
- setPrice(res.result.ethusd)
- setLoading(false)
- })
- }, [])
+ if (enabled) {
+ getEtherPrice().then(res => {
+ setPrice(res.result.ethusd)
+ setLoading(false)
+ })
+ }
+ }, [enabled])
return {
loading,
diff --git a/src/graphql/mutations.js b/src/graphql/mutations.js
index 327233b4d..2cc7e04ef 100644
--- a/src/graphql/mutations.js
+++ b/src/graphql/mutations.js
@@ -109,15 +109,27 @@ export const SET_REGISTRANT = gql`
}
`
+export const RECLAIM = gql`
+ mutation reclaim($name: String, $address: String) {
+ reclaim(name: $name, address: $address) @client
+ }
+`
+
export const COMMIT = gql`
- mutation commit($label: String, $owner: String, $secret: String) {
- commit(label: $label, owner: $owner, secret: $secret) @client
+ mutation commit($label: String) {
+ commit(label: $label) @client
}
`
export const REGISTER = gql`
mutation register($label: String, $duration: Int) {
- register(label: $label, duration: $duration, secret: $secret) @client
+ register(label: $label, duration: $duration) @client
+ }
+`
+
+export const RENEW = gql`
+ mutation renew($label: String, $duration: Int) {
+ renew(label: $label, duration: $duration) @client
}
`
diff --git a/src/routes/SearchResults.js b/src/routes/SearchResults.js
index fe6ee9fc9..2b257d468 100644
--- a/src/routes/SearchResults.js
+++ b/src/routes/SearchResults.js
@@ -1,8 +1,6 @@
import React, { Fragment } from 'react'
import DomainInfo from '../components/SearchName/DomainInfo'
-import SubDomainResults from '../components/SubDomainResults/SubDomainResults'
import { SubDomainStateFields } from '../graphql/fragments'
-import { GET_SINGLE_NAME } from '../graphql/queries'
import { Mutation } from 'react-apollo'
import gql from 'graphql-tag'
import { parseSearchTerm } from '../utils/utils'
@@ -24,7 +22,7 @@ class Results extends React.Component {
errorType: ''
}
checkValidity = () => {
- const { searchTerm, getDomainState, getSubDomainAvailability } = this.props
+ const { searchTerm, getSubDomainAvailability } = this.props
this.setState({
errors: []
diff --git a/src/routes/SingleName.js b/src/routes/SingleName.js
index 9a9e6dd60..f74d5f219 100644
--- a/src/routes/SingleName.js
+++ b/src/routes/SingleName.js
@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react'
-import { validateName } from '../utils/utils'
+import { validateName, parseSearchTerm } from '../utils/utils'
import { GET_SINGLE_NAME } from '../graphql/queries'
import { Query } from 'react-apollo'
import Loader from '../components/Loader'
@@ -14,19 +14,30 @@ function SingleName({
location: { pathname }
}) {
const [valid, setValid] = useState(undefined)
+ const [type, setType] = useState(undefined)
const [name, setNormalisedName] = useState('')
+ let normalisedName, errorMessage, _type
useEffect(() => {
try {
// This is under the assumption that validateName never returns false
- const normalisedName = validateName(searchTerm)
- setValid(true)
+ normalisedName = validateName(searchTerm)
setNormalisedName(normalisedName)
-
document.title = searchTerm
} catch {
- setValid(false)
document.title = 'Error finding name'
+ } finally {
+ if (normalisedName) {
+ _type = parseSearchTerm(normalisedName)
+ } else {
+ _type = parseSearchTerm(searchTerm)
+ }
+ setType(_type)
+ if (_type === 'supported') {
+ setValid(true)
+ } else {
+ setValid(false)
+ }
}
}, [searchTerm])
@@ -49,7 +60,19 @@ function SingleName({
)
} else if (valid === false) {
- return
+ if (type === 'invalid') {
+ errorMessage = 'domainMalformed'
+ } else if (type === 'short') {
+ errorMessage = 'tooShort'
+ } else {
+ errorMessage = type
+ }
+ return (
+
+ )
} else {
return
}
diff --git a/src/testing-utils/deployENS.js b/src/testing-utils/deployENS.js
index b33079a57..0a19d4aa5 100644
--- a/src/testing-utils/deployENS.js
+++ b/src/testing-utils/deployENS.js
@@ -38,12 +38,11 @@ const registerName = async function(web3, account, controllerContract, name) {
.call()
await controllerContract.commit(commitment).send({ from: account })
var minCommitmentAge = await controllerContract.minCommitmentAge().call()
- await advanceTime(web3, parseInt(minCommitmentAge))
+ const time = await advanceTime(web3, parseInt(minCommitmentAge))
await mine(web3)
-
await controllerContract
.register(name, account, 28 * DAYS, secret)
- .send({ from: account, value: VALUE, gas: 5000000 })
+ .send({ from: account, value: VALUE, gas: 6000000 })
// The name should be no longer available
newnameAvailable = await controllerContract.available(name).call()
@@ -292,6 +291,8 @@ async function deployENS({ web3, accounts }) {
.register(sha3('example'), accounts[0])
.send({ from: accounts[0] })
+ console.log('registered example.test')
+
/* Setup the root reverse node */
await ensContract
.setSubnodeOwner(
@@ -303,16 +304,22 @@ async function deployENS({ web3, accounts }) {
from: accounts[0]
})
+ console.log('setup root reverse')
+
await ensContract
.setSubnodeOwner(namehash('reverse'), sha3('addr'), accounts[0])
.send({
from: accounts[0]
})
+ console.log('setup root reverse with addr label')
+
await ensContract
.setResolver(namehash('addr.reverse'), resolver._address)
.send({ from: accounts[0] })
+ console.log('setup root reverse with public resolver')
+
/* Setup the reverse subdomain: addr.reverse */
await ensContract
.setSubnodeOwner(
@@ -324,6 +331,8 @@ async function deployENS({ web3, accounts }) {
from: accounts[0]
})
+ console.log('setup root reverse with the reverse registrar')
+
/* Set the old hash registrar contract as the owner of .eth */
await ensContract
.setSubnodeOwner(
@@ -335,14 +344,18 @@ async function deployENS({ web3, accounts }) {
from: accounts[0]
})
+ console.log('Successfully setup old hash registrar')
+
const now = (await web3.eth.getBlock('latest')).timestamp
const priceOracle = await deploy(priceOracleJSON, 1)
const baseRegistrar = await deploy(
baseRegistrarJSON,
ens._address,
+ legacyAuctionRegistrar._address,
namehash('eth'),
now + 365 * DAYS
)
+ console.log('Successfully setup base registrar')
const controller = await deploy(
controllerJSON,
baseRegistrar._address,
@@ -350,6 +363,8 @@ async function deployENS({ web3, accounts }) {
2, // 10 mins in seconds
86400 // 24 hours in seconds
)
+
+ console.log('Successfully setup permanent registrar controller')
const baseRegistrarContract = baseRegistrar.methods
const controllerContract = controller.methods
@@ -406,15 +421,19 @@ async function deployENS({ web3, accounts }) {
.send({ from: accounts[0] })
console.log('Register name')
- await registerName(web3, accounts[0], controllerContract, 'newname')
- await registerName(web3, accounts[0], controllerContract, 'resolver')
- await registerName(web3, accounts[0], controllerContract, 'oldresolver')
- await registerName(web3, accounts[0], controllerContract, 'awesome')
- await registerName(web3, accounts[0], controllerContract, 'superawesome')
- await registerName(web3, accounts[0], controllerContract, 'notsoawesome')
- await registerName(web3, accounts[0], controllerContract, 'abittooawesome')
- await registerName(web3, accounts[0], controllerContract, 'subdomaindummy')
- await registerName(web3, accounts[0], controllerContract, 'subdomain')
+ try {
+ await registerName(web3, accounts[0], controllerContract, 'newname')
+ await registerName(web3, accounts[0], controllerContract, 'resolver')
+ await registerName(web3, accounts[0], controllerContract, 'oldresolver')
+ await registerName(web3, accounts[0], controllerContract, 'awesome')
+ await registerName(web3, accounts[0], controllerContract, 'superawesome')
+ await registerName(web3, accounts[0], controllerContract, 'notsoawesome')
+ await registerName(web3, accounts[0], controllerContract, 'abittooawesome')
+ await registerName(web3, accounts[0], controllerContract, 'subdomaindummy')
+ await registerName(web3, accounts[0], controllerContract, 'subdomain')
+ } catch (e) {
+ console.log('Failed to register a name', e)
+ }
await ensContract
.setResolver(namehash('notsoawesome.eth'), resolver._address)
diff --git a/src/utils/analytics.js b/src/utils/analytics.js
new file mode 100644
index 000000000..f734c899b
--- /dev/null
+++ b/src/utils/analytics.js
@@ -0,0 +1,23 @@
+import ReactGA from 'react-ga';
+const TrackingID = 'UA-138903307-1'
+
+function isProduction(){
+ return window.location.host === 'manager.ens.domains'
+}
+
+export const setup = () => {
+ if(isProduction()){
+ ReactGA.initialize(TrackingID);
+ }
+}
+
+export const pageview = () => {
+ const page = window.location.pathname + window.location.search
+ if(isProduction()){
+ ReactGA.pageview(page);
+ }
+}
+
+export default {
+ setup, pageview
+}
diff --git a/src/utils/dates.js b/src/utils/dates.js
index 2dca85245..0041580b6 100644
--- a/src/utils/dates.js
+++ b/src/utils/dates.js
@@ -2,7 +2,7 @@ import moment from 'moment'
export function formatDate(unixTimeStamp, noTime) {
let date = moment(unixTimeStamp).format('YYYY.MM.DD')
- if(!noTime){
+ if (!noTime) {
date = date + ' at ' + moment(unixTimeStamp).format('hh:mm:ss')
}
return date
@@ -25,3 +25,5 @@ export function humanizeDate(timeLeft) {
.toFixed(0)}m`
}
}
+
+export const yearInSeconds = 31556952
diff --git a/yarn.lock b/yarn.lock
index f8ac196c7..17bb52d96 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -980,23 +980,23 @@
testrpc "0.0.1"
web3-utils "^1.0.0-beta.31"
-"@ensdomains/ethregistrar@^1.1.4":
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/@ensdomains/ethregistrar/-/ethregistrar-1.1.4.tgz#d6bb288502302203729e7c97e5e357eb439d4e19"
- integrity sha512-kAprCgxb8Q+JIKtVGAug9Ma8zpE0P7KtY9YBcq2U5vJ1JR0Bgv5TKhgkVsCjfq/Hw0r5JgwFxgFqgLlTWr8uJA==
+"@ensdomains/ethregistrar@^1.2.2":
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/@ensdomains/ethregistrar/-/ethregistrar-1.2.2.tgz#4b770cad47a805cf0629d255cfcd5998d7b704e2"
+ integrity sha512-F0XH1jGqy0M1k5YNlkOz+ZUSIMxiijsRvS59WJrsQiMa2fUcSX+snB/lZBpkoZTd0xu5bwDYlKlVHmzGxmYArg==
dependencies:
"@ensdomains/ens" "^0.3.4"
bluebird "^3.5.3"
eth-ens-namehash "^2.0.8"
- openzeppelin-solidity "^2.1.3"
+ openzeppelin-solidity "2.1.3"
solium "^1.2.3"
truffle "^5.0.5"
web3-utils "^1.0.0-beta.48"
-"@ensdomains/resolver@^0.1.4":
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/@ensdomains/resolver/-/resolver-0.1.4.tgz#5933b94d1b24521d08153a09c60a939f7cc9543b"
- integrity sha512-hF4lm8tcao8WWowJEBihii53C3/wDDT2vz+1WKhAJLMKAKyBjz2DQTGuIJE0aPGzFKl8GFR6Be/OxqF6U9reGA==
+"@ensdomains/resolver@^0.1.6":
+ version "0.1.6"
+ resolved "https://registry.yarnpkg.com/@ensdomains/resolver/-/resolver-0.1.6.tgz#15e070a58d5f2be6b46787661121dddae0cfe132"
+ integrity sha512-6+xbVrajjDJJiTqtqjl18tO98YM3ceiNCOwjDzI/LZU9PadJZlKreGTgaZOQJi4bj5hyXZf3xwRU2tJOHSaPpQ==
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"
@@ -8767,7 +8767,7 @@ onetime@^2.0.0:
dependencies:
mimic-fn "^1.0.0"
-openzeppelin-solidity@^2.1.3:
+openzeppelin-solidity@2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/openzeppelin-solidity/-/openzeppelin-solidity-2.1.3.tgz#34aea40c2d05665f0dd6bc021e48516cf25b1ef6"
integrity sha512-Zz595xw6w4ZrNU3JJJTlPd8bHFbHv5OkJlkqsM2i+NBs6CzImoHI3dZ+nmhkfR+p52cGXKmayAGbnfdCum1SMg==
@@ -10112,6 +10112,11 @@ react-error-overlay@^5.0.4:
version "5.0.4"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.0.4.tgz#39cf184d770f98b65a2ee59a779d03b8d092b69e"
+react-ga@^2.5.7:
+ version "2.5.7"
+ resolved "https://registry.yarnpkg.com/react-ga/-/react-ga-2.5.7.tgz#1c80a289004bf84f84c26d46f3a6a6513081bf2e"
+ integrity sha512-UmATFaZpEQDO96KFjB5FRLcT6hFcwaxOmAJZnjrSiFN/msTqylq9G+z5Z8TYzN/dbamDTiWf92m6MnXXJkAivQ==
+
react-input-autosize@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-2.2.1.tgz#ec428fa15b1592994fb5f9aa15bb1eb6baf420f8"