1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Feat/add collectible manually (#12834)

* hook up add collectible manually flow

* address feedback
This commit is contained in:
Alex Donesky 2021-11-26 14:03:35 -06:00 committed by GitHub
parent 609f541b76
commit 5aa191fd2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 139 additions and 11 deletions

View File

@ -1680,6 +1680,12 @@
"message": "Account $1", "message": "Account $1",
"description": "Default name of next account to be created on create account screen" "description": "Default name of next account to be created on create account screen"
}, },
"newCollectibleAddFailed": {
"message": "Collectible was not added because: $1"
},
"newCollectibleAddedMessage": {
"message": "Collectible was successfully added!"
},
"newContact": { "newContact": {
"message": "New Contact" "message": "New Contact"
}, },

View File

@ -183,7 +183,9 @@ export default class MetamaskController extends EventEmitter {
state: initState.TokensController, state: initState.TokensController,
}); });
this.assetsContractController = new AssetsContractController(); this.assetsContractController = new AssetsContractController({
provider: this.provider,
});
this.collectiblesController = new CollectiblesController({ this.collectiblesController = new CollectiblesController({
onPreferencesStateChange: this.preferencesController.store.subscribe.bind( onPreferencesStateChange: this.preferencesController.store.subscribe.bind(
@ -588,6 +590,7 @@ export default class MetamaskController extends EventEmitter {
console.error(error); console.error(error);
} }
}); });
this.networkController.lookupNetwork(); this.networkController.lookupNetwork();
this.messageManager = new MessageManager({ this.messageManager = new MessageManager({
metricsEvent: this.metaMetricsController.trackEvent.bind( metricsEvent: this.metaMetricsController.trackEvent.bind(
@ -1046,6 +1049,11 @@ export default class MetamaskController extends EventEmitter {
collectiblesController, collectiblesController,
), ),
addCollectibleVerifyOwnership: nodeify(
collectiblesController.addCollectibleVerifyOwnership,
collectiblesController,
),
removeAndIgnoreCollectible: nodeify( removeAndIgnoreCollectible: nodeify(
collectiblesController.removeAndIgnoreCollectible, collectiblesController.removeAndIgnoreCollectible,
collectiblesController, collectiblesController,

View File

@ -111,7 +111,7 @@
"@keystonehq/metamask-airgapped-keyring": "0.2.1", "@keystonehq/metamask-airgapped-keyring": "0.2.1",
"@material-ui/core": "^4.11.0", "@material-ui/core": "^4.11.0",
"@metamask/contract-metadata": "^1.28.0", "@metamask/contract-metadata": "^1.28.0",
"@metamask/controllers": "^20.0.0", "@metamask/controllers": "^20.1.0",
"@metamask/eth-ledger-bridge-keyring": "^0.10.0", "@metamask/eth-ledger-bridge-keyring": "^0.10.0",
"@metamask/eth-token-tracker": "^3.0.1", "@metamask/eth-token-tracker": "^3.0.1",
"@metamask/etherscan-link": "^2.1.0", "@metamask/etherscan-link": "^2.1.0",

View File

@ -55,6 +55,7 @@ export default function reduceApp(state = {}, action) {
ledgerWebHidConnectedStatus: WEBHID_CONNECTED_STATUSES.UNKNOWN, ledgerWebHidConnectedStatus: WEBHID_CONNECTED_STATUSES.UNKNOWN,
ledgerTransportStatus: TRANSPORT_STATES.NONE, ledgerTransportStatus: TRANSPORT_STATES.NONE,
newNetworkAdded: '', newNetworkAdded: '',
newCollectibleAddedMessage: '',
...state, ...state,
}; };
@ -290,6 +291,12 @@ export default function reduceApp(state = {}, action) {
newNetworkAdded: action.value, newNetworkAdded: action.value,
}; };
case actionConstants.SET_NEW_COLLECTIBLE_ADDED_MESSAGE:
return {
...appState,
newCollectibleAddedMessage: action.value,
};
case actionConstants.LOADING_METHOD_DATA_STARTED: case actionConstants.LOADING_METHOD_DATA_STARTED:
return { return {
...appState, ...appState,

View File

@ -1,27 +1,43 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { useI18nContext } from '../../hooks/useI18nContext'; import { useI18nContext } from '../../hooks/useI18nContext';
import { DEFAULT_ROUTE } from '../../helpers/constants/routes'; import { DEFAULT_ROUTE } from '../../helpers/constants/routes';
import Box from '../../components/ui/box'; import Box from '../../components/ui/box';
import TextField from '../../components/ui/text-field'; import TextField from '../../components/ui/text-field';
import PageContainer from '../../components/ui/page-container'; import PageContainer from '../../components/ui/page-container';
import {
addCollectibleVerifyOwnership,
setNewCollectibleAddedMessage,
} from '../../store/actions';
export default function AddCollectible() { export default function AddCollectible() {
const t = useI18nContext(); const t = useI18nContext();
const history = useHistory(); const history = useHistory();
const dispatch = useDispatch();
const [address, setAddress] = useState(''); const [address, setAddress] = useState('');
const [tokenId, setTokenId] = useState(''); const [tokenId, setTokenId] = useState('');
const handleAddCollectible = async () => {
try {
await dispatch(addCollectibleVerifyOwnership(address, tokenId));
} catch (error) {
const { message } = error;
dispatch(setNewCollectibleAddedMessage(message));
history.push(DEFAULT_ROUTE);
return;
}
dispatch(setNewCollectibleAddedMessage('success'));
history.push(DEFAULT_ROUTE);
};
return ( return (
<PageContainer <PageContainer
title={t('addNFT')} title={t('addNFT')}
onSubmit={() => { onSubmit={() => {
console.log( handleAddCollectible();
`Adding collectible with ID: ${tokenId} and address ${address}`,
);
history.push(DEFAULT_ROUTE);
}} }}
submitText={t('add')} submitText={t('add')}
onCancel={() => { onCancel={() => {

View File

@ -92,6 +92,8 @@ export default class Home extends PureComponent {
newNetworkAdded: PropTypes.string, newNetworkAdded: PropTypes.string,
setNewNetworkAdded: PropTypes.func.isRequired, setNewNetworkAdded: PropTypes.func.isRequired,
isSigningQRHardwareTransaction: PropTypes.bool.isRequired, isSigningQRHardwareTransaction: PropTypes.bool.isRequired,
newCollectibleAddedMessage: PropTypes.string,
setNewCollectibleAddedMessage: PropTypes.func.isRequired,
}; };
state = { state = {
@ -225,9 +227,42 @@ export default class Home extends PureComponent {
infuraBlocked, infuraBlocked,
newNetworkAdded, newNetworkAdded,
setNewNetworkAdded, setNewNetworkAdded,
newCollectibleAddedMessage,
setNewCollectibleAddedMessage,
} = this.props; } = this.props;
return ( return (
<MultipleNotifications> <MultipleNotifications>
{newCollectibleAddedMessage ? (
<ActionableMessage
type={newCollectibleAddedMessage === 'success' ? 'info' : 'warning'}
className="home__new-network-notification"
message={
<div className="home__new-network-notification-message">
{newCollectibleAddedMessage === 'success' ? (
<img
src="./images/check_circle.svg"
className="home__new-network-notification-message--image"
/>
) : null}
<Typography
variant={TYPOGRAPHY.H7}
fontWeight={FONT_WEIGHT.NORMAL}
>
{newCollectibleAddedMessage === 'success'
? t('newCollectibleAddedMessage')
: t('newCollectibleAddFailed', [
newCollectibleAddedMessage,
])}
</Typography>
<button
className="fas fa-times home__close"
title={t('close')}
onClick={() => setNewCollectibleAddedMessage('')}
/>
</div>
}
/>
) : null}
{newNetworkAdded ? ( {newNetworkAdded ? (
<ActionableMessage <ActionableMessage
type="info" type="info"
@ -242,7 +277,7 @@ export default class Home extends PureComponent {
variant={TYPOGRAPHY.H7} variant={TYPOGRAPHY.H7}
fontWeight={FONT_WEIGHT.NORMAL} fontWeight={FONT_WEIGHT.NORMAL}
> >
{this.context.t('newNetworkAdded', [newNetworkAdded])} {t('newNetworkAdded', [newNetworkAdded])}
</Typography> </Typography>
<button <button
className="fas fa-times home__close" className="fas fa-times home__close"

View File

@ -18,6 +18,7 @@ import {
getNewNetworkAdded, getNewNetworkAdded,
hasUnsignedQRHardwareTransaction, hasUnsignedQRHardwareTransaction,
hasUnsignedQRHardwareMessage, hasUnsignedQRHardwareMessage,
getNewCollectibleAddedMessage,
} from '../../selectors'; } from '../../selectors';
import { import {
@ -32,6 +33,7 @@ import {
setRecoveryPhraseReminderHasBeenShown, setRecoveryPhraseReminderHasBeenShown,
setRecoveryPhraseReminderLastShown, setRecoveryPhraseReminderLastShown,
setNewNetworkAdded, setNewNetworkAdded,
setNewCollectibleAddedMessage,
} from '../../store/actions'; } from '../../store/actions';
import { setThreeBoxLastUpdated, hideWhatsNewPopup } from '../../ducks/app/app'; import { setThreeBoxLastUpdated, hideWhatsNewPopup } from '../../ducks/app/app';
import { getWeb3ShimUsageAlertEnabledness } from '../../ducks/metamask/metamask'; import { getWeb3ShimUsageAlertEnabledness } from '../../ducks/metamask/metamask';
@ -122,6 +124,7 @@ const mapStateToProps = (state) => {
seedPhraseBackedUp, seedPhraseBackedUp,
newNetworkAdded: getNewNetworkAdded(state), newNetworkAdded: getNewNetworkAdded(state),
isSigningQRHardwareTransaction, isSigningQRHardwareTransaction,
newCollectibleAddedMessage: getNewCollectibleAddedMessage(state),
}; };
}; };
@ -154,6 +157,9 @@ const mapDispatchToProps = (dispatch) => ({
setNewNetworkAdded: (newNetwork) => { setNewNetworkAdded: (newNetwork) => {
dispatch(setNewNetworkAdded(newNetwork)); dispatch(setNewNetworkAdded(newNetwork));
}, },
setNewCollectibleAddedMessage: (message) => {
dispatch(setNewCollectibleAddedMessage(message));
},
}); });
export default compose( export default compose(

View File

@ -735,6 +735,10 @@ export function doesAddressRequireLedgerHidConnection(state, address) {
); );
} }
export function getNewCollectibleAddedMessage(state) {
return state.appState.newCollectibleAddedMessage;
}
/** /**
* To retrieve the name of the new Network added using add network form * To retrieve the name of the new Network added using add network form
* @param {*} state * @param {*} state

View File

@ -90,6 +90,8 @@ export const SET_FIRST_TIME_FLOW_TYPE = 'SET_FIRST_TIME_FLOW_TYPE';
export const SET_SELECTED_SETTINGS_RPC_URL = 'SET_SELECTED_SETTINGS_RPC_URL'; export const SET_SELECTED_SETTINGS_RPC_URL = 'SET_SELECTED_SETTINGS_RPC_URL';
export const SET_NEW_NETWORK_ADDED = 'SET_NEW_NETWORK_ADDED'; export const SET_NEW_NETWORK_ADDED = 'SET_NEW_NETWORK_ADDED';
export const SET_NEW_COLLECTIBLE_ADDED_MESSAGE =
'SET_NEW_COLLECTIBLE_ADDED_MESSAGE';
export const LOADING_METHOD_DATA_STARTED = 'LOADING_METHOD_DATA_STARTED'; export const LOADING_METHOD_DATA_STARTED = 'LOADING_METHOD_DATA_STARTED';
export const LOADING_METHOD_DATA_FINISHED = 'LOADING_METHOD_DATA_FINISHED'; export const LOADING_METHOD_DATA_FINISHED = 'LOADING_METHOD_DATA_FINISHED';

View File

@ -1317,6 +1317,43 @@ export function addCollectible(address, tokenID, dontShowLoadingIndicator) {
}; };
} }
export function addCollectibleVerifyOwnership(
address,
tokenID,
dontShowLoadingIndicator,
) {
return async (dispatch) => {
if (!address) {
throw new Error('MetaMask - Cannot add collectible without address');
}
if (!tokenID) {
throw new Error('MetaMask - Cannot add collectible without tokenID');
}
if (!dontShowLoadingIndicator) {
dispatch(showLoadingIndication());
}
try {
await promisifiedBackground.addCollectibleVerifyOwnership(
address,
tokenID,
);
} catch (error) {
if (
error.message.includes('This collectible is not owned by the user') ||
error.message.includes('Unable to verify ownership.')
) {
throw error;
} else {
log.error(error);
dispatch(displayWarning(error.message));
}
} finally {
await forceUpdateMetamaskState(dispatch);
dispatch(hideLoadingIndication());
}
};
}
export function removeAndIgnoreCollectible( export function removeAndIgnoreCollectible(
address, address,
tokenID, tokenID,
@ -2552,6 +2589,13 @@ export function setNewNetworkAdded(newNetworkAdded) {
}; };
} }
export function setNewCollectibleAddedMessage(newCollectibleAddedMessage) {
return {
type: actionConstants.SET_NEW_COLLECTIBLE_ADDED_MESSAGE,
value: newCollectibleAddedMessage,
};
}
export function setLastActiveTime() { export function setLastActiveTime() {
return (dispatch) => { return (dispatch) => {
background.setLastActiveTime((err) => { background.setLastActiveTime((err) => {

View File

@ -2606,10 +2606,10 @@
resolved "https://registry.yarnpkg.com/@metamask/contract-metadata/-/contract-metadata-1.30.0.tgz#fa8e1b0c3e7aaa963986088f691fb553ffbe3904" resolved "https://registry.yarnpkg.com/@metamask/contract-metadata/-/contract-metadata-1.30.0.tgz#fa8e1b0c3e7aaa963986088f691fb553ffbe3904"
integrity sha512-b2usYW/ptQYnE6zhUmr4T+nvOAQJK5ABcpKudyQANpy4K099elpv4aN0WcrcOcwV99NHOdMzFP3ZuG0HoAyOBQ== integrity sha512-b2usYW/ptQYnE6zhUmr4T+nvOAQJK5ABcpKudyQANpy4K099elpv4aN0WcrcOcwV99NHOdMzFP3ZuG0HoAyOBQ==
"@metamask/controllers@^20.0.0": "@metamask/controllers@^20.1.0":
version "20.0.0" version "20.1.0"
resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-20.0.0.tgz#5c3fd293e1c8d3de964bbbfadbd73d637d83a1a8" resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-20.1.0.tgz#1d8386dc22d14f9fd9feb8b3cc8314d663587550"
integrity sha512-H7ql719730+KCFRvAxWVKe3PvEabKgA9b+0+k4/nRA2Xvb7Pe3BlQ4lgt44wOFKDqdWyvwvH7mgNTAhDzBu4OA== integrity sha512-Z/7uLGXZWbCBbtCybR3jo1bx3mcvZRUSm1i43od4dnJoQo2+Veq4ePrFVgPKS3WtLIM/hzZuI7UTAQ9HNX9aew==
dependencies: dependencies:
"@ethereumjs/common" "^2.3.1" "@ethereumjs/common" "^2.3.1"
"@ethereumjs/tx" "^3.2.1" "@ethereumjs/tx" "^3.2.1"