Override package-lock and fix merge conflictsfeature/default_network_editable
commit
96d789d2cf
@ -0,0 +1,34 @@ |
||||
--- |
||||
name: Bug Report |
||||
about: Using MetaMask, but it's not working as you expect? |
||||
|
||||
--- |
||||
|
||||
<!-- |
||||
BEFORE SUBMITTING: PLEASE SEARCH TO MAKE SURE THIS ISSUE HAS NOT BEEN SUBMITTED |
||||
--> |
||||
|
||||
**Describe the bug** |
||||
A clear and concise description of what the bug is. |
||||
|
||||
**To Reproduce** |
||||
Steps to reproduce the behavior: |
||||
1. Go to '...' |
||||
2. Click on '....' |
||||
3. Scroll down to '....' |
||||
4. See error |
||||
|
||||
**Expected behavior** |
||||
A clear description of what you expected to happen. |
||||
|
||||
**Screenshots** |
||||
If applicable, add screenshots to help explain your problem. |
||||
|
||||
**Browser details (please complete the following information):** |
||||
- OS: [e.g. iOS] |
||||
- Browser [e.g. chrome, safari] |
||||
- MetaMask Version [e.g. 4.9.0] |
||||
- Old UI or New / Beta UI? |
||||
|
||||
**Additional context** |
||||
Add any other context about the problem here. |
@ -0,0 +1,14 @@ |
||||
--- |
||||
name: Feature Request |
||||
about: Looking for a feature that doesn't exist? Let us know! |
||||
|
||||
--- |
||||
|
||||
**What problem are you trying to solve?** |
||||
A short description of what you're trying to do. E.g., "My users need to wrap ETH, but they're intimidated by the confirm screen..." or "I'm trying to debug my application, and XYZ..." |
||||
|
||||
**Describe the solution you'd like** |
||||
A clear and concise description of what you want to happen. Try to also include any alternative solutions you've considered. |
||||
|
||||
**Additional context** |
||||
Add any other context or screenshots about the feature request here. |
@ -0,0 +1,9 @@ |
||||
--- |
||||
name: Support Request or Question |
||||
about: Have a question about how to use MetaMask? |
||||
|
||||
--- |
||||
|
||||
FOR USER QUESTIONS, PLEASE DO NOT OPEN A GITHUB ISSUE - IT WILL NOT BE HANDLED HERE. |
||||
|
||||
INSTEAD, PLEASE EMAIL SUPPORT@METAMASK.IO WITH A DESCRIPTION OF YOUR PROBLEM. |
After Width: | Height: | Size: 1020 B |
@ -0,0 +1,40 @@ |
||||
// next version number
|
||||
const version = 28 |
||||
|
||||
/* |
||||
|
||||
normalizes txParams on unconfirmed txs |
||||
|
||||
*/ |
||||
const clone = require('clone') |
||||
|
||||
module.exports = { |
||||
version, |
||||
|
||||
migrate: async function (originalVersionedData) { |
||||
const versionedData = clone(originalVersionedData) |
||||
versionedData.meta.version = version |
||||
const state = versionedData.data |
||||
const newState = transformState(state) |
||||
versionedData.data = newState |
||||
return versionedData |
||||
}, |
||||
} |
||||
|
||||
function transformState (state) { |
||||
const newState = state |
||||
|
||||
if (newState.PreferencesController) { |
||||
if (newState.PreferencesController.tokens && newState.PreferencesController.identities) { |
||||
const identities = newState.PreferencesController.identities |
||||
const tokens = newState.PreferencesController.tokens |
||||
newState.PreferencesController.accountTokens = {} |
||||
for (const identity in identities) { |
||||
newState.PreferencesController.accountTokens[identity] = {'mainnet': tokens} |
||||
} |
||||
newState.PreferencesController.tokens = [] |
||||
} |
||||
} |
||||
|
||||
return newState |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,46 @@ |
||||
const assert = require('assert') |
||||
const migration28 = require('../../../app/scripts/migrations/028') |
||||
|
||||
const oldStorage = { |
||||
'meta': {}, |
||||
'data': { |
||||
'PreferencesController': { |
||||
'tokens': [{address: '0xa', symbol: 'A', decimals: 4}, {address: '0xb', symbol: 'B', decimals: 4}], |
||||
'identities': { |
||||
'0x6d14': {}, |
||||
'0x3695': {}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
describe('migration #28', () => { |
||||
it('should add corresponding tokens to accountTokens', (done) => { |
||||
migration28.migrate(oldStorage) |
||||
.then((newStorage) => { |
||||
const newTokens = newStorage.data.PreferencesController.tokens |
||||
const newAccountTokens = newStorage.data.PreferencesController.accountTokens |
||||
|
||||
const testTokens = [{address: '0xa', symbol: 'A', decimals: 4}, {address: '0xb', symbol: 'B', decimals: 4}] |
||||
assert.equal(newTokens.length, 0, 'tokens is expected to have the length of 0') |
||||
assert.equal(newAccountTokens['0x6d14']['mainnet'].length, 2, 'tokens for address is expected to have the length of 2') |
||||
assert.equal(newAccountTokens['0x3695']['mainnet'].length, 2, 'tokens for address is expected to have the length of 2') |
||||
assert.equal(Object.keys(newAccountTokens).length, 2, 'account tokens should be created for all identities') |
||||
assert.deepEqual(newAccountTokens['0x6d14']['mainnet'], testTokens, 'tokens for address should be the same than before') |
||||
assert.deepEqual(newAccountTokens['0x3695']['mainnet'], testTokens, 'tokens for address should be the same than before') |
||||
done() |
||||
}) |
||||
.catch(done) |
||||
}) |
||||
|
||||
it('should successfully migrate first time state', (done) => { |
||||
migration28.migrate({ |
||||
meta: {}, |
||||
data: require('../../../app/scripts/first-time-state'), |
||||
}) |
||||
.then((migratedData) => { |
||||
assert.equal(migratedData.meta.version, migration28.version) |
||||
done() |
||||
}).catch(done) |
||||
}) |
||||
}) |
@ -0,0 +1,2 @@ |
||||
import QrScanner from './qr-scanner.container' |
||||
module.exports = QrScanner |
@ -0,0 +1,83 @@ |
||||
.qr-scanner { |
||||
width: 100%; |
||||
height: 100%; |
||||
background-color: #fff; |
||||
display: flex; |
||||
flex-flow: column; |
||||
border-radius: 8px; |
||||
|
||||
&__title { |
||||
font-size: 1.5rem; |
||||
font-weight: 500; |
||||
padding: 16px 0; |
||||
text-align: center; |
||||
} |
||||
|
||||
&__content { |
||||
padding-left: 20px; |
||||
padding-right: 20px; |
||||
|
||||
&__video-wrapper { |
||||
overflow: hidden; |
||||
width: 100%; |
||||
height: 275px; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
|
||||
video { |
||||
transform: scaleX(-1); |
||||
width: auto; |
||||
height: 275px; |
||||
} |
||||
} |
||||
} |
||||
|
||||
&__status { |
||||
text-align: center; |
||||
font-size: 14px; |
||||
padding: 15px; |
||||
} |
||||
|
||||
&__image { |
||||
font-size: 1.5rem; |
||||
font-weight: 500; |
||||
padding: 16px 0 0; |
||||
text-align: center; |
||||
} |
||||
|
||||
&__error { |
||||
text-align: center; |
||||
font-size: 16px; |
||||
padding: 15px; |
||||
} |
||||
|
||||
&__footer { |
||||
padding: 20px; |
||||
flex-direction: row; |
||||
display: flex; |
||||
|
||||
button { |
||||
margin-right: 15px; |
||||
} |
||||
|
||||
button:last-of-type { |
||||
margin-right: 0; |
||||
background-color: #009eec; |
||||
border: none; |
||||
color: #fff; |
||||
} |
||||
} |
||||
|
||||
&__close::after { |
||||
content: '\00D7'; |
||||
font-size: 35px; |
||||
color: #9b9b9b; |
||||
position: absolute; |
||||
top: 4px; |
||||
right: 20px; |
||||
cursor: pointer; |
||||
font-weight: 300; |
||||
} |
||||
} |
||||
|
@ -0,0 +1,216 @@ |
||||
import React, { Component } from 'react' |
||||
import PropTypes from 'prop-types' |
||||
import { BrowserQRCodeReader } from '@zxing/library' |
||||
import adapter from 'webrtc-adapter' // eslint-disable-line import/no-nodejs-modules, no-unused-vars
|
||||
import Spinner from '../../spinner' |
||||
import WebcamUtils from '../../../../lib/webcam-utils' |
||||
import PageContainerFooter from '../../page-container/page-container-footer/page-container-footer.component' |
||||
|
||||
export default class QrScanner extends Component { |
||||
static propTypes = { |
||||
hideModal: PropTypes.func.isRequired, |
||||
qrCodeDetected: PropTypes.func, |
||||
scanQrCode: PropTypes.func, |
||||
error: PropTypes.bool, |
||||
errorType: PropTypes.string, |
||||
} |
||||
|
||||
static contextTypes = { |
||||
t: PropTypes.func, |
||||
} |
||||
|
||||
constructor (props, context) { |
||||
super(props) |
||||
|
||||
this.state = { |
||||
ready: false, |
||||
msg: context.t('accessingYourCamera'), |
||||
} |
||||
this.codeReader = null |
||||
this.permissionChecker = null |
||||
this.needsToReinit = false |
||||
|
||||
// Clear pre-existing qr code data before scanning
|
||||
this.props.qrCodeDetected(null) |
||||
} |
||||
|
||||
componentDidMount () { |
||||
this.initCamera() |
||||
} |
||||
|
||||
async checkPermisisions () { |
||||
const { permissions } = await WebcamUtils.checkStatus() |
||||
if (permissions) { |
||||
clearTimeout(this.permissionChecker) |
||||
// Let the video stream load first...
|
||||
setTimeout(_ => { |
||||
this.setState({ |
||||
ready: true, |
||||
msg: this.context.t('scanInstructions'), |
||||
}) |
||||
if (this.needsToReinit) { |
||||
this.initCamera() |
||||
this.needsToReinit = false |
||||
} |
||||
}, 2000) |
||||
} else { |
||||
// Keep checking for permissions
|
||||
this.permissionChecker = setTimeout(_ => { |
||||
this.checkPermisisions() |
||||
}, 1000) |
||||
} |
||||
} |
||||
|
||||
componentWillUnmount () { |
||||
clearTimeout(this.permissionChecker) |
||||
if (this.codeReader) { |
||||
this.codeReader.reset() |
||||
} |
||||
} |
||||
|
||||
initCamera () { |
||||
this.codeReader = new BrowserQRCodeReader() |
||||
this.codeReader.getVideoInputDevices() |
||||
.then(videoInputDevices => { |
||||
clearTimeout(this.permissionChecker) |
||||
this.checkPermisisions() |
||||
this.codeReader.decodeFromInputVideoDevice(undefined, 'video') |
||||
.then(content => { |
||||
const result = this.parseContent(content.text) |
||||
if (result.type !== 'unknown') { |
||||
this.props.qrCodeDetected(result) |
||||
this.stopAndClose() |
||||
} else { |
||||
this.setState({msg: this.context.t('unknownQrCode')}) |
||||
} |
||||
}) |
||||
.catch(err => { |
||||
if (err && err.name === 'NotAllowedError') { |
||||
this.setState({msg: this.context.t('youNeedToAllowCameraAccess')}) |
||||
clearTimeout(this.permissionChecker) |
||||
this.needsToReinit = true |
||||
this.checkPermisisions() |
||||
} |
||||
}) |
||||
}).catch(err => { |
||||
console.error('[QR-SCANNER]: getVideoInputDevices threw an exception: ', err) |
||||
}) |
||||
} |
||||
|
||||
parseContent (content) { |
||||
let type = 'unknown' |
||||
let values = {} |
||||
|
||||
// Here we could add more cases
|
||||
// To parse other type of links
|
||||
// For ex. EIP-681 (https://eips.ethereum.org/EIPS/eip-681)
|
||||
|
||||
|
||||
// Ethereum address links - fox ex. ethereum:0x.....1111
|
||||
if (content.split('ethereum:').length > 1) { |
||||
|
||||
type = 'address' |
||||
values = {'address': content.split('ethereum:')[1] } |
||||
|
||||
// Regular ethereum addresses - fox ex. 0x.....1111
|
||||
} else if (content.substring(0, 2).toLowerCase() === '0x') { |
||||
|
||||
type = 'address' |
||||
values = {'address': content } |
||||
|
||||
} |
||||
return {type, values} |
||||
} |
||||
|
||||
|
||||
stopAndClose = () => { |
||||
if (this.codeReader) { |
||||
this.codeReader.reset() |
||||
} |
||||
this.setState({ ready: false }) |
||||
this.props.hideModal() |
||||
} |
||||
|
||||
tryAgain = () => { |
||||
// close the modal
|
||||
this.stopAndClose() |
||||
// wait for the animation and try again
|
||||
setTimeout(_ => { |
||||
this.props.scanQrCode() |
||||
}, 1000) |
||||
} |
||||
|
||||
renderVideo () { |
||||
return ( |
||||
<div className={'qr-scanner__content__video-wrapper'}> |
||||
<video |
||||
id="video" |
||||
style={{ |
||||
display: this.state.ready ? 'block' : 'none', |
||||
}} |
||||
/> |
||||
{ !this.state.ready ? <Spinner color={'#F7C06C'} /> : null} |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
renderErrorModal () { |
||||
let title, msg |
||||
|
||||
if (this.props.error) { |
||||
if (this.props.errorType === 'NO_WEBCAM_FOUND') { |
||||
title = this.context.t('noWebcamFoundTitle') |
||||
msg = this.context.t('noWebcamFound') |
||||
} else { |
||||
title = this.context.t('unknownCameraErrorTitle') |
||||
msg = this.context.t('unknownCameraError') |
||||
} |
||||
} |
||||
|
||||
return ( |
||||
<div className="qr-scanner"> |
||||
<div className="qr-scanner__close" onClick={this.stopAndClose}></div> |
||||
|
||||
<div className="qr-scanner__image"> |
||||
<img src={'images/webcam.svg'} width={70} height={70} /> |
||||
</div> |
||||
<div className="qr-scanner__title"> |
||||
{ title } |
||||
</div> |
||||
<div className={'qr-scanner__error'}> |
||||
{msg} |
||||
</div> |
||||
<PageContainerFooter |
||||
onCancel={this.stopAndClose} |
||||
onSubmit={this.tryAgain} |
||||
cancelText={this.context.t('cancel')} |
||||
submitText={this.context.t('tryAgain')} |
||||
submitButtonType="confirm" |
||||
/> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
render () { |
||||
const { t } = this.context |
||||
|
||||
if (this.props.error) { |
||||
return this.renderErrorModal() |
||||
} |
||||
|
||||
return ( |
||||
<div className="qr-scanner"> |
||||
<div className="qr-scanner__close" onClick={this.stopAndClose}></div> |
||||
<div className="qr-scanner__title"> |
||||
{ `${t('scanQrCode')}` } |
||||
</div> |
||||
<div className="qr-scanner__content"> |
||||
{ this.renderVideo() } |
||||
</div> |
||||
<div className={'qr-scanner__status'}> |
||||
{this.state.msg} |
||||
</div> |
||||
</div> |
||||
) |
||||
} |
||||
} |
@ -0,0 +1,24 @@ |
||||
import { connect } from 'react-redux' |
||||
import QrScanner from './qr-scanner.component' |
||||
|
||||
const { hideModal, qrCodeDetected, showQrScanner } = require('../../../actions') |
||||
import { |
||||
SEND_ROUTE, |
||||
} from '../../../routes' |
||||
|
||||
const mapStateToProps = state => { |
||||
return { |
||||
error: state.appState.modal.modalState.props.error, |
||||
errorType: state.appState.modal.modalState.props.errorType, |
||||
} |
||||
} |
||||
|
||||
const mapDispatchToProps = dispatch => { |
||||
return { |
||||
hideModal: () => dispatch(hideModal()), |
||||
qrCodeDetected: (data) => dispatch(qrCodeDetected(data)), |
||||
scanQrCode: () => dispatch(showQrScanner(SEND_ROUTE)), |
||||
} |
||||
} |
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(QrScanner) |
@ -1,3 +1,3 @@ |
||||
export const INSUFFICIENT_FUNDS_ERROR_KEY = 'insufficientFunds' |
||||
export const GAS_LIMIT_TOO_LOW_ERROR_KEY = 'gasLimitTooLow' |
||||
export const TRANSACTION_ERROR = 'transactionError' |
||||
export const TRANSACTION_ERROR_KEY = 'transactionError' |
||||
|
@ -0,0 +1,36 @@ |
||||
'use strict' |
||||
|
||||
import DetectRTC from 'detectrtc' |
||||
const { ENVIRONMENT_TYPE_POPUP } = require('../../app/scripts/lib/enums') |
||||
const { getEnvironmentType, getPlatform } = require('../../app/scripts/lib/util') |
||||
const { PLATFORM_BRAVE, PLATFORM_FIREFOX } = require('../../app/scripts/lib/enums') |
||||
|
||||
class WebcamUtils { |
||||
|
||||
static checkStatus () { |
||||
return new Promise((resolve, reject) => { |
||||
const isPopup = getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP |
||||
const isFirefoxOrBrave = getPlatform() === (PLATFORM_FIREFOX || PLATFORM_BRAVE) |
||||
try { |
||||
DetectRTC.load(_ => { |
||||
if (DetectRTC.hasWebcam) { |
||||
let environmentReady = true |
||||
if ((isFirefoxOrBrave && isPopup) || (isPopup && !DetectRTC.isWebsiteHasWebcamPermissions)) { |
||||
environmentReady = false |
||||
} |
||||
resolve({ |
||||
permissions: DetectRTC.isWebsiteHasWebcamPermissions, |
||||
environmentReady, |
||||
}) |
||||
} else { |
||||
reject({type: 'NO_WEBCAM_FOUND'}) |
||||
} |
||||
}) |
||||
} catch (e) { |
||||
reject({type: 'UNKNOWN_ERROR'}) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
module.exports = WebcamUtils |
Loading…
Reference in new issue