Merge pull request #12584 from MetaMask/Version-v10.5.0
Version v10.5.0 RCfeature/default_network_editable
commit
263e80da5e
@ -0,0 +1,37 @@ |
||||
import { cloneDeep } from 'lodash'; |
||||
import { LEDGER_TRANSPORT_TYPES } from '../../../shared/constants/hardware-wallets'; |
||||
|
||||
const version = 66; |
||||
|
||||
/** |
||||
* Changes the useLedgerLive boolean property to the ledgerTransportType enum |
||||
*/ |
||||
export default { |
||||
version, |
||||
async migrate(originalVersionedData) { |
||||
const versionedData = cloneDeep(originalVersionedData); |
||||
versionedData.meta.version = version; |
||||
const state = versionedData.data; |
||||
const newState = transformState(state); |
||||
versionedData.data = newState; |
||||
return versionedData; |
||||
}, |
||||
}; |
||||
|
||||
function transformState(state) { |
||||
const defaultTransportType = window.navigator.hid |
||||
? LEDGER_TRANSPORT_TYPES.WEBHID |
||||
: LEDGER_TRANSPORT_TYPES.U2F; |
||||
const useLedgerLive = Boolean(state.PreferencesController?.useLedgerLive); |
||||
const newState = { |
||||
...state, |
||||
PreferencesController: { |
||||
...state?.PreferencesController, |
||||
ledgerTransportType: useLedgerLive |
||||
? LEDGER_TRANSPORT_TYPES.LIVE |
||||
: defaultTransportType, |
||||
}, |
||||
}; |
||||
delete newState.PreferencesController.useLedgerLive; |
||||
return newState; |
||||
} |
@ -0,0 +1,116 @@ |
||||
import { LEDGER_TRANSPORT_TYPES } from '../../../shared/constants/hardware-wallets'; |
||||
import migration66 from './066'; |
||||
|
||||
describe('migration #66', () => { |
||||
beforeEach(() => { |
||||
jest.resetAllMocks(); |
||||
}); |
||||
|
||||
it('should update the version metadata', async () => { |
||||
const oldStorage = { |
||||
meta: { |
||||
version: 65, |
||||
}, |
||||
data: {}, |
||||
}; |
||||
|
||||
const newStorage = await migration66.migrate(oldStorage); |
||||
expect(newStorage.meta).toStrictEqual({ |
||||
version: 66, |
||||
}); |
||||
}); |
||||
|
||||
it('should set ledgerTransportType to `u2f` if no preferences controller exists and webhid is not available', async () => { |
||||
const oldStorage = { |
||||
meta: {}, |
||||
data: {}, |
||||
}; |
||||
|
||||
const newStorage = await migration66.migrate(oldStorage); |
||||
expect( |
||||
newStorage.data.PreferencesController.ledgerTransportType, |
||||
).toStrictEqual(LEDGER_TRANSPORT_TYPES.U2F); |
||||
}); |
||||
|
||||
it('should set ledgerTransportType to `u2f` if no useLedgerLive property exists and webhid is not available', async () => { |
||||
const oldStorage = { |
||||
meta: {}, |
||||
data: { |
||||
PreferencesController: {}, |
||||
}, |
||||
}; |
||||
|
||||
const newStorage = await migration66.migrate(oldStorage); |
||||
expect( |
||||
newStorage.data.PreferencesController.ledgerTransportType, |
||||
).toStrictEqual(LEDGER_TRANSPORT_TYPES.U2F); |
||||
}); |
||||
|
||||
it('should set ledgerTransportType to `u2f` if useLedgerLive is false and webhid is not available', async () => { |
||||
const oldStorage = { |
||||
meta: {}, |
||||
data: { |
||||
PreferencesController: { |
||||
useLedgerLive: false, |
||||
}, |
||||
}, |
||||
}; |
||||
|
||||
const newStorage = await migration66.migrate(oldStorage); |
||||
expect( |
||||
newStorage.data.PreferencesController.ledgerTransportType, |
||||
).toStrictEqual(LEDGER_TRANSPORT_TYPES.U2F); |
||||
}); |
||||
|
||||
it('should set ledgerTransportType to `webhid` if useLedgerLive is false and webhid is available', async () => { |
||||
const oldStorage = { |
||||
meta: {}, |
||||
data: { |
||||
PreferencesController: { |
||||
useLedgerLive: false, |
||||
}, |
||||
}, |
||||
}; |
||||
jest |
||||
.spyOn(window, 'navigator', 'get') |
||||
.mockImplementation(() => ({ hid: true })); |
||||
const newStorage = await migration66.migrate(oldStorage); |
||||
expect( |
||||
newStorage.data.PreferencesController.ledgerTransportType, |
||||
).toStrictEqual(LEDGER_TRANSPORT_TYPES.WEBHID); |
||||
}); |
||||
|
||||
it('should set ledgerTransportType to `ledgerLive` if useLedgerLive is true', async () => { |
||||
const oldStorage = { |
||||
meta: {}, |
||||
data: { |
||||
PreferencesController: { |
||||
useLedgerLive: true, |
||||
}, |
||||
}, |
||||
}; |
||||
|
||||
const newStorage = await migration66.migrate(oldStorage); |
||||
expect( |
||||
newStorage.data.PreferencesController.ledgerTransportType, |
||||
).toStrictEqual('ledgerLive'); |
||||
}); |
||||
|
||||
it('should not change ledgerTransportType if useLedgerLive is true and webhid is available', async () => { |
||||
const oldStorage = { |
||||
meta: {}, |
||||
data: { |
||||
PreferencesController: { |
||||
useLedgerLive: true, |
||||
}, |
||||
}, |
||||
}; |
||||
jest |
||||
.spyOn(window, 'navigator', 'get') |
||||
.mockImplementation(() => ({ hid: true })); |
||||
const newStorage = await migration66.migrate(oldStorage); |
||||
expect( |
||||
newStorage.data.PreferencesController.ledgerTransportType, |
||||
).toStrictEqual(LEDGER_TRANSPORT_TYPES.LIVE); |
||||
}); |
||||
}); |
@ -0,0 +1 @@ |
||||
export { default } from './ledger-instruction-field'; |
@ -0,0 +1,212 @@ |
||||
import React, { useEffect } from 'react'; |
||||
import { useDispatch, useSelector } from 'react-redux'; |
||||
import PropTypes from 'prop-types'; |
||||
import { |
||||
LEDGER_TRANSPORT_TYPES, |
||||
LEDGER_USB_VENDOR_ID, |
||||
WEBHID_CONNECTED_STATUSES, |
||||
TRANSPORT_STATES, |
||||
} from '../../../../shared/constants/hardware-wallets'; |
||||
import { |
||||
PLATFORM_FIREFOX, |
||||
ENVIRONMENT_TYPE_FULLSCREEN, |
||||
} from '../../../../shared/constants/app'; |
||||
|
||||
import { |
||||
setLedgerWebHidConnectedStatus, |
||||
getLedgerWebHidConnectedStatus, |
||||
setLedgerTransportStatus, |
||||
getLedgerTransportStatus, |
||||
} from '../../../ducks/app/app'; |
||||
|
||||
import Typography from '../../ui/typography/typography'; |
||||
import Button from '../../ui/button'; |
||||
import { useI18nContext } from '../../../hooks/useI18nContext'; |
||||
import { |
||||
COLORS, |
||||
FONT_WEIGHT, |
||||
TYPOGRAPHY, |
||||
} from '../../../helpers/constants/design-system'; |
||||
import Dialog from '../../ui/dialog'; |
||||
import { |
||||
getPlatform, |
||||
getEnvironmentType, |
||||
} from '../../../../app/scripts/lib/util'; |
||||
import { getLedgerTransportType } from '../../../ducks/metamask/metamask'; |
||||
import { attemptLedgerTransportCreation } from '../../../store/actions'; |
||||
|
||||
const renderInstructionStep = (text, show = true, color = COLORS.PRIMARY3) => { |
||||
return ( |
||||
show && ( |
||||
<Typography |
||||
boxProps={{ margin: 0 }} |
||||
color={color} |
||||
fontWeight={FONT_WEIGHT.BOLD} |
||||
variant={TYPOGRAPHY.H7} |
||||
> |
||||
{text} |
||||
</Typography> |
||||
) |
||||
); |
||||
}; |
||||
|
||||
export default function LedgerInstructionField({ showDataInstruction }) { |
||||
const t = useI18nContext(); |
||||
const dispatch = useDispatch(); |
||||
|
||||
const webHidConnectedStatus = useSelector(getLedgerWebHidConnectedStatus); |
||||
const ledgerTransportType = useSelector(getLedgerTransportType); |
||||
const transportStatus = useSelector(getLedgerTransportStatus); |
||||
const environmentType = getEnvironmentType(); |
||||
const environmentTypeIsFullScreen = |
||||
environmentType === ENVIRONMENT_TYPE_FULLSCREEN; |
||||
|
||||
useEffect(() => { |
||||
const initialConnectedDeviceCheck = async () => { |
||||
if ( |
||||
ledgerTransportType === LEDGER_TRANSPORT_TYPES.WEBHID && |
||||
webHidConnectedStatus !== WEBHID_CONNECTED_STATUSES.CONNECTED |
||||
) { |
||||
const devices = await window.navigator.hid.getDevices(); |
||||
const webHidIsConnected = devices.some( |
||||
(device) => device.vendorId === Number(LEDGER_USB_VENDOR_ID), |
||||
); |
||||
dispatch( |
||||
setLedgerWebHidConnectedStatus( |
||||
webHidIsConnected |
||||
? WEBHID_CONNECTED_STATUSES.CONNECTED |
||||
: WEBHID_CONNECTED_STATUSES.NOT_CONNECTED, |
||||
), |
||||
); |
||||
} |
||||
}; |
||||
const determineTransportStatus = async () => { |
||||
if ( |
||||
ledgerTransportType === LEDGER_TRANSPORT_TYPES.WEBHID && |
||||
webHidConnectedStatus === WEBHID_CONNECTED_STATUSES.CONNECTED && |
||||
transportStatus === TRANSPORT_STATES.NONE |
||||
) { |
||||
try { |
||||
const transportedCreated = await attemptLedgerTransportCreation(); |
||||
dispatch( |
||||
setLedgerTransportStatus( |
||||
transportedCreated |
||||
? TRANSPORT_STATES.VERIFIED |
||||
: TRANSPORT_STATES.UNKNOWN_FAILURE, |
||||
), |
||||
); |
||||
} catch (e) { |
||||
if (e.message.match('Failed to open the device')) { |
||||
dispatch( |
||||
setLedgerTransportStatus(TRANSPORT_STATES.DEVICE_OPEN_FAILURE), |
||||
); |
||||
} else if (e.message.match('the device is already open')) { |
||||
dispatch(setLedgerTransportStatus(TRANSPORT_STATES.VERIFIED)); |
||||
} else { |
||||
dispatch( |
||||
setLedgerTransportStatus(TRANSPORT_STATES.UNKNOWN_FAILURE), |
||||
); |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
determineTransportStatus(); |
||||
initialConnectedDeviceCheck(); |
||||
}, [dispatch, ledgerTransportType, webHidConnectedStatus, transportStatus]); |
||||
|
||||
useEffect(() => { |
||||
return () => { |
||||
dispatch(setLedgerTransportStatus(TRANSPORT_STATES.NONE)); |
||||
}; |
||||
}, [dispatch]); |
||||
|
||||
const usingLedgerLive = ledgerTransportType === LEDGER_TRANSPORT_TYPES.LIVE; |
||||
const usingWebHID = ledgerTransportType === LEDGER_TRANSPORT_TYPES.WEBHID; |
||||
|
||||
const isFirefox = getPlatform() === PLATFORM_FIREFOX; |
||||
|
||||
return ( |
||||
<div> |
||||
<div className="confirm-detail-row"> |
||||
<Dialog type="message"> |
||||
<div className="ledger-live-dialog"> |
||||
{renderInstructionStep(t('ledgerConnectionInstructionHeader'))} |
||||
{renderInstructionStep( |
||||
`- ${t('ledgerConnectionInstructionStepOne')}`, |
||||
!isFirefox && usingLedgerLive, |
||||
)} |
||||
{renderInstructionStep( |
||||
`- ${t('ledgerConnectionInstructionStepTwo')}`, |
||||
!isFirefox && usingLedgerLive, |
||||
)} |
||||
{renderInstructionStep( |
||||
`- ${t('ledgerConnectionInstructionStepThree')}`, |
||||
)} |
||||
{renderInstructionStep( |
||||
`- ${t('ledgerConnectionInstructionStepFour')}`, |
||||
showDataInstruction, |
||||
)} |
||||
{renderInstructionStep( |
||||
<span> |
||||
<Button |
||||
type="link" |
||||
onClick={async () => { |
||||
if (environmentTypeIsFullScreen) { |
||||
window.location.reload(); |
||||
} else { |
||||
global.platform.openExtensionInBrowser(null, null, true); |
||||
} |
||||
}} |
||||
> |
||||
{t('ledgerConnectionInstructionCloseOtherApps')} |
||||
</Button> |
||||
</span>, |
||||
transportStatus === TRANSPORT_STATES.DEVICE_OPEN_FAILURE, |
||||
)} |
||||
{renderInstructionStep( |
||||
<span> |
||||
<Button |
||||
type="link" |
||||
onClick={async () => { |
||||
if (environmentTypeIsFullScreen) { |
||||
const connectedDevices = await window.navigator.hid.requestDevice( |
||||
{ |
||||
filters: [{ vendorId: LEDGER_USB_VENDOR_ID }], |
||||
}, |
||||
); |
||||
const webHidIsConnected = connectedDevices.some( |
||||
(device) => |
||||
device.vendorId === Number(LEDGER_USB_VENDOR_ID), |
||||
); |
||||
dispatch( |
||||
setLedgerWebHidConnectedStatus({ |
||||
webHidConnectedStatus: webHidIsConnected |
||||
? WEBHID_CONNECTED_STATUSES.CONNECTED |
||||
: WEBHID_CONNECTED_STATUSES.NOT_CONNECTED, |
||||
}), |
||||
); |
||||
} else { |
||||
global.platform.openExtensionInBrowser(null, null, true); |
||||
} |
||||
}} |
||||
> |
||||
{environmentTypeIsFullScreen |
||||
? t('clickToConnectLedgerViaWebHID') |
||||
: t('openFullScreenForLedgerWebHid')} |
||||
</Button> |
||||
</span>, |
||||
usingWebHID && |
||||
webHidConnectedStatus === |
||||
WEBHID_CONNECTED_STATUSES.NOT_CONNECTED, |
||||
COLORS.SECONDARY1, |
||||
)} |
||||
</div> |
||||
</Dialog> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
LedgerInstructionField.propTypes = { |
||||
showDataInstruction: PropTypes.bool, |
||||
}; |
Loading…
Reference in new issue