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>
feature/default_network_editable
Ben Behrman 2 years ago committed by GitHub
parent b675a12dbf
commit 086003555c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .storybook/test-data.js
  2. 74
      ui/ducks/domains.js
  3. 4
      ui/ducks/index.js
  4. 4
      ui/ducks/send/send.js
  5. 2
      ui/ducks/send/send.test.js
  6. 22
      ui/pages/send/send-content/add-recipient/add-recipient.component.js
  7. 8
      ui/pages/send/send-content/add-recipient/add-recipient.component.test.js
  8. 20
      ui/pages/send/send-content/add-recipient/add-recipient.container.js
  9. 14
      ui/pages/send/send-content/add-recipient/add-recipient.container.test.js
  10. 14
      ui/pages/send/send-content/add-recipient/add-recipient.stories.js
  11. 12
      ui/pages/send/send-content/add-recipient/domain-input.component.js
  12. 24
      ui/pages/send/send-content/add-recipient/domain-input.container.js
  13. 1
      ui/pages/send/send-content/add-recipient/domain-input.js
  14. 18
      ui/pages/send/send-content/add-recipient/ens-input.container.js
  15. 1
      ui/pages/send/send-content/add-recipient/ens-input.js
  16. 4
      ui/pages/send/send.js
  17. 12
      ui/pages/send/send.test.js
  18. 29
      ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js
  19. 14
      ui/pages/settings/contact-list-tab/add-contact/add-contact.container.js

@ -517,8 +517,8 @@ const state = {
maxModeOn: false,
editingTransactionId: null,
toNickname: 'Account 2',
ensResolution: null,
ensResolutionError: '',
domainResolution: null,
domainResolutionError: '',
token: {
address: '0xaD6D458402F60fD3Bd25163575031ACDce07538D',
symbol: 'DAI',

@ -30,6 +30,7 @@ import {
// Local Constants
const ZERO_X_ERROR_ADDRESS = '0x';
const ENS = 'ENS';
const initialState = {
stage: 'UNINITIALIZED',
@ -37,11 +38,13 @@ const initialState = {
error: null,
warning: null,
network: null,
domainType: null,
domainName: null,
};
export const ensInitialState = initialState;
export const domainInitialState = initialState;
const name = 'ENS';
const name = 'DNS';
let web3Provider = null;
@ -49,16 +52,18 @@ const slice = createSlice({
name,
initialState,
reducers: {
ensLookup: (state, action) => {
domainLookup: (state, action) => {
// first clear out the previous state
state.resolution = null;
state.error = null;
state.warning = null;
const { address, ensName, error, network } = action.payload;
const { address, error, network, domainType, domainName } =
action.payload;
state.domainType = domainType;
if (state.domainType === ENS) {
if (error) {
if (
isValidDomainName(ensName) &&
isValidDomainName(domainName) &&
error.message === 'ENS name not defined.'
) {
state.error =
@ -85,15 +90,16 @@ const slice = createSlice({
} else {
state.error = ENS_NO_ADDRESS_FOR_NAME;
}
}
},
enableEnsLookup: (state, action) => {
enableDomainLookup: (state, action) => {
state.stage = 'INITIALIZED';
state.error = null;
state.resolution = null;
state.warning = null;
state.network = action.payload;
},
disableEnsLookup: (state) => {
disableDomainLookup: (state) => {
state.stage = 'NO_NETWORK_SUPPORT';
state.error = null;
state.warning = null;
@ -105,7 +111,7 @@ const slice = createSlice({
state.warning = null;
state.error = ENS_NOT_SUPPORTED_ON_NETWORK;
},
resetEnsResolution: (state) => {
resetDomainResolution: (state) => {
state.resolution = null;
state.warning = null;
state.error = null;
@ -125,15 +131,15 @@ const { reducer, actions } = slice;
export default reducer;
const {
disableEnsLookup,
ensLookup,
enableEnsLookup,
disableDomainLookup,
domainLookup,
enableDomainLookup,
ensNotSupported,
resetEnsResolution,
resetDomainResolution,
} = actions;
export { resetEnsResolution };
export { resetDomainResolution };
export function initializeEnsSlice() {
export function initializeDomainSlice() {
return (dispatch, getState) => {
const state = getState();
const chainId = getCurrentChainId(state);
@ -150,69 +156,65 @@ export function initializeEnsSlice() {
ensAddress,
},
);
dispatch(enableEnsLookup(network));
dispatch(enableDomainLookup(network));
} else {
web3Provider = null;
dispatch(disableEnsLookup());
dispatch(disableDomainLookup());
}
};
}
export function lookupEnsName(ensName) {
export function lookupEnsName(domainName) {
return async (dispatch, getState) => {
const trimmedEnsName = ensName.trim();
const trimmedDomainName = domainName.trim();
let state = getState();
if (state[name].stage === 'UNINITIALIZED') {
await dispatch(initializeEnsSlice());
await dispatch(initializeDomainSlice());
}
state = getState();
if (
state[name].stage === 'NO_NETWORK_SUPPORT' &&
!(
isBurnAddress(trimmedEnsName) === false &&
isValidHexAddress(trimmedEnsName, { mixedCaseUseChecksum: true })
isBurnAddress(trimmedDomainName) === false &&
isValidHexAddress(trimmedDomainName, { mixedCaseUseChecksum: true })
) &&
!isHexString(trimmedEnsName)
!isHexString(trimmedDomainName)
) {
await dispatch(ensNotSupported());
} else {
log.info(`ENS attempting to resolve name: ${trimmedEnsName}`);
log.info(`ENS attempting to resolve name: ${trimmedDomainName}`);
let address;
let error;
try {
// the writable property on the 'provider' object on the 'web3Provider' flips to false when stale
// 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);
address = await web3Provider.resolveName(trimmedDomainName);
} catch (err) {
error = err;
}
const chainId = getCurrentChainId(state);
const network = CHAIN_ID_TO_NETWORK_ID_MAP[chainId];
await dispatch(
ensLookup({
ensName: trimmedEnsName,
domainLookup({
address,
error,
chainId,
network,
domainType: ENS,
domainName: trimmedDomainName,
}),
);
}
};
}
export function getEnsResolution(state) {
export function getDomainResolution(state) {
return state[name].resolution;
}
export function getEnsError(state) {
export function getDomainError(state) {
return state[name].error;
}
export function getEnsWarning(state) {
export function getDomainWarning(state) {
return state[name].warning;
}

@ -3,7 +3,7 @@ import { ALERT_TYPES } from '../../shared/constants/alerts';
import metamaskReducer from './metamask/metamask';
import localeMessagesReducer from './locale/locale';
import sendReducer from './send/send';
import ensReducer from './ens';
import domainReducer from './domains';
import appStateReducer from './app/app';
import confirmTransactionReducer from './confirm-transaction/confirm-transaction.duck';
import gasReducer from './gas/gas.duck';
@ -17,7 +17,7 @@ export default combineReducers({
activeTab: (s) => (s === undefined ? null : s),
metamask: metamaskReducer,
appState: appStateReducer,
ENS: ensReducer,
DNS: domainReducer,
history: historyReducer,
send: sendReducer,
confirmTransaction: confirmTransactionReducer,

@ -82,7 +82,7 @@ import {
getUnapprovedTxs,
} from '../metamask/metamask';
import { resetEnsResolution } from '../ens';
import { resetDomainResolution } from '../domains';
import {
isBurnAddress,
isValidHexAddress,
@ -2199,7 +2199,7 @@ export function resetRecipientInput() {
await dispatch(addHistoryEntry(`sendFlow - user cleared recipient input`));
await dispatch(updateRecipientUserInput(''));
await dispatch(updateRecipient({ address: '', nickname: '' }));
await dispatch(resetEnsResolution());
await dispatch(resetDomainResolution());
await dispatch(validateRecipientUserInput({ chainId }));
};
}

@ -2108,7 +2108,7 @@ describe('Send Slice', () => {
expect(actionResult[8].type).toStrictEqual(
'send/computeEstimatedGasLimit/rejected',
);
expect(actionResult[9].type).toStrictEqual('ENS/resetEnsResolution');
expect(actionResult[9].type).toStrictEqual('DNS/resetDomainResolution');
expect(actionResult[10].type).toStrictEqual(
'send/validateRecipientUserInput',
);

@ -16,9 +16,9 @@ export default class AddRecipient extends Component {
ownedAccounts: PropTypes.array,
addressBook: PropTypes.array,
updateRecipient: PropTypes.func,
ensResolution: PropTypes.string,
ensError: PropTypes.string,
ensWarning: PropTypes.string,
domainResolution: PropTypes.string,
domainError: PropTypes.string,
domainWarning: PropTypes.string,
addressBookEntryName: PropTypes.string,
contacts: PropTypes.array,
nonContacts: PropTypes.array,
@ -102,7 +102,7 @@ export default class AddRecipient extends Component {
render() {
const {
ensResolution,
domainResolution,
recipient,
userInput,
addressBookEntryName,
@ -117,9 +117,9 @@ export default class AddRecipient extends Component {
recipient.nickname,
'validated user input',
);
} else if (ensResolution && !recipient.error) {
} else if (domainResolution && !recipient.error) {
content = this.renderExplicitAddress(
ensResolution,
domainResolution,
addressBookEntryName || userInput,
'ENS resolution',
);
@ -233,19 +233,19 @@ export default class AddRecipient extends Component {
}
renderDialogs() {
const { ensError, recipient, ensWarning } = this.props;
const { domainError, recipient, domainWarning } = this.props;
const { t } = this.context;
if (ensError || (recipient.error && recipient.error !== 'required')) {
if (domainError || (recipient.error && recipient.error !== 'required')) {
return (
<Dialog type="error" className="send__error-dialog">
{t(ensError ?? recipient.error)}
{t(domainError ?? recipient.error)}
</Dialog>
);
} else if (ensWarning || recipient.warning) {
} else if (domainWarning || recipient.warning) {
return (
<Dialog type="warning" className="send__error-dialog">
{t(ensWarning ?? recipient.warning)}
{t(domainWarning ?? recipient.warning)}
</Dialog>
);
}

@ -136,7 +136,7 @@ describe('AddRecipient Component', () => {
it('should render error when query has no results', () => {
wrapper.setProps({
addressBook: [],
ensError: 'bad',
domainError: 'bad',
contacts: [],
nonContacts: [],
});
@ -151,7 +151,7 @@ describe('AddRecipient Component', () => {
it('should render error when query has ens does not resolve', () => {
wrapper.setProps({
addressBook: [],
ensError: 'very bad',
domainError: 'very bad',
contacts: [],
nonContacts: [],
});
@ -166,8 +166,8 @@ describe('AddRecipient Component', () => {
it('should render error when ens resolved but ens error exists', () => {
wrapper.setProps({
addressBook: [],
ensError: 'bad',
ensResolution: '0x128',
domainError: 'bad',
domainResolution: '0x128',
});
const dialog = wrapper.find(Dialog);

@ -16,20 +16,20 @@ import {
addHistoryEntry,
} from '../../../../ducks/send';
import {
getEnsResolution,
getEnsError,
getEnsWarning,
} from '../../../../ducks/ens';
getDomainResolution,
getDomainError,
getDomainWarning,
} from '../../../../ducks/domains';
import AddRecipient from './add-recipient.component';
export default connect(mapStateToProps, mapDispatchToProps)(AddRecipient);
function mapStateToProps(state) {
const ensResolution = getEnsResolution(state);
const domainResolution = getDomainResolution(state);
let addressBookEntryName = '';
if (ensResolution) {
const addressBookEntry = getAddressBookEntry(state, ensResolution) || {};
if (domainResolution) {
const addressBookEntry = getAddressBookEntry(state, domainResolution) || {};
addressBookEntryName = addressBookEntry.name;
}
@ -41,9 +41,9 @@ function mapStateToProps(state) {
addressBook,
addressBookEntryName,
contacts: addressBook.filter(({ name }) => Boolean(name)),
ensResolution,
ensError: getEnsError(state),
ensWarning: getEnsWarning(state),
domainResolution,
domainError: getDomainError(state),
domainWarning: getDomainWarning(state),
nonContacts: addressBook.filter(({ name }) => !name),
ownedAccounts,
isUsingMyAccountsForRecipientSearch:

@ -18,10 +18,10 @@ jest.mock('../../../../selectors', () => ({
],
}));
jest.mock('../../../../ducks/ens', () => ({
getEnsResolution: (s) => `mockSendEnsResolution:${s}`,
getEnsError: (s) => `mockSendEnsResolutionError:${s}`,
getEnsWarning: (s) => `mockSendEnsResolutionWarning:${s}`,
jest.mock('../../../../ducks/domains', () => ({
getDomainResolution: (s) => `mockSendDomainResolution:${s}`,
getDomainError: (s) => `mockSendDomainResolutionError:${s}`,
getDomainWarning: (s) => `mockSendDomainResolutionWarning:${s}`,
useMyAccountsForRecipientSearch: (s) =>
`useMyAccountsForRecipientSearch:${s}`,
}));
@ -49,9 +49,9 @@ describe('add-recipient container', () => {
addressBook: [{ name: 'mockAddressBook:mockState' }],
addressBookEntryName: undefined,
contacts: [{ name: 'mockAddressBook:mockState' }],
ensResolution: 'mockSendEnsResolution:mockState',
ensError: 'mockSendEnsResolutionError:mockState',
ensWarning: 'mockSendEnsResolutionWarning:mockState',
domainResolution: 'mockSendDomainResolution:mockState',
domainError: 'mockSendDomainResolutionError:mockState',
domainWarning: 'mockSendDomainResolutionWarning:mockState',
nonContacts: [],
ownedAccounts: [
{ name: 'account1:mockState' },

@ -29,13 +29,13 @@ export default {
updateRecipient: {
action: 'updateRecipient',
},
ensResolution: {
domainResolution: {
control: 'text',
},
ensError: {
domainError: {
control: 'text',
},
ensWarning: {
domainWarning: {
control: 'text',
},
addressBookEntryName: {
@ -96,8 +96,8 @@ export const ErrorStory = (args) => {
};
ErrorStory.argTypes = {
// ensError must be the key for a translation
ensError: { type: 'text', defaultValue: 'loading' },
// domainError must be the key for a translation
domainError: { type: 'text', defaultValue: 'loading' },
};
ErrorStory.storyName = 'Error';
@ -115,8 +115,8 @@ export const WarningStory = (args) => {
};
WarningStory.argTypes = {
// ensWarning must be the key for a translation
ensWarning: { type: 'text', defaultValue: 'loading' },
// domainWarning must be the key for a translation
domainWarning: { type: 'text', defaultValue: 'loading' },
};
WarningStory.storyName = 'Warning';

@ -9,7 +9,7 @@ import {
isValidHexAddress,
} from '../../../../../shared/modules/hexstring-utils';
export default class EnsInput extends Component {
export default class DomainInput extends Component {
static contextTypes = {
t: PropTypes.func,
metricsEvent: PropTypes.func,
@ -27,12 +27,12 @@ export default class EnsInput extends Component {
onChange: PropTypes.func.isRequired,
onReset: PropTypes.func.isRequired,
lookupEnsName: PropTypes.func.isRequired,
initializeEnsSlice: PropTypes.func.isRequired,
resetEnsResolution: PropTypes.func.isRequired,
initializeDomainSlice: PropTypes.func.isRequired,
resetDomainResolution: PropTypes.func.isRequired,
};
componentDidMount() {
this.props.initializeEnsSlice();
this.props.initializeDomainSlice();
}
onPaste = (event) => {
@ -56,7 +56,7 @@ export default class EnsInput extends Component {
internalSearch,
onChange,
lookupEnsName,
resetEnsResolution,
resetDomainResolution,
} = this.props;
const input = value.trim();
@ -69,7 +69,7 @@ export default class EnsInput extends Component {
if (isValidDomainName(input)) {
lookupEnsName(input);
} else {
resetEnsResolution();
resetDomainResolution();
if (
onValidAddressTyped &&
!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);

@ -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 SendContent from './send-content';
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) =>
isCustomPriceExcessive(state, true);
@ -112,7 +112,7 @@ export default function SendTransactionScreen() {
return (
<div className="page-container">
<SendHeader history={history} />
<EnsInput
<DomainInput
userInput={userInput}
className="send__to-row"
onChange={(address) => dispatch(updateRecipientUserInput(address))}

@ -3,7 +3,7 @@ import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { useLocation } from 'react-router-dom';
import { SEND_STAGES, startNewDraftTransaction } from '../../ducks/send';
import { ensInitialState } from '../../ducks/ens';
import { domainInitialState } from '../../ducks/domains';
import { renderWithProvider } from '../../../test/jest';
import { CHAIN_IDS } from '../../../shared/constants/network';
import { GAS_ESTIMATE_TYPES } from '../../../shared/constants/gas';
@ -54,7 +54,7 @@ jest.mock('ethers', () => {
});
const baseStore = {
send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
ENS: ensInitialState,
DNS: domainInitialState,
gas: {
customData: { limit: null, price: null },
},
@ -132,7 +132,7 @@ describe('Send Page', () => {
expect(actions).toStrictEqual(
expect.arrayContaining([
expect.objectContaining({
type: 'ENS/enableEnsLookup',
type: 'DNS/enableDomainLookup',
}),
]),
);
@ -146,7 +146,7 @@ describe('Send Page', () => {
expect(actions).toStrictEqual(
expect.arrayContaining([
expect.objectContaining({
type: 'ENS/enableEnsLookup',
type: 'DNS/enableDomainLookup',
}),
expect.objectContaining({
type: 'UI_MODAL_OPEN',
@ -165,7 +165,7 @@ describe('Send Page', () => {
expect(getByText('Send to')).toBeTruthy();
});
it('should render the EnsInput field', () => {
it('should render the DomainInput field', () => {
const store = configureMockStore(middleware)(baseStore);
const { getByPlaceholderText } = renderWithProvider(<Send />, store);
expect(
@ -209,7 +209,7 @@ describe('Send Page', () => {
expect(getByText('Send')).toBeTruthy();
});
it('should render the EnsInput field', () => {
it('should render the DomainInput field', () => {
const store = configureMockStore(middleware)(baseStore);
const { getByPlaceholderText } = renderWithProvider(<Send />, store);
expect(

@ -5,7 +5,7 @@ import Identicon from '../../../../components/ui/identicon';
import TextField from '../../../../components/ui/text-field';
import { CONTACT_LIST_ROUTE } from '../../../../helpers/constants/routes';
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 {
isBurnAddress,
@ -25,9 +25,9 @@ export default class AddContact extends PureComponent {
qrCodeData:
PropTypes.object /* eslint-disable-line react/no-unused-prop-types */,
qrCodeDetected: PropTypes.func,
ensResolution: PropTypes.string,
ensError: PropTypes.string,
resetEnsResolution: PropTypes.func,
domainResolution: PropTypes.string,
domainError: PropTypes.string,
resetDomainResolution: PropTypes.func,
};
state = {
@ -45,10 +45,10 @@ export default class AddContact extends PureComponent {
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.qrCodeData) {
if (nextProps.qrCodeData.type === 'address') {
const { ensResolution } = this.props;
const { domainResolution } = this.props;
const scannedAddress =
nextProps.qrCodeData.values.address.toLowerCase();
const currentAddress = ensResolution || this.state.ethAddress;
const currentAddress = domainResolution || this.state.ethAddress;
if (currentAddress.toLowerCase() !== scannedAddress) {
this.setState({ input: scannedAddress });
this.validate(scannedAddress);
@ -79,7 +79,7 @@ export default class AddContact extends PureComponent {
renderInput() {
return (
<EnsInput
<DomainInput
scanQrCode={(_) => {
this.props.scanQrCode();
}}
@ -89,7 +89,7 @@ export default class AddContact extends PureComponent {
this.validate(text);
}}
onReset={() => {
this.props.resetEnsResolution();
this.props.resetDomainResolution();
this.setState({ ethAddress: '', input: '' });
}}
userInput={this.state.input}
@ -99,17 +99,18 @@ export default class AddContact extends PureComponent {
render() {
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 (
<div className="settings-page__content-row address-book__add-contact">
{ensResolution && (
{domainResolution && (
<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">
{ensResolution}
{domainResolution}
</div>
</div>
)}
@ -145,7 +146,7 @@ export default class AddContact extends PureComponent {
disabled={Boolean(this.state.error)}
onSubmit={async () => {
await addToAddressBook(
ensResolution || this.state.ethAddress,
domainResolution || this.state.ethAddress,
this.state.newName,
);
history.push(CONTACT_LIST_ROUTE);

@ -8,17 +8,17 @@ import {
} from '../../../../store/actions';
import { getQrCodeData } from '../../../../ducks/app/app';
import {
getEnsError,
getEnsResolution,
resetEnsResolution,
} from '../../../../ducks/ens';
getDomainError,
getDomainResolution,
resetDomainResolution,
} from '../../../../ducks/domains';
import AddContact from './add-contact.component';
const mapStateToProps = (state) => {
return {
qrCodeData: getQrCodeData(state),
ensError: getEnsError(state),
ensResolution: getEnsResolution(state),
domainError: getDomainError(state),
domainResolution: getDomainResolution(state),
};
};
@ -28,7 +28,7 @@ const mapDispatchToProps = (dispatch) => {
dispatch(addToAddressBook(recipient, nickname)),
scanQrCode: () => dispatch(showQrScanner()),
qrCodeDetected: (data) => dispatch(qrCodeDetected(data)),
resetEnsResolution: () => dispatch(resetEnsResolution()),
resetDomainResolution: () => dispatch(resetDomainResolution()),
};
};

Loading…
Cancel
Save