mirror of
https://github.com/trezor/trezor-wallet
synced 2024-11-15 21:08:57 +00:00
Merge branch 'master' of github.com:satoshilabs/trezor-wallet
This commit is contained in:
commit
6f0d7988a6
@ -10,6 +10,7 @@ import * as PENDING from 'actions/constants/pendingTx';
|
||||
import * as WALLET from 'actions/constants/wallet';
|
||||
import { httpRequest } from 'utils/networkUtils';
|
||||
import * as buildUtils from 'utils/build';
|
||||
import * as storageUtils from 'utils/storage';
|
||||
|
||||
import { findAccountTokens } from 'reducers/TokensReducer';
|
||||
import type { Account } from 'reducers/AccountsReducer';
|
||||
@ -17,7 +18,6 @@ import type { Token } from 'reducers/TokensReducer';
|
||||
import type { PendingTx } from 'reducers/PendingTxReducer';
|
||||
import type { Discovery } from 'reducers/DiscoveryReducer';
|
||||
|
||||
|
||||
import type {
|
||||
TrezorDevice,
|
||||
ThunkAction,
|
||||
@ -43,22 +43,15 @@ export type StorageAction = {
|
||||
error: string,
|
||||
};
|
||||
|
||||
const get = (key: string): ?string => {
|
||||
try {
|
||||
return window.localStorage.getItem(key);
|
||||
} catch (error) {
|
||||
// available = false;
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const set = (key: string, value: string | boolean): void => {
|
||||
try {
|
||||
window.localStorage.setItem(key, value);
|
||||
} catch (error) {
|
||||
console.error(`Local Storage ERROR: ${error}`);
|
||||
}
|
||||
};
|
||||
const TYPE: 'local' = 'local';
|
||||
const { STORAGE_PATH } = storageUtils;
|
||||
const KEY_VERSION: string = `${STORAGE_PATH}version`;
|
||||
const KEY_DEVICES: string = `${STORAGE_PATH}devices`;
|
||||
const KEY_ACCOUNTS: string = `${STORAGE_PATH}accounts`;
|
||||
const KEY_DISCOVERY: string = `${STORAGE_PATH}discovery`;
|
||||
const KEY_TOKENS: string = `${STORAGE_PATH}tokens`;
|
||||
const KEY_PENDING: string = `${STORAGE_PATH}pending`;
|
||||
const KEY_BETA_MODAL: string = '/betaModalPrivacy'; // this key needs to be compatible with "parent" (old) wallet
|
||||
|
||||
// https://github.com/STRML/react-localstorage/blob/master/react-localstorage.js
|
||||
// or
|
||||
@ -80,25 +73,25 @@ export const save = (): ThunkAction => (dispatch: Dispatch, getState: GetState):
|
||||
const discovery: Array<Discovery> = findDiscovery(devices, getState().discovery);
|
||||
|
||||
// save devices
|
||||
set('devices', JSON.stringify(devices));
|
||||
storageUtils.set(TYPE, KEY_DEVICES, JSON.stringify(devices));
|
||||
|
||||
// save already preloaded accounts
|
||||
set('accounts', JSON.stringify(accounts));
|
||||
storageUtils.set(TYPE, KEY_ACCOUNTS, JSON.stringify(accounts));
|
||||
|
||||
// save discovery state
|
||||
set('discovery', JSON.stringify(discovery));
|
||||
storageUtils.set(TYPE, KEY_DISCOVERY, JSON.stringify(discovery));
|
||||
|
||||
// tokens
|
||||
set('tokens', JSON.stringify(tokens));
|
||||
storageUtils.set(TYPE, KEY_TOKENS, JSON.stringify(tokens));
|
||||
|
||||
// pending transactions
|
||||
set('pending', JSON.stringify(pending));
|
||||
storageUtils.set(TYPE, KEY_PENDING, JSON.stringify(pending));
|
||||
};
|
||||
|
||||
export const update = (event: StorageEvent): ThunkAction => (dispatch: Dispatch): void => {
|
||||
if (!event.newValue) return;
|
||||
|
||||
if (event.key === 'devices') {
|
||||
if (event.key === KEY_DEVICES) {
|
||||
// check if device was added/ removed
|
||||
// const newDevices: Array<TrezorDevice> = JSON.parse(event.newValue);
|
||||
// const myDevices: Array<TrezorDevice> = getState().connect.devices.filter(d => d.features);
|
||||
@ -113,28 +106,28 @@ export const update = (event: StorageEvent): ThunkAction => (dispatch: Dispatch)
|
||||
// const diff = oldDevices.filter(d => newDevices.indexOf())
|
||||
}
|
||||
|
||||
if (event.key === 'accounts') {
|
||||
if (event.key === KEY_ACCOUNTS) {
|
||||
dispatch({
|
||||
type: ACCOUNT.FROM_STORAGE,
|
||||
payload: JSON.parse(event.newValue),
|
||||
});
|
||||
}
|
||||
|
||||
if (event.key === 'tokens') {
|
||||
if (event.key === KEY_TOKENS) {
|
||||
dispatch({
|
||||
type: TOKEN.FROM_STORAGE,
|
||||
payload: JSON.parse(event.newValue),
|
||||
});
|
||||
}
|
||||
|
||||
if (event.key === 'pending') {
|
||||
if (event.key === KEY_PENDING) {
|
||||
dispatch({
|
||||
type: PENDING.FROM_STORAGE,
|
||||
payload: JSON.parse(event.newValue),
|
||||
});
|
||||
}
|
||||
|
||||
if (event.key === 'discovery') {
|
||||
if (event.key === KEY_DISCOVERY) {
|
||||
dispatch({
|
||||
type: DISCOVERY.FROM_STORAGE,
|
||||
payload: JSON.parse(event.newValue),
|
||||
@ -142,14 +135,13 @@ export const update = (event: StorageEvent): ThunkAction => (dispatch: Dispatch)
|
||||
}
|
||||
};
|
||||
|
||||
const VERSION: string = '1';
|
||||
|
||||
const loadJSON = (): AsyncAction => async (dispatch: Dispatch): Promise<void> => {
|
||||
if (typeof window.localStorage === 'undefined') return;
|
||||
|
||||
try {
|
||||
const config: Config = await httpRequest(AppConfigJSON, 'json');
|
||||
|
||||
// remove ropsten testnet from config networks
|
||||
if (!buildUtils.isDev()) {
|
||||
const index = config.networks.findIndex(c => c.shortcut === 'trop');
|
||||
delete config.networks[index];
|
||||
@ -161,18 +153,6 @@ const loadJSON = (): AsyncAction => async (dispatch: Dispatch): Promise<void> =>
|
||||
dispatch(update(event));
|
||||
});
|
||||
|
||||
// validate version
|
||||
const version: ?string = get('version');
|
||||
if (version !== VERSION) {
|
||||
try {
|
||||
window.localStorage.clear();
|
||||
window.sessionStorage.clear();
|
||||
} catch (error) {
|
||||
// empty
|
||||
}
|
||||
set('version', VERSION);
|
||||
}
|
||||
|
||||
// load tokens
|
||||
const tokens = await config.networks.reduce(async (promise: Promise<TokensCollection>, network: Network): Promise<TokensCollection> => {
|
||||
const collection: TokensCollection = await promise;
|
||||
@ -195,9 +175,17 @@ const loadJSON = (): AsyncAction => async (dispatch: Dispatch): Promise<void> =>
|
||||
}
|
||||
};
|
||||
|
||||
const VERSION: string = '1';
|
||||
|
||||
const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||
const devices: ?string = get('devices');
|
||||
// validate version
|
||||
const version: ?string = storageUtils.get(TYPE, KEY_VERSION);
|
||||
if (version && version !== VERSION) {
|
||||
storageUtils.clearAll();
|
||||
}
|
||||
storageUtils.set(TYPE, KEY_VERSION, VERSION);
|
||||
|
||||
const devices: ?string = storageUtils.get(TYPE, KEY_DEVICES);
|
||||
if (devices) {
|
||||
dispatch({
|
||||
type: CONNECT.DEVICE_FROM_STORAGE,
|
||||
@ -205,7 +193,7 @@ const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||
});
|
||||
}
|
||||
|
||||
const accounts: ?string = get('accounts');
|
||||
const accounts: ?string = storageUtils.get(TYPE, KEY_ACCOUNTS);
|
||||
if (accounts) {
|
||||
dispatch({
|
||||
type: ACCOUNT.FROM_STORAGE,
|
||||
@ -213,7 +201,7 @@ const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||
});
|
||||
}
|
||||
|
||||
const userTokens: ?string = get('tokens');
|
||||
const userTokens: ?string = storageUtils.get(TYPE, KEY_TOKENS);
|
||||
if (userTokens) {
|
||||
dispatch({
|
||||
type: TOKEN.FROM_STORAGE,
|
||||
@ -221,7 +209,7 @@ const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||
});
|
||||
}
|
||||
|
||||
const pending: ?string = get('pending');
|
||||
const pending: ?string = storageUtils.get(TYPE, KEY_PENDING);
|
||||
if (pending) {
|
||||
dispatch({
|
||||
type: PENDING.FROM_STORAGE,
|
||||
@ -229,7 +217,7 @@ const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||
});
|
||||
}
|
||||
|
||||
const discovery: ?string = get('discovery');
|
||||
const discovery: ?string = storageUtils.get(TYPE, KEY_DISCOVERY);
|
||||
if (discovery) {
|
||||
dispatch({
|
||||
type: DISCOVERY.FROM_STORAGE,
|
||||
@ -238,7 +226,7 @@ const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||
}
|
||||
|
||||
if (buildUtils.isDev() || buildUtils.isBeta()) {
|
||||
const betaModal = get('/betaModalPrivacy');
|
||||
const betaModal = Object.keys(window.localStorage).find(key => key.indexOf(KEY_BETA_MODAL) >= 0);
|
||||
if (!betaModal) {
|
||||
dispatch({
|
||||
type: WALLET.SHOW_BETA_DISCLAIMER,
|
||||
@ -258,6 +246,6 @@ export const loadData = (): ThunkAction => (dispatch: Dispatch, getState: GetSta
|
||||
};
|
||||
|
||||
export const hideBetaDisclaimer = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||
set('/betaModalPrivacy', true);
|
||||
storageUtils.set(TYPE, KEY_BETA_MODAL, true);
|
||||
dispatch(loadJSON());
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
import * as storageUtils from 'utils/storage';
|
||||
|
||||
import type { State as SendFormState } from 'reducers/SendFormReducer';
|
||||
import type {
|
||||
@ -9,52 +9,39 @@ import type {
|
||||
Dispatch,
|
||||
} from 'flowtype';
|
||||
|
||||
const TX_PREFIX: string = 'trezor:draft-tx:';
|
||||
const TYPE: 'session' = 'session';
|
||||
const { STORAGE_PATH } = storageUtils;
|
||||
const KEY_TX_DRAFT: string = `${STORAGE_PATH}txdraft`;
|
||||
|
||||
const getTxDraftKey = (getState: GetState): string => {
|
||||
const { pathname } = getState().router.location;
|
||||
return `${KEY_TX_DRAFT}${pathname}`;
|
||||
};
|
||||
|
||||
export const saveDraftTransaction = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||
if (typeof window.localStorage === 'undefined') return;
|
||||
|
||||
const state = getState().sendForm;
|
||||
if (state.untouched) return;
|
||||
|
||||
const location = getState().router.location.pathname;
|
||||
try {
|
||||
// save state as it is
|
||||
// "loadDraftTransaction" will do the validation
|
||||
window.sessionStorage.setItem(`${TX_PREFIX}${location}`, JSON.stringify(state));
|
||||
} catch (error) {
|
||||
console.error(`Saving sessionStorage error: ${error}`);
|
||||
}
|
||||
const key = getTxDraftKey(getState);
|
||||
storageUtils.set(TYPE, key, JSON.stringify(state));
|
||||
};
|
||||
|
||||
export const loadDraftTransaction = (): PayloadAction<?SendFormState> => (dispatch: Dispatch, getState: GetState): ?SendFormState => {
|
||||
if (typeof window.localStorage === 'undefined') return null;
|
||||
|
||||
try {
|
||||
const location = getState().router.location.pathname;
|
||||
const value: string = window.sessionStorage.getItem(`${TX_PREFIX}${location}`);
|
||||
const state: ?SendFormState = JSON.parse(value);
|
||||
if (state) {
|
||||
// decide if draft is valid and should be returned
|
||||
// ignore this draft if has any error
|
||||
if (Object.keys(state.errors).length > 0) {
|
||||
window.sessionStorage.removeItem(`${TX_PREFIX}${location}`);
|
||||
return null;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Loading sessionStorage error: ${error}`);
|
||||
const key = getTxDraftKey(getState);
|
||||
const value: ?string = storageUtils.get(TYPE, key);
|
||||
if (!value) return null;
|
||||
const state: ?SendFormState = JSON.parse(value);
|
||||
if (!state) return null;
|
||||
// decide if draft is valid and should be returned
|
||||
// ignore this draft if has any error
|
||||
if (Object.keys(state.errors).length > 0) {
|
||||
storageUtils.remove(TYPE, key);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
return state;
|
||||
};
|
||||
|
||||
export const clear = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||
if (typeof window.localStorage === 'undefined') return;
|
||||
const location = getState().router.location.pathname;
|
||||
try {
|
||||
window.sessionStorage.removeItem(`${TX_PREFIX}${location}`);
|
||||
} catch (error) {
|
||||
console.error(`Clearing sessionStorage error: ${error}`);
|
||||
}
|
||||
};
|
||||
const key = getTxDraftKey(getState);
|
||||
storageUtils.remove(TYPE, key);
|
||||
};
|
||||
|
@ -34,7 +34,7 @@ exports[`eth utils sanitizeHex 3`] = `"0x02"`;
|
||||
|
||||
exports[`eth utils sanitizeHex 4`] = `"0x0100"`;
|
||||
|
||||
exports[`eth utils sanitizeHex 5`] = `null`;
|
||||
exports[`eth utils sanitizeHex 5`] = `"0x0999"`;
|
||||
|
||||
exports[`eth utils sanitizeHex 6`] = `""`;
|
||||
|
||||
|
@ -40,9 +40,9 @@ describe('device utils', () => {
|
||||
|
||||
it('isWebUSB', () => {
|
||||
const data = [
|
||||
{ transport: { version: 'webusb' } },
|
||||
{ transport: { version: 'aaaaaa' } },
|
||||
{ transport: { version: 'webusb' } },
|
||||
{ transport: { type: 'ParallelTransport', version: 'webusb' } },
|
||||
{ transport: { type: null, version: 'aaaaaa' } },
|
||||
{ transport: { type: 'ParallelTransport', version: 'webusb' } },
|
||||
];
|
||||
|
||||
data.forEach((item) => {
|
||||
|
62
src/utils/storage.js
Normal file
62
src/utils/storage.js
Normal file
@ -0,0 +1,62 @@
|
||||
/* @flow */
|
||||
|
||||
// Copy-paste from mytrezor (old wallet)
|
||||
// https://github.com/satoshilabs/mytrezor/blob/develop/app/scripts/storage/BackendLocalStorage.js
|
||||
export const getStoragePath = (): string => {
|
||||
const regexHash = /^([^#]*)#.*$/;
|
||||
const path = window.location.href.replace(regexHash, '$1');
|
||||
const regexStart = /^[^:]*:\/\/[^/]*\//;
|
||||
return path.replace(regexStart, '/');
|
||||
};
|
||||
|
||||
export const STORAGE_PATH: string = getStoragePath();
|
||||
|
||||
type StorageType = 'local' | 'session';
|
||||
|
||||
export const get: <T>(type: StorageType, key: string) => T | null = (type, key) => {
|
||||
const storage = type === 'local' ? window.localStorage : window.sessionStorage;
|
||||
try {
|
||||
return storage.getItem(key);
|
||||
} catch (error) {
|
||||
console.warn(`Get ${type} storage: ${error}`);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const set = (type: StorageType, key: string, value: string | number | boolean): void => {
|
||||
const storage = type === 'local' ? window.localStorage : window.sessionStorage;
|
||||
try {
|
||||
storage.setItem(key, value);
|
||||
} catch (error) {
|
||||
console.warn(`Save ${type} storage: ${error}`);
|
||||
}
|
||||
};
|
||||
|
||||
export const remove = (type: StorageType, key: string): void => {
|
||||
const storage = type === 'local' ? window.localStorage : window.sessionStorage;
|
||||
try {
|
||||
storage.removeItem(key);
|
||||
} catch (error) {
|
||||
console.warn(`Remove ${type} storage: ${error}`);
|
||||
}
|
||||
};
|
||||
|
||||
export const clearAll = (type: ?StorageType): void => {
|
||||
let clearLocal: boolean = true;
|
||||
let clearSession: boolean = true;
|
||||
if (typeof type === 'string') {
|
||||
clearLocal = type === 'local';
|
||||
clearSession = !clearLocal;
|
||||
}
|
||||
|
||||
try {
|
||||
if (clearLocal) {
|
||||
Object.keys(window.localStorage).forEach(key => key.indexOf(STORAGE_PATH) >= 0 && window.localStorage.removeItem(key));
|
||||
}
|
||||
if (clearSession) {
|
||||
Object.keys(window.sessionStorage).forEach(key => key.indexOf(STORAGE_PATH) >= 0 && window.sessionStorage.removeItem(key));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Clearing sessionStorage error: ${error}`);
|
||||
}
|
||||
};
|
@ -78,7 +78,7 @@ const AddAccountIconWrapper = styled.div`
|
||||
const DiscoveryStatusWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
width: 100%;
|
||||
font-size: ${FONT_SIZE.SMALL};
|
||||
padding: ${LEFT_NAVIGATION_ROW.PADDING};
|
||||
white-space: nowrap;
|
||||
@ -164,15 +164,34 @@ const AccountMenu = (props: Props) => {
|
||||
let discoveryStatus = null;
|
||||
const discovery = props.discovery.find(d => d.deviceState === selected.state && d.network === location.state.network);
|
||||
|
||||
if (discovery) {
|
||||
if (discovery.completed) {
|
||||
// TODO: add only if last one is not empty
|
||||
//if (selectedAccounts.length > 0 && selectedAccounts[selectedAccounts.length - 1])
|
||||
const lastAccount = deviceAccounts[deviceAccounts.length - 1];
|
||||
if (lastAccount && (new BigNumber(lastAccount.balance).greaterThan(0) || lastAccount.nonce > 0)) {
|
||||
discoveryStatus = (
|
||||
<Row onClick={props.addAccount}>
|
||||
<RowAddAccountWrapper>
|
||||
if (discovery && discovery.completed) {
|
||||
// TODO: add only if last one is not empty
|
||||
//if (selectedAccounts.length > 0 && selectedAccounts[selectedAccounts.length - 1])
|
||||
const lastAccount = deviceAccounts[deviceAccounts.length - 1];
|
||||
if (lastAccount && (new BigNumber(lastAccount.balance).greaterThan(0) || lastAccount.nonce > 0)) {
|
||||
discoveryStatus = (
|
||||
<Row onClick={props.addAccount}>
|
||||
<RowAddAccountWrapper>
|
||||
<AddAccountIconWrapper>
|
||||
<Icon
|
||||
icon={ICONS.PLUS}
|
||||
size={24}
|
||||
color={colors.TEXT_SECONDARY}
|
||||
/>
|
||||
</AddAccountIconWrapper>
|
||||
Add account
|
||||
</RowAddAccountWrapper>
|
||||
</Row>
|
||||
);
|
||||
} else {
|
||||
discoveryStatus = (
|
||||
<Tooltip
|
||||
maxWidth={200}
|
||||
content={<TooltipContent>To add a new account, last account must have some transactions.</TooltipContent>}
|
||||
placement="bottom"
|
||||
>
|
||||
<Row>
|
||||
<RowAddAccountWrapper disabled>
|
||||
<AddAccountIconWrapper>
|
||||
<Icon
|
||||
icon={ICONS.PLUS}
|
||||
@ -183,53 +202,33 @@ const AccountMenu = (props: Props) => {
|
||||
Add account
|
||||
</RowAddAccountWrapper>
|
||||
</Row>
|
||||
);
|
||||
} else {
|
||||
discoveryStatus = (
|
||||
<Tooltip
|
||||
maxWidth={200}
|
||||
content={<TooltipContent>To add a new account, last account must have some transactions.</TooltipContent>}
|
||||
placement="bottom"
|
||||
>
|
||||
<Row>
|
||||
<RowAddAccountWrapper disabled>
|
||||
<AddAccountIconWrapper>
|
||||
<Icon
|
||||
icon={ICONS.PLUS}
|
||||
size={24}
|
||||
color={colors.TEXT_SECONDARY}
|
||||
/>
|
||||
</AddAccountIconWrapper>
|
||||
Add account
|
||||
</RowAddAccountWrapper>
|
||||
</Row>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
} else if (!selected.connected || !selected.available) {
|
||||
discoveryStatus = (
|
||||
<Row>
|
||||
<DiscoveryStatusWrapper>
|
||||
Accounts could not be loaded
|
||||
<DiscoveryStatusText>
|
||||
{`Connect ${selected.instanceLabel} device`}
|
||||
</DiscoveryStatusText>
|
||||
</DiscoveryStatusWrapper>
|
||||
</Row>
|
||||
);
|
||||
} else {
|
||||
discoveryStatus = (
|
||||
<Row>
|
||||
<DiscoveryLoadingWrapper>
|
||||
<Loader size={20} />
|
||||
<DiscoveryLoadingText>
|
||||
Loading...
|
||||
</DiscoveryLoadingText>
|
||||
</DiscoveryLoadingWrapper>
|
||||
</Row>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
} else if (!selected.connected) {
|
||||
discoveryStatus = (
|
||||
<Row>
|
||||
<DiscoveryStatusWrapper>
|
||||
Accounts could not be loaded
|
||||
<DiscoveryStatusText>
|
||||
{`Connect ${selected.instanceLabel} device`}
|
||||
</DiscoveryStatusText>
|
||||
</DiscoveryStatusWrapper>
|
||||
</Row>
|
||||
);
|
||||
} else {
|
||||
discoveryStatus = (
|
||||
<Row>
|
||||
<DiscoveryLoadingWrapper>
|
||||
<Loader size={20} />
|
||||
<DiscoveryLoadingText>
|
||||
Loading...
|
||||
</DiscoveryLoadingText>
|
||||
</DiscoveryLoadingWrapper>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
|
@ -6,7 +6,7 @@ import React, { PureComponent } from 'react';
|
||||
|
||||
type Props = {
|
||||
pathname: string;
|
||||
wrapper: ?HTMLElement;
|
||||
wrapper: () => ?HTMLElement;
|
||||
}
|
||||
|
||||
type State = {
|
||||
@ -71,8 +71,8 @@ class Indicator extends PureComponent<Props, State> {
|
||||
handleResize: () => void;
|
||||
|
||||
reposition(resetAnimation: boolean = true) {
|
||||
if (!this.props.wrapper) return;
|
||||
const { wrapper } = this.props;
|
||||
const wrapper = this.props.wrapper();
|
||||
if (!wrapper) return;
|
||||
const active = wrapper.querySelector('.active');
|
||||
if (!active) return;
|
||||
const bounds = active.getBoundingClientRect();
|
||||
|
@ -65,7 +65,7 @@ class TopNavigationAccount extends React.PureComponent<Props> {
|
||||
<StyledNavLink to={`${basePath}/receive`}>Receive</StyledNavLink>
|
||||
<StyledNavLink to={`${basePath}/send`}>Send</StyledNavLink>
|
||||
{/* <StyledNavLink to={`${basePath}/signverify`}>Sign & Verify</StyledNavLink> */}
|
||||
<Indicator pathname={pathname} wrapper={this.wrapper} />
|
||||
<Indicator pathname={pathname} wrapper={() => this.wrapper} />
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user