mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
Make ENS named elements domain generic (#16166)
Co-authored-by: Olaf Tomalka <olaf.tomalka@gmail.com> Co-authored-by: Vincent Shadbolt <vince.shadbolt@gmail.com> Co-authored-by: Brad Decker <bhdecker84@gmail.com> Co-authored-by: Brad Decker <git@braddecker.dev>
This commit is contained in:
parent
b675a12dbf
commit
086003555c
@ -517,8 +517,8 @@ const state = {
|
|||||||
maxModeOn: false,
|
maxModeOn: false,
|
||||||
editingTransactionId: null,
|
editingTransactionId: null,
|
||||||
toNickname: 'Account 2',
|
toNickname: 'Account 2',
|
||||||
ensResolution: null,
|
domainResolution: null,
|
||||||
ensResolutionError: '',
|
domainResolutionError: '',
|
||||||
token: {
|
token: {
|
||||||
address: '0xaD6D458402F60fD3Bd25163575031ACDce07538D',
|
address: '0xaD6D458402F60fD3Bd25163575031ACDce07538D',
|
||||||
symbol: 'DAI',
|
symbol: 'DAI',
|
||||||
|
@ -30,6 +30,7 @@ import {
|
|||||||
|
|
||||||
// Local Constants
|
// Local Constants
|
||||||
const ZERO_X_ERROR_ADDRESS = '0x';
|
const ZERO_X_ERROR_ADDRESS = '0x';
|
||||||
|
const ENS = 'ENS';
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
stage: 'UNINITIALIZED',
|
stage: 'UNINITIALIZED',
|
||||||
@ -37,11 +38,13 @@ const initialState = {
|
|||||||
error: null,
|
error: null,
|
||||||
warning: null,
|
warning: null,
|
||||||
network: null,
|
network: null,
|
||||||
|
domainType: null,
|
||||||
|
domainName: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ensInitialState = initialState;
|
export const domainInitialState = initialState;
|
||||||
|
|
||||||
const name = 'ENS';
|
const name = 'DNS';
|
||||||
|
|
||||||
let web3Provider = null;
|
let web3Provider = null;
|
||||||
|
|
||||||
@ -49,51 +52,54 @@ const slice = createSlice({
|
|||||||
name,
|
name,
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
ensLookup: (state, action) => {
|
domainLookup: (state, action) => {
|
||||||
// first clear out the previous state
|
// first clear out the previous state
|
||||||
state.resolution = null;
|
state.resolution = null;
|
||||||
state.error = null;
|
state.error = null;
|
||||||
state.warning = null;
|
state.warning = null;
|
||||||
const { address, ensName, error, network } = action.payload;
|
const { address, error, network, domainType, domainName } =
|
||||||
|
action.payload;
|
||||||
if (error) {
|
state.domainType = domainType;
|
||||||
if (
|
if (state.domainType === ENS) {
|
||||||
isValidDomainName(ensName) &&
|
if (error) {
|
||||||
error.message === 'ENS name not defined.'
|
if (
|
||||||
) {
|
isValidDomainName(domainName) &&
|
||||||
state.error =
|
error.message === 'ENS name not defined.'
|
||||||
network === NETWORK_IDS.MAINNET
|
) {
|
||||||
? ENS_NO_ADDRESS_FOR_NAME
|
state.error =
|
||||||
: ENS_NOT_FOUND_ON_NETWORK;
|
network === NETWORK_IDS.MAINNET
|
||||||
} else if (error.message === 'Illegal character for ENS.') {
|
? ENS_NO_ADDRESS_FOR_NAME
|
||||||
state.error = ENS_ILLEGAL_CHARACTER;
|
: ENS_NOT_FOUND_ON_NETWORK;
|
||||||
|
} else if (error.message === 'Illegal character for ENS.') {
|
||||||
|
state.error = ENS_ILLEGAL_CHARACTER;
|
||||||
|
} else {
|
||||||
|
log.error(error);
|
||||||
|
state.error = ENS_UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
} else if (address) {
|
||||||
|
if (address === BURN_ADDRESS) {
|
||||||
|
state.error = ENS_NO_ADDRESS_FOR_NAME;
|
||||||
|
} else if (address === ZERO_X_ERROR_ADDRESS) {
|
||||||
|
state.error = ENS_REGISTRATION_ERROR;
|
||||||
|
} else {
|
||||||
|
state.resolution = address;
|
||||||
|
}
|
||||||
|
if (isValidDomainName(address) && isConfusing(address)) {
|
||||||
|
state.warning = CONFUSING_ENS_ERROR;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.error(error);
|
|
||||||
state.error = ENS_UNKNOWN_ERROR;
|
|
||||||
}
|
|
||||||
} else if (address) {
|
|
||||||
if (address === BURN_ADDRESS) {
|
|
||||||
state.error = ENS_NO_ADDRESS_FOR_NAME;
|
state.error = ENS_NO_ADDRESS_FOR_NAME;
|
||||||
} else if (address === ZERO_X_ERROR_ADDRESS) {
|
|
||||||
state.error = ENS_REGISTRATION_ERROR;
|
|
||||||
} else {
|
|
||||||
state.resolution = address;
|
|
||||||
}
|
}
|
||||||
if (isValidDomainName(address) && isConfusing(address)) {
|
|
||||||
state.warning = CONFUSING_ENS_ERROR;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.error = ENS_NO_ADDRESS_FOR_NAME;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
enableEnsLookup: (state, action) => {
|
enableDomainLookup: (state, action) => {
|
||||||
state.stage = 'INITIALIZED';
|
state.stage = 'INITIALIZED';
|
||||||
state.error = null;
|
state.error = null;
|
||||||
state.resolution = null;
|
state.resolution = null;
|
||||||
state.warning = null;
|
state.warning = null;
|
||||||
state.network = action.payload;
|
state.network = action.payload;
|
||||||
},
|
},
|
||||||
disableEnsLookup: (state) => {
|
disableDomainLookup: (state) => {
|
||||||
state.stage = 'NO_NETWORK_SUPPORT';
|
state.stage = 'NO_NETWORK_SUPPORT';
|
||||||
state.error = null;
|
state.error = null;
|
||||||
state.warning = null;
|
state.warning = null;
|
||||||
@ -105,7 +111,7 @@ const slice = createSlice({
|
|||||||
state.warning = null;
|
state.warning = null;
|
||||||
state.error = ENS_NOT_SUPPORTED_ON_NETWORK;
|
state.error = ENS_NOT_SUPPORTED_ON_NETWORK;
|
||||||
},
|
},
|
||||||
resetEnsResolution: (state) => {
|
resetDomainResolution: (state) => {
|
||||||
state.resolution = null;
|
state.resolution = null;
|
||||||
state.warning = null;
|
state.warning = null;
|
||||||
state.error = null;
|
state.error = null;
|
||||||
@ -125,15 +131,15 @@ const { reducer, actions } = slice;
|
|||||||
export default reducer;
|
export default reducer;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
disableEnsLookup,
|
disableDomainLookup,
|
||||||
ensLookup,
|
domainLookup,
|
||||||
enableEnsLookup,
|
enableDomainLookup,
|
||||||
ensNotSupported,
|
ensNotSupported,
|
||||||
resetEnsResolution,
|
resetDomainResolution,
|
||||||
} = actions;
|
} = actions;
|
||||||
export { resetEnsResolution };
|
export { resetDomainResolution };
|
||||||
|
|
||||||
export function initializeEnsSlice() {
|
export function initializeDomainSlice() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const chainId = getCurrentChainId(state);
|
const chainId = getCurrentChainId(state);
|
||||||
@ -150,69 +156,65 @@ export function initializeEnsSlice() {
|
|||||||
ensAddress,
|
ensAddress,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
dispatch(enableEnsLookup(network));
|
dispatch(enableDomainLookup(network));
|
||||||
} else {
|
} else {
|
||||||
web3Provider = null;
|
web3Provider = null;
|
||||||
dispatch(disableEnsLookup());
|
dispatch(disableDomainLookup());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function lookupEnsName(ensName) {
|
export function lookupEnsName(domainName) {
|
||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
const trimmedEnsName = ensName.trim();
|
const trimmedDomainName = domainName.trim();
|
||||||
let state = getState();
|
let state = getState();
|
||||||
if (state[name].stage === 'UNINITIALIZED') {
|
if (state[name].stage === 'UNINITIALIZED') {
|
||||||
await dispatch(initializeEnsSlice());
|
await dispatch(initializeDomainSlice());
|
||||||
}
|
}
|
||||||
state = getState();
|
state = getState();
|
||||||
if (
|
if (
|
||||||
state[name].stage === 'NO_NETWORK_SUPPORT' &&
|
state[name].stage === 'NO_NETWORK_SUPPORT' &&
|
||||||
!(
|
!(
|
||||||
isBurnAddress(trimmedEnsName) === false &&
|
isBurnAddress(trimmedDomainName) === false &&
|
||||||
isValidHexAddress(trimmedEnsName, { mixedCaseUseChecksum: true })
|
isValidHexAddress(trimmedDomainName, { mixedCaseUseChecksum: true })
|
||||||
) &&
|
) &&
|
||||||
!isHexString(trimmedEnsName)
|
!isHexString(trimmedDomainName)
|
||||||
) {
|
) {
|
||||||
await dispatch(ensNotSupported());
|
await dispatch(ensNotSupported());
|
||||||
} else {
|
} else {
|
||||||
log.info(`ENS attempting to resolve name: ${trimmedEnsName}`);
|
log.info(`ENS attempting to resolve name: ${trimmedDomainName}`);
|
||||||
let address;
|
let address;
|
||||||
let error;
|
let error;
|
||||||
try {
|
try {
|
||||||
// the writable property on the 'provider' object on the 'web3Provider' flips to false when stale
|
address = await web3Provider.resolveName(trimmedDomainName);
|
||||||
// This helps handle the case where the provider is becomes unresponsive if/when, in MV3, the service worker dies after the ENS slice is instantiated
|
|
||||||
const isProviderActive = web3Provider.provider?.writable;
|
|
||||||
if (!isProviderActive) {
|
|
||||||
await dispatch(initializeEnsSlice());
|
|
||||||
}
|
|
||||||
address = await web3Provider.resolveName(trimmedEnsName);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error = err;
|
error = err;
|
||||||
}
|
}
|
||||||
const chainId = getCurrentChainId(state);
|
const chainId = getCurrentChainId(state);
|
||||||
const network = CHAIN_ID_TO_NETWORK_ID_MAP[chainId];
|
const network = CHAIN_ID_TO_NETWORK_ID_MAP[chainId];
|
||||||
|
|
||||||
await dispatch(
|
await dispatch(
|
||||||
ensLookup({
|
domainLookup({
|
||||||
ensName: trimmedEnsName,
|
|
||||||
address,
|
address,
|
||||||
error,
|
error,
|
||||||
chainId,
|
chainId,
|
||||||
network,
|
network,
|
||||||
|
domainType: ENS,
|
||||||
|
domainName: trimmedDomainName,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEnsResolution(state) {
|
export function getDomainResolution(state) {
|
||||||
return state[name].resolution;
|
return state[name].resolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEnsError(state) {
|
export function getDomainError(state) {
|
||||||
return state[name].error;
|
return state[name].error;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEnsWarning(state) {
|
export function getDomainWarning(state) {
|
||||||
return state[name].warning;
|
return state[name].warning;
|
||||||
}
|
}
|
@ -3,7 +3,7 @@ import { ALERT_TYPES } from '../../shared/constants/alerts';
|
|||||||
import metamaskReducer from './metamask/metamask';
|
import metamaskReducer from './metamask/metamask';
|
||||||
import localeMessagesReducer from './locale/locale';
|
import localeMessagesReducer from './locale/locale';
|
||||||
import sendReducer from './send/send';
|
import sendReducer from './send/send';
|
||||||
import ensReducer from './ens';
|
import domainReducer from './domains';
|
||||||
import appStateReducer from './app/app';
|
import appStateReducer from './app/app';
|
||||||
import confirmTransactionReducer from './confirm-transaction/confirm-transaction.duck';
|
import confirmTransactionReducer from './confirm-transaction/confirm-transaction.duck';
|
||||||
import gasReducer from './gas/gas.duck';
|
import gasReducer from './gas/gas.duck';
|
||||||
@ -17,7 +17,7 @@ export default combineReducers({
|
|||||||
activeTab: (s) => (s === undefined ? null : s),
|
activeTab: (s) => (s === undefined ? null : s),
|
||||||
metamask: metamaskReducer,
|
metamask: metamaskReducer,
|
||||||
appState: appStateReducer,
|
appState: appStateReducer,
|
||||||
ENS: ensReducer,
|
DNS: domainReducer,
|
||||||
history: historyReducer,
|
history: historyReducer,
|
||||||
send: sendReducer,
|
send: sendReducer,
|
||||||
confirmTransaction: confirmTransactionReducer,
|
confirmTransaction: confirmTransactionReducer,
|
||||||
|
@ -82,7 +82,7 @@ import {
|
|||||||
getUnapprovedTxs,
|
getUnapprovedTxs,
|
||||||
} from '../metamask/metamask';
|
} from '../metamask/metamask';
|
||||||
|
|
||||||
import { resetEnsResolution } from '../ens';
|
import { resetDomainResolution } from '../domains';
|
||||||
import {
|
import {
|
||||||
isBurnAddress,
|
isBurnAddress,
|
||||||
isValidHexAddress,
|
isValidHexAddress,
|
||||||
@ -2199,7 +2199,7 @@ export function resetRecipientInput() {
|
|||||||
await dispatch(addHistoryEntry(`sendFlow - user cleared recipient input`));
|
await dispatch(addHistoryEntry(`sendFlow - user cleared recipient input`));
|
||||||
await dispatch(updateRecipientUserInput(''));
|
await dispatch(updateRecipientUserInput(''));
|
||||||
await dispatch(updateRecipient({ address: '', nickname: '' }));
|
await dispatch(updateRecipient({ address: '', nickname: '' }));
|
||||||
await dispatch(resetEnsResolution());
|
await dispatch(resetDomainResolution());
|
||||||
await dispatch(validateRecipientUserInput({ chainId }));
|
await dispatch(validateRecipientUserInput({ chainId }));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2108,7 +2108,7 @@ describe('Send Slice', () => {
|
|||||||
expect(actionResult[8].type).toStrictEqual(
|
expect(actionResult[8].type).toStrictEqual(
|
||||||
'send/computeEstimatedGasLimit/rejected',
|
'send/computeEstimatedGasLimit/rejected',
|
||||||
);
|
);
|
||||||
expect(actionResult[9].type).toStrictEqual('ENS/resetEnsResolution');
|
expect(actionResult[9].type).toStrictEqual('DNS/resetDomainResolution');
|
||||||
expect(actionResult[10].type).toStrictEqual(
|
expect(actionResult[10].type).toStrictEqual(
|
||||||
'send/validateRecipientUserInput',
|
'send/validateRecipientUserInput',
|
||||||
);
|
);
|
||||||
|
@ -16,9 +16,9 @@ export default class AddRecipient extends Component {
|
|||||||
ownedAccounts: PropTypes.array,
|
ownedAccounts: PropTypes.array,
|
||||||
addressBook: PropTypes.array,
|
addressBook: PropTypes.array,
|
||||||
updateRecipient: PropTypes.func,
|
updateRecipient: PropTypes.func,
|
||||||
ensResolution: PropTypes.string,
|
domainResolution: PropTypes.string,
|
||||||
ensError: PropTypes.string,
|
domainError: PropTypes.string,
|
||||||
ensWarning: PropTypes.string,
|
domainWarning: PropTypes.string,
|
||||||
addressBookEntryName: PropTypes.string,
|
addressBookEntryName: PropTypes.string,
|
||||||
contacts: PropTypes.array,
|
contacts: PropTypes.array,
|
||||||
nonContacts: PropTypes.array,
|
nonContacts: PropTypes.array,
|
||||||
@ -102,7 +102,7 @@ export default class AddRecipient extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
ensResolution,
|
domainResolution,
|
||||||
recipient,
|
recipient,
|
||||||
userInput,
|
userInput,
|
||||||
addressBookEntryName,
|
addressBookEntryName,
|
||||||
@ -117,9 +117,9 @@ export default class AddRecipient extends Component {
|
|||||||
recipient.nickname,
|
recipient.nickname,
|
||||||
'validated user input',
|
'validated user input',
|
||||||
);
|
);
|
||||||
} else if (ensResolution && !recipient.error) {
|
} else if (domainResolution && !recipient.error) {
|
||||||
content = this.renderExplicitAddress(
|
content = this.renderExplicitAddress(
|
||||||
ensResolution,
|
domainResolution,
|
||||||
addressBookEntryName || userInput,
|
addressBookEntryName || userInput,
|
||||||
'ENS resolution',
|
'ENS resolution',
|
||||||
);
|
);
|
||||||
@ -233,19 +233,19 @@ export default class AddRecipient extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderDialogs() {
|
renderDialogs() {
|
||||||
const { ensError, recipient, ensWarning } = this.props;
|
const { domainError, recipient, domainWarning } = this.props;
|
||||||
const { t } = this.context;
|
const { t } = this.context;
|
||||||
|
|
||||||
if (ensError || (recipient.error && recipient.error !== 'required')) {
|
if (domainError || (recipient.error && recipient.error !== 'required')) {
|
||||||
return (
|
return (
|
||||||
<Dialog type="error" className="send__error-dialog">
|
<Dialog type="error" className="send__error-dialog">
|
||||||
{t(ensError ?? recipient.error)}
|
{t(domainError ?? recipient.error)}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
} else if (ensWarning || recipient.warning) {
|
} else if (domainWarning || recipient.warning) {
|
||||||
return (
|
return (
|
||||||
<Dialog type="warning" className="send__error-dialog">
|
<Dialog type="warning" className="send__error-dialog">
|
||||||
{t(ensWarning ?? recipient.warning)}
|
{t(domainWarning ?? recipient.warning)}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ describe('AddRecipient Component', () => {
|
|||||||
it('should render error when query has no results', () => {
|
it('should render error when query has no results', () => {
|
||||||
wrapper.setProps({
|
wrapper.setProps({
|
||||||
addressBook: [],
|
addressBook: [],
|
||||||
ensError: 'bad',
|
domainError: 'bad',
|
||||||
contacts: [],
|
contacts: [],
|
||||||
nonContacts: [],
|
nonContacts: [],
|
||||||
});
|
});
|
||||||
@ -151,7 +151,7 @@ describe('AddRecipient Component', () => {
|
|||||||
it('should render error when query has ens does not resolve', () => {
|
it('should render error when query has ens does not resolve', () => {
|
||||||
wrapper.setProps({
|
wrapper.setProps({
|
||||||
addressBook: [],
|
addressBook: [],
|
||||||
ensError: 'very bad',
|
domainError: 'very bad',
|
||||||
contacts: [],
|
contacts: [],
|
||||||
nonContacts: [],
|
nonContacts: [],
|
||||||
});
|
});
|
||||||
@ -166,8 +166,8 @@ describe('AddRecipient Component', () => {
|
|||||||
it('should render error when ens resolved but ens error exists', () => {
|
it('should render error when ens resolved but ens error exists', () => {
|
||||||
wrapper.setProps({
|
wrapper.setProps({
|
||||||
addressBook: [],
|
addressBook: [],
|
||||||
ensError: 'bad',
|
domainError: 'bad',
|
||||||
ensResolution: '0x128',
|
domainResolution: '0x128',
|
||||||
});
|
});
|
||||||
|
|
||||||
const dialog = wrapper.find(Dialog);
|
const dialog = wrapper.find(Dialog);
|
||||||
|
@ -16,20 +16,20 @@ import {
|
|||||||
addHistoryEntry,
|
addHistoryEntry,
|
||||||
} from '../../../../ducks/send';
|
} from '../../../../ducks/send';
|
||||||
import {
|
import {
|
||||||
getEnsResolution,
|
getDomainResolution,
|
||||||
getEnsError,
|
getDomainError,
|
||||||
getEnsWarning,
|
getDomainWarning,
|
||||||
} from '../../../../ducks/ens';
|
} from '../../../../ducks/domains';
|
||||||
import AddRecipient from './add-recipient.component';
|
import AddRecipient from './add-recipient.component';
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(AddRecipient);
|
export default connect(mapStateToProps, mapDispatchToProps)(AddRecipient);
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStateToProps(state) {
|
||||||
const ensResolution = getEnsResolution(state);
|
const domainResolution = getDomainResolution(state);
|
||||||
|
|
||||||
let addressBookEntryName = '';
|
let addressBookEntryName = '';
|
||||||
if (ensResolution) {
|
if (domainResolution) {
|
||||||
const addressBookEntry = getAddressBookEntry(state, ensResolution) || {};
|
const addressBookEntry = getAddressBookEntry(state, domainResolution) || {};
|
||||||
addressBookEntryName = addressBookEntry.name;
|
addressBookEntryName = addressBookEntry.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,9 +41,9 @@ function mapStateToProps(state) {
|
|||||||
addressBook,
|
addressBook,
|
||||||
addressBookEntryName,
|
addressBookEntryName,
|
||||||
contacts: addressBook.filter(({ name }) => Boolean(name)),
|
contacts: addressBook.filter(({ name }) => Boolean(name)),
|
||||||
ensResolution,
|
domainResolution,
|
||||||
ensError: getEnsError(state),
|
domainError: getDomainError(state),
|
||||||
ensWarning: getEnsWarning(state),
|
domainWarning: getDomainWarning(state),
|
||||||
nonContacts: addressBook.filter(({ name }) => !name),
|
nonContacts: addressBook.filter(({ name }) => !name),
|
||||||
ownedAccounts,
|
ownedAccounts,
|
||||||
isUsingMyAccountsForRecipientSearch:
|
isUsingMyAccountsForRecipientSearch:
|
||||||
|
@ -18,10 +18,10 @@ jest.mock('../../../../selectors', () => ({
|
|||||||
],
|
],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('../../../../ducks/ens', () => ({
|
jest.mock('../../../../ducks/domains', () => ({
|
||||||
getEnsResolution: (s) => `mockSendEnsResolution:${s}`,
|
getDomainResolution: (s) => `mockSendDomainResolution:${s}`,
|
||||||
getEnsError: (s) => `mockSendEnsResolutionError:${s}`,
|
getDomainError: (s) => `mockSendDomainResolutionError:${s}`,
|
||||||
getEnsWarning: (s) => `mockSendEnsResolutionWarning:${s}`,
|
getDomainWarning: (s) => `mockSendDomainResolutionWarning:${s}`,
|
||||||
useMyAccountsForRecipientSearch: (s) =>
|
useMyAccountsForRecipientSearch: (s) =>
|
||||||
`useMyAccountsForRecipientSearch:${s}`,
|
`useMyAccountsForRecipientSearch:${s}`,
|
||||||
}));
|
}));
|
||||||
@ -49,9 +49,9 @@ describe('add-recipient container', () => {
|
|||||||
addressBook: [{ name: 'mockAddressBook:mockState' }],
|
addressBook: [{ name: 'mockAddressBook:mockState' }],
|
||||||
addressBookEntryName: undefined,
|
addressBookEntryName: undefined,
|
||||||
contacts: [{ name: 'mockAddressBook:mockState' }],
|
contacts: [{ name: 'mockAddressBook:mockState' }],
|
||||||
ensResolution: 'mockSendEnsResolution:mockState',
|
domainResolution: 'mockSendDomainResolution:mockState',
|
||||||
ensError: 'mockSendEnsResolutionError:mockState',
|
domainError: 'mockSendDomainResolutionError:mockState',
|
||||||
ensWarning: 'mockSendEnsResolutionWarning:mockState',
|
domainWarning: 'mockSendDomainResolutionWarning:mockState',
|
||||||
nonContacts: [],
|
nonContacts: [],
|
||||||
ownedAccounts: [
|
ownedAccounts: [
|
||||||
{ name: 'account1:mockState' },
|
{ name: 'account1:mockState' },
|
||||||
|
@ -29,13 +29,13 @@ export default {
|
|||||||
updateRecipient: {
|
updateRecipient: {
|
||||||
action: 'updateRecipient',
|
action: 'updateRecipient',
|
||||||
},
|
},
|
||||||
ensResolution: {
|
domainResolution: {
|
||||||
control: 'text',
|
control: 'text',
|
||||||
},
|
},
|
||||||
ensError: {
|
domainError: {
|
||||||
control: 'text',
|
control: 'text',
|
||||||
},
|
},
|
||||||
ensWarning: {
|
domainWarning: {
|
||||||
control: 'text',
|
control: 'text',
|
||||||
},
|
},
|
||||||
addressBookEntryName: {
|
addressBookEntryName: {
|
||||||
@ -96,8 +96,8 @@ export const ErrorStory = (args) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ErrorStory.argTypes = {
|
ErrorStory.argTypes = {
|
||||||
// ensError must be the key for a translation
|
// domainError must be the key for a translation
|
||||||
ensError: { type: 'text', defaultValue: 'loading' },
|
domainError: { type: 'text', defaultValue: 'loading' },
|
||||||
};
|
};
|
||||||
|
|
||||||
ErrorStory.storyName = 'Error';
|
ErrorStory.storyName = 'Error';
|
||||||
@ -115,8 +115,8 @@ export const WarningStory = (args) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
WarningStory.argTypes = {
|
WarningStory.argTypes = {
|
||||||
// ensWarning must be the key for a translation
|
// domainWarning must be the key for a translation
|
||||||
ensWarning: { type: 'text', defaultValue: 'loading' },
|
domainWarning: { type: 'text', defaultValue: 'loading' },
|
||||||
};
|
};
|
||||||
|
|
||||||
WarningStory.storyName = 'Warning';
|
WarningStory.storyName = 'Warning';
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
isValidHexAddress,
|
isValidHexAddress,
|
||||||
} from '../../../../../shared/modules/hexstring-utils';
|
} from '../../../../../shared/modules/hexstring-utils';
|
||||||
|
|
||||||
export default class EnsInput extends Component {
|
export default class DomainInput extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
metricsEvent: PropTypes.func,
|
metricsEvent: PropTypes.func,
|
||||||
@ -27,12 +27,12 @@ export default class EnsInput extends Component {
|
|||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
onReset: PropTypes.func.isRequired,
|
onReset: PropTypes.func.isRequired,
|
||||||
lookupEnsName: PropTypes.func.isRequired,
|
lookupEnsName: PropTypes.func.isRequired,
|
||||||
initializeEnsSlice: PropTypes.func.isRequired,
|
initializeDomainSlice: PropTypes.func.isRequired,
|
||||||
resetEnsResolution: PropTypes.func.isRequired,
|
resetDomainResolution: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.initializeEnsSlice();
|
this.props.initializeDomainSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
onPaste = (event) => {
|
onPaste = (event) => {
|
||||||
@ -56,7 +56,7 @@ export default class EnsInput extends Component {
|
|||||||
internalSearch,
|
internalSearch,
|
||||||
onChange,
|
onChange,
|
||||||
lookupEnsName,
|
lookupEnsName,
|
||||||
resetEnsResolution,
|
resetDomainResolution,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const input = value.trim();
|
const input = value.trim();
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ export default class EnsInput extends Component {
|
|||||||
if (isValidDomainName(input)) {
|
if (isValidDomainName(input)) {
|
||||||
lookupEnsName(input);
|
lookupEnsName(input);
|
||||||
} else {
|
} else {
|
||||||
resetEnsResolution();
|
resetDomainResolution();
|
||||||
if (
|
if (
|
||||||
onValidAddressTyped &&
|
onValidAddressTyped &&
|
||||||
!isBurnAddress(input) &&
|
!isBurnAddress(input) &&
|
@ -0,0 +1,24 @@
|
|||||||
|
import { debounce } from 'lodash';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
lookupEnsName,
|
||||||
|
initializeDomainSlice,
|
||||||
|
resetDomainResolution,
|
||||||
|
} from '../../../../ducks/domains';
|
||||||
|
import DomainInput from './domain-input.component';
|
||||||
|
|
||||||
|
function mapDispatchToProps(dispatch) {
|
||||||
|
return {
|
||||||
|
lookupEnsName: debounce(
|
||||||
|
(domainName) => dispatch(lookupEnsName(domainName)),
|
||||||
|
150,
|
||||||
|
),
|
||||||
|
initializeDomainSlice: () => dispatch(initializeDomainSlice()),
|
||||||
|
resetDomainResolution: debounce(
|
||||||
|
() => dispatch(resetDomainResolution()),
|
||||||
|
300,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(null, mapDispatchToProps)(DomainInput);
|
1
ui/pages/send/send-content/add-recipient/domain-input.js
Normal file
1
ui/pages/send/send-content/add-recipient/domain-input.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './domain-input.container';
|
@ -1,18 +0,0 @@
|
|||||||
import { debounce } from 'lodash';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import {
|
|
||||||
lookupEnsName,
|
|
||||||
initializeEnsSlice,
|
|
||||||
resetEnsResolution,
|
|
||||||
} from '../../../../ducks/ens';
|
|
||||||
import EnsInput from './ens-input.component';
|
|
||||||
|
|
||||||
function mapDispatchToProps(dispatch) {
|
|
||||||
return {
|
|
||||||
lookupEnsName: debounce((ensName) => dispatch(lookupEnsName(ensName)), 150),
|
|
||||||
initializeEnsSlice: () => dispatch(initializeEnsSlice()),
|
|
||||||
resetEnsResolution: debounce(() => dispatch(resetEnsResolution()), 300),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(EnsInput);
|
|
@ -1 +0,0 @@
|
|||||||
export { default } from './ens-input.container';
|
|
@ -25,7 +25,7 @@ import SendHeader from './send-header';
|
|||||||
import AddRecipient from './send-content/add-recipient';
|
import AddRecipient from './send-content/add-recipient';
|
||||||
import SendContent from './send-content';
|
import SendContent from './send-content';
|
||||||
import SendFooter from './send-footer';
|
import SendFooter from './send-footer';
|
||||||
import EnsInput from './send-content/add-recipient/ens-input';
|
import DomainInput from './send-content/add-recipient/domain-input';
|
||||||
|
|
||||||
const sendSliceIsCustomPriceExcessive = (state) =>
|
const sendSliceIsCustomPriceExcessive = (state) =>
|
||||||
isCustomPriceExcessive(state, true);
|
isCustomPriceExcessive(state, true);
|
||||||
@ -112,7 +112,7 @@ export default function SendTransactionScreen() {
|
|||||||
return (
|
return (
|
||||||
<div className="page-container">
|
<div className="page-container">
|
||||||
<SendHeader history={history} />
|
<SendHeader history={history} />
|
||||||
<EnsInput
|
<DomainInput
|
||||||
userInput={userInput}
|
userInput={userInput}
|
||||||
className="send__to-row"
|
className="send__to-row"
|
||||||
onChange={(address) => dispatch(updateRecipientUserInput(address))}
|
onChange={(address) => dispatch(updateRecipientUserInput(address))}
|
||||||
|
@ -3,7 +3,7 @@ import configureMockStore from 'redux-mock-store';
|
|||||||
import thunk from 'redux-thunk';
|
import thunk from 'redux-thunk';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { SEND_STAGES, startNewDraftTransaction } from '../../ducks/send';
|
import { SEND_STAGES, startNewDraftTransaction } from '../../ducks/send';
|
||||||
import { ensInitialState } from '../../ducks/ens';
|
import { domainInitialState } from '../../ducks/domains';
|
||||||
import { renderWithProvider } from '../../../test/jest';
|
import { renderWithProvider } from '../../../test/jest';
|
||||||
import { CHAIN_IDS } from '../../../shared/constants/network';
|
import { CHAIN_IDS } from '../../../shared/constants/network';
|
||||||
import { GAS_ESTIMATE_TYPES } from '../../../shared/constants/gas';
|
import { GAS_ESTIMATE_TYPES } from '../../../shared/constants/gas';
|
||||||
@ -54,7 +54,7 @@ jest.mock('ethers', () => {
|
|||||||
});
|
});
|
||||||
const baseStore = {
|
const baseStore = {
|
||||||
send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
||||||
ENS: ensInitialState,
|
DNS: domainInitialState,
|
||||||
gas: {
|
gas: {
|
||||||
customData: { limit: null, price: null },
|
customData: { limit: null, price: null },
|
||||||
},
|
},
|
||||||
@ -132,7 +132,7 @@ describe('Send Page', () => {
|
|||||||
expect(actions).toStrictEqual(
|
expect(actions).toStrictEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: 'ENS/enableEnsLookup',
|
type: 'DNS/enableDomainLookup',
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
@ -146,7 +146,7 @@ describe('Send Page', () => {
|
|||||||
expect(actions).toStrictEqual(
|
expect(actions).toStrictEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: 'ENS/enableEnsLookup',
|
type: 'DNS/enableDomainLookup',
|
||||||
}),
|
}),
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: 'UI_MODAL_OPEN',
|
type: 'UI_MODAL_OPEN',
|
||||||
@ -165,7 +165,7 @@ describe('Send Page', () => {
|
|||||||
expect(getByText('Send to')).toBeTruthy();
|
expect(getByText('Send to')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render the EnsInput field', () => {
|
it('should render the DomainInput field', () => {
|
||||||
const store = configureMockStore(middleware)(baseStore);
|
const store = configureMockStore(middleware)(baseStore);
|
||||||
const { getByPlaceholderText } = renderWithProvider(<Send />, store);
|
const { getByPlaceholderText } = renderWithProvider(<Send />, store);
|
||||||
expect(
|
expect(
|
||||||
@ -209,7 +209,7 @@ describe('Send Page', () => {
|
|||||||
expect(getByText('Send')).toBeTruthy();
|
expect(getByText('Send')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render the EnsInput field', () => {
|
it('should render the DomainInput field', () => {
|
||||||
const store = configureMockStore(middleware)(baseStore);
|
const store = configureMockStore(middleware)(baseStore);
|
||||||
const { getByPlaceholderText } = renderWithProvider(<Send />, store);
|
const { getByPlaceholderText } = renderWithProvider(<Send />, store);
|
||||||
expect(
|
expect(
|
||||||
|
@ -5,7 +5,7 @@ import Identicon from '../../../../components/ui/identicon';
|
|||||||
import TextField from '../../../../components/ui/text-field';
|
import TextField from '../../../../components/ui/text-field';
|
||||||
import { CONTACT_LIST_ROUTE } from '../../../../helpers/constants/routes';
|
import { CONTACT_LIST_ROUTE } from '../../../../helpers/constants/routes';
|
||||||
import { isValidDomainName } from '../../../../helpers/utils/util';
|
import { isValidDomainName } from '../../../../helpers/utils/util';
|
||||||
import EnsInput from '../../../send/send-content/add-recipient/ens-input';
|
import DomainInput from '../../../send/send-content/add-recipient/domain-input';
|
||||||
import PageContainerFooter from '../../../../components/ui/page-container/page-container-footer';
|
import PageContainerFooter from '../../../../components/ui/page-container/page-container-footer';
|
||||||
import {
|
import {
|
||||||
isBurnAddress,
|
isBurnAddress,
|
||||||
@ -25,9 +25,9 @@ export default class AddContact extends PureComponent {
|
|||||||
qrCodeData:
|
qrCodeData:
|
||||||
PropTypes.object /* eslint-disable-line react/no-unused-prop-types */,
|
PropTypes.object /* eslint-disable-line react/no-unused-prop-types */,
|
||||||
qrCodeDetected: PropTypes.func,
|
qrCodeDetected: PropTypes.func,
|
||||||
ensResolution: PropTypes.string,
|
domainResolution: PropTypes.string,
|
||||||
ensError: PropTypes.string,
|
domainError: PropTypes.string,
|
||||||
resetEnsResolution: PropTypes.func,
|
resetDomainResolution: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -45,10 +45,10 @@ export default class AddContact extends PureComponent {
|
|||||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||||
if (nextProps.qrCodeData) {
|
if (nextProps.qrCodeData) {
|
||||||
if (nextProps.qrCodeData.type === 'address') {
|
if (nextProps.qrCodeData.type === 'address') {
|
||||||
const { ensResolution } = this.props;
|
const { domainResolution } = this.props;
|
||||||
const scannedAddress =
|
const scannedAddress =
|
||||||
nextProps.qrCodeData.values.address.toLowerCase();
|
nextProps.qrCodeData.values.address.toLowerCase();
|
||||||
const currentAddress = ensResolution || this.state.ethAddress;
|
const currentAddress = domainResolution || this.state.ethAddress;
|
||||||
if (currentAddress.toLowerCase() !== scannedAddress) {
|
if (currentAddress.toLowerCase() !== scannedAddress) {
|
||||||
this.setState({ input: scannedAddress });
|
this.setState({ input: scannedAddress });
|
||||||
this.validate(scannedAddress);
|
this.validate(scannedAddress);
|
||||||
@ -79,7 +79,7 @@ export default class AddContact extends PureComponent {
|
|||||||
|
|
||||||
renderInput() {
|
renderInput() {
|
||||||
return (
|
return (
|
||||||
<EnsInput
|
<DomainInput
|
||||||
scanQrCode={(_) => {
|
scanQrCode={(_) => {
|
||||||
this.props.scanQrCode();
|
this.props.scanQrCode();
|
||||||
}}
|
}}
|
||||||
@ -89,7 +89,7 @@ export default class AddContact extends PureComponent {
|
|||||||
this.validate(text);
|
this.validate(text);
|
||||||
}}
|
}}
|
||||||
onReset={() => {
|
onReset={() => {
|
||||||
this.props.resetEnsResolution();
|
this.props.resetDomainResolution();
|
||||||
this.setState({ ethAddress: '', input: '' });
|
this.setState({ ethAddress: '', input: '' });
|
||||||
}}
|
}}
|
||||||
userInput={this.state.input}
|
userInput={this.state.input}
|
||||||
@ -99,17 +99,18 @@ export default class AddContact extends PureComponent {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { t } = this.context;
|
const { t } = this.context;
|
||||||
const { history, addToAddressBook, ensError, ensResolution } = this.props;
|
const { history, addToAddressBook, domainError, domainResolution } =
|
||||||
|
this.props;
|
||||||
|
|
||||||
const errorToRender = ensError || this.state.error;
|
const errorToRender = domainError || this.state.error;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-page__content-row address-book__add-contact">
|
<div className="settings-page__content-row address-book__add-contact">
|
||||||
{ensResolution && (
|
{domainResolution && (
|
||||||
<div className="address-book__view-contact__group">
|
<div className="address-book__view-contact__group">
|
||||||
<Identicon address={ensResolution} diameter={60} />
|
<Identicon address={domainResolution} diameter={60} />
|
||||||
<div className="address-book__view-contact__group__value">
|
<div className="address-book__view-contact__group__value">
|
||||||
{ensResolution}
|
{domainResolution}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -145,7 +146,7 @@ export default class AddContact extends PureComponent {
|
|||||||
disabled={Boolean(this.state.error)}
|
disabled={Boolean(this.state.error)}
|
||||||
onSubmit={async () => {
|
onSubmit={async () => {
|
||||||
await addToAddressBook(
|
await addToAddressBook(
|
||||||
ensResolution || this.state.ethAddress,
|
domainResolution || this.state.ethAddress,
|
||||||
this.state.newName,
|
this.state.newName,
|
||||||
);
|
);
|
||||||
history.push(CONTACT_LIST_ROUTE);
|
history.push(CONTACT_LIST_ROUTE);
|
||||||
|
@ -8,17 +8,17 @@ import {
|
|||||||
} from '../../../../store/actions';
|
} from '../../../../store/actions';
|
||||||
import { getQrCodeData } from '../../../../ducks/app/app';
|
import { getQrCodeData } from '../../../../ducks/app/app';
|
||||||
import {
|
import {
|
||||||
getEnsError,
|
getDomainError,
|
||||||
getEnsResolution,
|
getDomainResolution,
|
||||||
resetEnsResolution,
|
resetDomainResolution,
|
||||||
} from '../../../../ducks/ens';
|
} from '../../../../ducks/domains';
|
||||||
import AddContact from './add-contact.component';
|
import AddContact from './add-contact.component';
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = (state) => {
|
||||||
return {
|
return {
|
||||||
qrCodeData: getQrCodeData(state),
|
qrCodeData: getQrCodeData(state),
|
||||||
ensError: getEnsError(state),
|
domainError: getDomainError(state),
|
||||||
ensResolution: getEnsResolution(state),
|
domainResolution: getDomainResolution(state),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ const mapDispatchToProps = (dispatch) => {
|
|||||||
dispatch(addToAddressBook(recipient, nickname)),
|
dispatch(addToAddressBook(recipient, nickname)),
|
||||||
scanQrCode: () => dispatch(showQrScanner()),
|
scanQrCode: () => dispatch(showQrScanner()),
|
||||||
qrCodeDetected: (data) => dispatch(qrCodeDetected(data)),
|
qrCodeDetected: (data) => dispatch(qrCodeDetected(data)),
|
||||||
resetEnsResolution: () => dispatch(resetEnsResolution()),
|
resetDomainResolution: () => dispatch(resetDomainResolution()),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user