(Fix) Enforce linting and formatting (#564)
* (remove) unused / old packages / linting script * (add) eslint / prettier packages and plugins * (fix) eslint / prettier config * (add) prettier / eslint scripts * (fix) linting and formatting problems * (remove) unused files * (fix) linting problem that needed to be addressed manually * (add) pre commit hooks * (fix) lint-staged config * (fix) config * (fix) config * (check) if pre config formatting works * Add nvmrc and update yarn.lock * Update `yarn.lock` * Make jsx-a11y/no-autofocus report as warn * Apply lint:fix to project Co-authored-by: Fernando <fernando.greco@gmail.com>
This commit is contained in:
parent
dff0f0bd4d
commit
6df5e72135
|
@ -1,7 +1,15 @@
|
|||
node_modules
|
||||
!.eslintrc.js
|
||||
build_webpack
|
||||
config
|
||||
contracts
|
||||
flow-typed
|
||||
flow-typed/npm
|
||||
config
|
||||
scripts
|
||||
migrations
|
||||
node_modules
|
||||
public
|
||||
scripts
|
||||
src/assets
|
||||
src/config
|
||||
test
|
||||
*.spec*
|
||||
*.test*
|
133
.eslintrc
133
.eslintrc
|
@ -1,82 +1,57 @@
|
|||
{
|
||||
"settings": {
|
||||
"react": {
|
||||
"flowVersion": "0.112.0",
|
||||
"pragma": "React",
|
||||
"version": "16.12.0"
|
||||
},
|
||||
"import/extensions": [".js", ".jsx"]
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"plugins": ["react", "flowtype", "import", "jsx-a11y", "prettier"],
|
||||
"extends": [
|
||||
"airbnb",
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:flowtype/recommended",
|
||||
"plugin:import/errors",
|
||||
"plugin:import/warnings",
|
||||
"plugin:jsx-a11y/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
"prettier/react"
|
||||
],
|
||||
"parser": "babel-eslint",
|
||||
"plugins": [
|
||||
"jsx-a11y",
|
||||
"jest",
|
||||
"flowtype"
|
||||
"prettier/react",
|
||||
"prettier/flowtype"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"amd": true,
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"rules": {
|
||||
"react/jsx-filename-extension": [
|
||||
1,
|
||||
{
|
||||
"extensions": [
|
||||
".js",
|
||||
".jsx"
|
||||
]
|
||||
}
|
||||
],
|
||||
"react/forbid-prop-types": [
|
||||
1,
|
||||
{
|
||||
"forbid": [
|
||||
"object",
|
||||
"any"
|
||||
]
|
||||
}
|
||||
],
|
||||
"class-methods-use-this": 0,
|
||||
"semi": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"consistent-return": "off",
|
||||
"max-len": [
|
||||
"error",
|
||||
120,
|
||||
2,
|
||||
{
|
||||
"ignoreUrls": true,
|
||||
"ignoreComments": false,
|
||||
"ignoreRegExpLiterals": true,
|
||||
"ignoreStrings": true,
|
||||
"ignoreTemplateLiterals": true
|
||||
}
|
||||
],
|
||||
"import/no-unresolved": 0,
|
||||
"import/no-extraneous-dependencies": 0,
|
||||
"import/extensions": 0,
|
||||
"import/prefer-default-export": 0,
|
||||
"react/default-props-match-prop-types": [
|
||||
"error",
|
||||
{
|
||||
"allowRequiredDefaults": true
|
||||
}
|
||||
],
|
||||
// https://github.com/yannickcr/eslint-plugin-react/issues/1593 ^
|
||||
"jsx-a11y/label-has-for": 0,
|
||||
"indent": [
|
||||
"error",
|
||||
2,
|
||||
{
|
||||
"SwitchCase": 1
|
||||
"ignoreTemplateLiterals": true,
|
||||
"ignoreUrls": true
|
||||
}
|
||||
],
|
||||
"no-console": [
|
||||
"error",
|
||||
{
|
||||
"allow": [
|
||||
"warn",
|
||||
"error"
|
||||
]
|
||||
"allow": ["warn", "error"]
|
||||
}
|
||||
],
|
||||
"semi": ["error", "never"],
|
||||
"flowtype/require-valid-file-annotation": [
|
||||
2,
|
||||
"always",
|
||||
|
@ -84,32 +59,36 @@
|
|||
"annotationStyle": "line"
|
||||
}
|
||||
],
|
||||
"flowtype/generic-spacing": 0,
|
||||
"import/extensions": 0,
|
||||
"import/no-extraneous-dependencies": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/prefer-default-export": 0,
|
||||
"jsx-a11y/anchor-is-valid": [
|
||||
"error",
|
||||
{
|
||||
"components": [
|
||||
"Link"
|
||||
],
|
||||
"specialLink": [
|
||||
"to",
|
||||
"hrefLeft",
|
||||
"hrefRight"
|
||||
],
|
||||
"aspects": [
|
||||
"noHref",
|
||||
"invalidHref",
|
||||
"preferButton"
|
||||
]
|
||||
"components": ["Link"],
|
||||
"specialLink": ["to", "hrefLeft", "hrefRight"],
|
||||
"aspects": ["noHref", "invalidHref", "preferButton"]
|
||||
}
|
||||
],
|
||||
"react/default-props-match-prop-types": ["error", { "allowRequiredDefaults": true }],
|
||||
"react/forbid-prop-types": [
|
||||
1,
|
||||
{
|
||||
"forbid": ["object", "any"]
|
||||
}
|
||||
],
|
||||
"react/require-default-props": 0,
|
||||
"react/no-array-index-key": 0,
|
||||
"react/require-default-props": 0,
|
||||
"react/state-in-constructor": 0,
|
||||
"react/jsx-filename-extension": [
|
||||
1,
|
||||
{
|
||||
"extensions": [".js", ".jsx"]
|
||||
}
|
||||
],
|
||||
"react/jsx-props-no-spreading": 0,
|
||||
"react/state-in-constructor": 0
|
||||
},
|
||||
"env": {
|
||||
"jest/globals": true,
|
||||
"browser": true
|
||||
"prettier/prettier": "error",
|
||||
"jsx-a11y/no-autofocus": "warn"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
!.eslintrc.js
|
||||
build_webpack
|
||||
config
|
||||
contracts
|
||||
flow-typed
|
||||
flow-typed/npm
|
||||
migrations
|
||||
node_modules
|
||||
public
|
||||
scripts
|
||||
src/assets
|
||||
src/config
|
||||
test
|
||||
*.spec*
|
||||
*.test*
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
}
|
||||
"printWidth": 120,
|
||||
"trailingComma": "all",
|
||||
"singleQuote": true,
|
||||
"semi": false
|
||||
}
|
||||
|
|
39
package.json
39
package.json
|
@ -19,19 +19,27 @@
|
|||
"build": "REACT_APP_APP_VERSION=$npm_package_version node scripts/build.js",
|
||||
"build-mainnet": "REACT_APP_NETWORK=mainnet yarn build",
|
||||
"flow": "flow",
|
||||
"precommit": "./precommit.sh",
|
||||
"format:staged": "lint-staged",
|
||||
"lint:check": "eslint './src/**/*.{js,jsx}'",
|
||||
"lint:fix": "yarn lint:check --fix",
|
||||
"prettier": "prettier './src/**/*.{js,jsx}'",
|
||||
"prettier:check": "yarn prettier --check",
|
||||
"prettier:fix": "yarn prettier --write",
|
||||
"start": "node scripts/start.js",
|
||||
"start-mainnet": "REACT_APP_NETWORK=mainnet yarn start",
|
||||
"test": "NODE_ENV=test && node scripts/test.js --env=jsdom",
|
||||
"format": "prettier-eslint $PWD'/src/**/*.{js,jsx}' --write",
|
||||
"format:staged": "lint-staged"
|
||||
"test": "NODE_ENV=test && node scripts/test.js --env=jsdom"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged --allow-empty"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"./src/**/*.{js,jsx}": "prettier-eslint --write"
|
||||
"src/**/*.{js,jsx}": [
|
||||
"eslint --fix",
|
||||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"pre-commit": [
|
||||
"precommit"
|
||||
],
|
||||
"dependencies": {
|
||||
"@gnosis.pm/safe-contracts": "1.1.1-dev.1",
|
||||
"@gnosis.pm/util-contracts": "2.0.6",
|
||||
|
@ -124,15 +132,13 @@
|
|||
"css-loader": "3.4.0",
|
||||
"detect-port": "^1.3.0",
|
||||
"dotenv-expand": "^5.1.0",
|
||||
"eslint": "6.7.2",
|
||||
"eslint-config-airbnb": "18.0.1",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-prettier": "^6.10.0",
|
||||
"eslint-plugin-flowtype": "4.5.2",
|
||||
"eslint-plugin-import": "2.19.1",
|
||||
"eslint-plugin-jest": "23.1.1",
|
||||
"eslint-plugin-jsx-a11y": "6.2.3",
|
||||
"eslint-plugin-flowtype": "^4.6.0",
|
||||
"eslint-plugin-import": "^2.20.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.2.3",
|
||||
"eslint-plugin-prettier": "^3.1.2",
|
||||
"eslint-plugin-react": "7.17.0",
|
||||
"eslint-plugin-react": "^7.18.3",
|
||||
"ethereumjs-abi": "0.6.8",
|
||||
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
||||
"file-loader": "5.0.2",
|
||||
|
@ -140,6 +146,7 @@
|
|||
"fs-extra": "8.1.0",
|
||||
"html-loader": "^0.5.5",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"husky": "^4.2.2",
|
||||
"jest": "24.9.0",
|
||||
"jest-dom": "4.0.0",
|
||||
"json-loader": "^0.5.7",
|
||||
|
@ -147,9 +154,7 @@
|
|||
"postcss-loader": "^3.0.0",
|
||||
"postcss-mixins": "6.2.3",
|
||||
"postcss-simple-vars": "^5.0.2",
|
||||
"pre-commit": "^1.2.2",
|
||||
"prettier": "^1.19.1",
|
||||
"prettier-eslint-cli": "5.0.0",
|
||||
"run-with-testrpc": "0.3.1",
|
||||
"style-loader": "1.0.2",
|
||||
"terser-webpack-plugin": "2.3.1",
|
||||
|
|
41
precommit.sh
41
precommit.sh
|
@ -1,41 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep ".jsx\{0,1\}$")
|
||||
ESLINT="$(git rev-parse --show-toplevel)/node_modules/.bin/eslint"
|
||||
|
||||
if [[ "$STAGED_FILES" = "" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
PASS=true
|
||||
|
||||
echo -e "\nValidating Javascript:\n"
|
||||
|
||||
# Check for eslint
|
||||
if [[ ! -x "$ESLINT" ]]; then
|
||||
echo -e "\t\033[41mPlease install ESlint\033[0m (npm i --save-dev eslint)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for FILE in $STAGED_FILES
|
||||
do
|
||||
"$ESLINT" "$FILE" "--fix"
|
||||
|
||||
if [[ "$?" == 0 ]]; then
|
||||
echo -e "\t\033[32mESLint Passed: $FILE\033[0m"
|
||||
else
|
||||
echo -e "\t\033[41mESLint Failed: $FILE\033[0m"
|
||||
PASS=false
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "\nJavascript validation completed!\n"
|
||||
|
||||
if ! $PASS; then
|
||||
echo -e "\033[0;31mCOMMIT FAILED, PLEASE FIX ESLINT ERRORS:\033[0m Your commit contains files that should pass ESLint but do not. Please fix the ESLint errors in a new commit.\n"
|
||||
exit 1
|
||||
else
|
||||
echo -e "\033[42mCOMMIT SUCCEEDED\033[0m\n"
|
||||
fi
|
||||
|
||||
exit $?
|
|
@ -40,10 +40,7 @@ const List = ({ items, activeItem, onItemClick, classes }: Props) => {
|
|||
{items.map(i => (
|
||||
<Item
|
||||
key={i.id}
|
||||
className={cn(
|
||||
classes.menuOption,
|
||||
activeItem === i.id && classes.active
|
||||
)}
|
||||
className={cn(classes.menuOption, activeItem === i.id && classes.active)}
|
||||
onClick={() => onItemClick(i.id)}
|
||||
>
|
||||
<div className="container">
|
||||
|
|
|
@ -35,7 +35,7 @@ const useStyles = makeStyles(() =>
|
|||
height: 'auto',
|
||||
position: 'static',
|
||||
},
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
type Props = {
|
||||
|
@ -49,13 +49,7 @@ const GenericModal = ({ title, body, footer, onClose }: Props) => {
|
|||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="GenericModal"
|
||||
description="GenericModal"
|
||||
handleClose={onClose}
|
||||
paperClassName={classes.paper}
|
||||
open
|
||||
>
|
||||
<Modal title="GenericModal" description="GenericModal" handleClose={onClose} paperClassName={classes.paper} open>
|
||||
<TitleSection>
|
||||
{title}
|
||||
<IconButton onClick={onClose} disableRipple>
|
||||
|
|
|
@ -20,13 +20,7 @@ const TitleWrapper = styled.div`
|
|||
align-items: center;
|
||||
`
|
||||
|
||||
export const ModalTitle = ({
|
||||
title,
|
||||
iconUrl,
|
||||
}: {
|
||||
title: string,
|
||||
iconUrl: string,
|
||||
}) => {
|
||||
export const ModalTitle = ({ title, iconUrl }: { title: string, iconUrl: string }) => {
|
||||
return (
|
||||
<TitleWrapper>
|
||||
{iconUrl && <IconImg src={iconUrl} alt={title} />}
|
||||
|
@ -58,12 +52,7 @@ export const ModalFooterConfirmation = ({
|
|||
<Button minWidth={130} onClick={handleCancel}>
|
||||
{cancelText}
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
minWidth={130}
|
||||
onClick={handleOk}
|
||||
variant="contained"
|
||||
>
|
||||
<Button color="primary" minWidth={130} onClick={handleOk} variant="contained">
|
||||
{okText}
|
||||
</Button>
|
||||
</FooterWrapper>
|
||||
|
|
|
@ -143,14 +143,12 @@ const CookiesBanner = () => {
|
|||
</span>
|
||||
<div className={classes.content}>
|
||||
<p className={classes.text}>
|
||||
We use cookies to give you the best experience and to help improve our
|
||||
website. Please read our{' '}
|
||||
We use cookies to give you the best experience and to help improve our website. Please read our{' '}
|
||||
<Link className={classes.link} to="https://safe.gnosis.io/cookie">
|
||||
Cookie Policy
|
||||
</Link>{' '}
|
||||
for more information. By clicking "Accept all", you agree to
|
||||
the storing of cookies on your device to enhance site navigation,
|
||||
analyze site usage and provide customer support.
|
||||
for more information. By clicking "Accept all", you agree to the storing of cookies on your device
|
||||
to enhance site navigation, analyze site usage and provide customer support.
|
||||
</p>
|
||||
<div className={classes.form}>
|
||||
<div className={classes.formItem}>
|
||||
|
|
|
@ -33,9 +33,7 @@ type EtherscanBtnProps = {
|
|||
value: string,
|
||||
}
|
||||
|
||||
const EtherscanBtn = ({
|
||||
type, value, className, increaseZindex = false,
|
||||
}: EtherscanBtnProps) => {
|
||||
const EtherscanBtn = ({ type, value, className, increaseZindex = false }: EtherscanBtnProps) => {
|
||||
const classes = useStyles()
|
||||
const customClasses = increaseZindex ? { popper: classes.increasedPopperZindex } : {}
|
||||
|
||||
|
|
|
@ -18,14 +18,9 @@ type EtherscanLinkProps = {
|
|||
value: string,
|
||||
}
|
||||
|
||||
const EtherscanLink = ({
|
||||
type, value, cut, classes, knownAddress,
|
||||
}: EtherscanLinkProps) => (
|
||||
const EtherscanLink = ({ type, value, cut, classes, knownAddress }: EtherscanLinkProps) => (
|
||||
<Block className={classes.etherscanLink}>
|
||||
<Span
|
||||
className={cn(knownAddress && classes.addressParagraph, classes.address)}
|
||||
size="md"
|
||||
>
|
||||
<Span className={cn(knownAddress && classes.addressParagraph, classes.address)} size="md">
|
||||
{cut ? shortVersionOf(value, cut) : value}
|
||||
</Span>
|
||||
<CopyBtn className={cn(classes.button, classes.firstButton)} content={value} />
|
||||
|
|
|
@ -42,9 +42,7 @@ const useStyles = makeStyles({
|
|||
},
|
||||
})
|
||||
|
||||
const appVersion = process.env.REACT_APP_APP_VERSION
|
||||
? `v${process.env.REACT_APP_APP_VERSION} `
|
||||
: 'Versions'
|
||||
const appVersion = process.env.REACT_APP_APP_VERSION ? `v${process.env.REACT_APP_APP_VERSION} ` : 'Versions'
|
||||
|
||||
const Footer = () => {
|
||||
const date = new Date()
|
||||
|
@ -59,50 +57,27 @@ const Footer = () => {
|
|||
<footer className={classes.footer}>
|
||||
<span className={classes.item}>©{date.getFullYear()} Gnosis</span>
|
||||
<span className={classes.sep}>|</span>
|
||||
<Link
|
||||
className={cn(classes.item, classes.link)}
|
||||
to="https://safe.gnosis.io/terms"
|
||||
target="_blank"
|
||||
>
|
||||
<Link className={cn(classes.item, classes.link)} to="https://safe.gnosis.io/terms" target="_blank">
|
||||
Terms
|
||||
</Link>
|
||||
<span className={classes.sep}>|</span>
|
||||
<Link
|
||||
className={cn(classes.item, classes.link)}
|
||||
to="https://safe.gnosis.io/privacy"
|
||||
target="_blank"
|
||||
>
|
||||
<Link className={cn(classes.item, classes.link)} to="https://safe.gnosis.io/privacy" target="_blank">
|
||||
Privacy
|
||||
</Link>
|
||||
<span className={classes.sep}>|</span>
|
||||
<Link
|
||||
className={cn(classes.item, classes.link)}
|
||||
to="https://safe.gnosis.io/licenses"
|
||||
target="_blank"
|
||||
>
|
||||
<Link className={cn(classes.item, classes.link)} to="https://safe.gnosis.io/licenses" target="_blank">
|
||||
Licenses
|
||||
</Link>
|
||||
<span className={classes.sep}>|</span>
|
||||
<Link
|
||||
className={cn(classes.item, classes.link)}
|
||||
to="https://safe.gnosis.io/imprint"
|
||||
target="_blank"
|
||||
>
|
||||
<Link className={cn(classes.item, classes.link)} to="https://safe.gnosis.io/imprint" target="_blank">
|
||||
Imprint
|
||||
</Link>
|
||||
<span className={classes.sep}>|</span>
|
||||
<Link
|
||||
className={cn(classes.item, classes.link)}
|
||||
to="https://safe.gnosis.io/cookie"
|
||||
target="_blank"
|
||||
>
|
||||
<Link className={cn(classes.item, classes.link)} to="https://safe.gnosis.io/cookie" target="_blank">
|
||||
Cookie Policy
|
||||
</Link>
|
||||
<span className={classes.sep}>-</span>
|
||||
<GnoButtonLink
|
||||
className={cn(classes.item, classes.link, classes.buttonLink)}
|
||||
onClick={openCookiesHandler}
|
||||
>
|
||||
<GnoButtonLink className={cn(classes.item, classes.link, classes.buttonLink)} onClick={openCookiesHandler}>
|
||||
Preferences
|
||||
</GnoButtonLink>
|
||||
<span className={classes.sep}>|</span>
|
||||
|
|
|
@ -54,12 +54,7 @@ const buildKeyStyleFrom = (size: number, center: boolean, dotSize: number) => ({
|
|||
borderRadius: `${size}px`,
|
||||
})
|
||||
|
||||
const buildDotStyleFrom = (
|
||||
size: number,
|
||||
top: number,
|
||||
right: number,
|
||||
mode: Mode
|
||||
) => ({
|
||||
const buildDotStyleFrom = (size: number, top: number, right: number, mode: Mode) => ({
|
||||
width: `${size}px`,
|
||||
height: `${size}px`,
|
||||
borderRadius: `${size}px`,
|
||||
|
|
|
@ -62,55 +62,42 @@ const styles = () => ({
|
|||
},
|
||||
})
|
||||
|
||||
const Layout = openHoc(
|
||||
({
|
||||
open,
|
||||
toggle,
|
||||
clickAway,
|
||||
classes,
|
||||
providerInfo,
|
||||
providerDetails,
|
||||
}: Props) => (
|
||||
<Row className={classes.summary}>
|
||||
<Col start="xs" middle="xs" className={classes.logo}>
|
||||
<Link to="/">
|
||||
<Img src={logo} height={32} alt="Gnosis Team Safe" />
|
||||
</Link>
|
||||
</Col>
|
||||
<Divider />
|
||||
<SafeListHeader />
|
||||
<Divider />
|
||||
<NetworkLabel />
|
||||
<Spacer />
|
||||
<Provider open={open} toggle={toggle} info={providerInfo}>
|
||||
{providerRef => (
|
||||
<Popper
|
||||
anchorEl={providerRef.current}
|
||||
className={classes.popper}
|
||||
open={open}
|
||||
placement="bottom"
|
||||
popperOptions={{ positionFixed: true }}
|
||||
>
|
||||
{({ TransitionProps }) => (
|
||||
<Grow {...TransitionProps}>
|
||||
<>
|
||||
<ClickAwayListener
|
||||
onClickAway={clickAway}
|
||||
mouseEvent="onClick"
|
||||
touchEvent={false}
|
||||
>
|
||||
<List className={classes.root} component="div">
|
||||
{providerDetails}
|
||||
</List>
|
||||
</ClickAwayListener>
|
||||
</>
|
||||
</Grow>
|
||||
)}
|
||||
</Popper>
|
||||
)}
|
||||
</Provider>
|
||||
</Row>
|
||||
)
|
||||
)
|
||||
const Layout = openHoc(({ open, toggle, clickAway, classes, providerInfo, providerDetails }: Props) => (
|
||||
<Row className={classes.summary}>
|
||||
<Col start="xs" middle="xs" className={classes.logo}>
|
||||
<Link to="/">
|
||||
<Img src={logo} height={32} alt="Gnosis Team Safe" />
|
||||
</Link>
|
||||
</Col>
|
||||
<Divider />
|
||||
<SafeListHeader />
|
||||
<Divider />
|
||||
<NetworkLabel />
|
||||
<Spacer />
|
||||
<Provider open={open} toggle={toggle} info={providerInfo}>
|
||||
{providerRef => (
|
||||
<Popper
|
||||
anchorEl={providerRef.current}
|
||||
className={classes.popper}
|
||||
open={open}
|
||||
placement="bottom"
|
||||
popperOptions={{ positionFixed: true }}
|
||||
>
|
||||
{({ TransitionProps }) => (
|
||||
<Grow {...TransitionProps}>
|
||||
<>
|
||||
<ClickAwayListener onClickAway={clickAway} mouseEvent="onClick" touchEvent={false}>
|
||||
<List className={classes.root} component="div">
|
||||
{providerDetails}
|
||||
</List>
|
||||
</ClickAwayListener>
|
||||
</>
|
||||
</Grow>
|
||||
)}
|
||||
</Popper>
|
||||
)}
|
||||
</Provider>
|
||||
</Row>
|
||||
))
|
||||
|
||||
export default withStyles(styles)(Layout)
|
||||
|
|
|
@ -7,8 +7,7 @@ import Col from '~/components/layout/Col'
|
|||
import { xs, sm, md, border, screenSm } from '~/theme/variables'
|
||||
|
||||
const network = getNetwork()
|
||||
const formattedNetwork =
|
||||
network[0].toUpperCase() + network.substring(1).toLowerCase()
|
||||
const formattedNetwork = network[0].toUpperCase() + network.substring(1).toLowerCase()
|
||||
|
||||
const useStyles = makeStyles({
|
||||
container: {
|
||||
|
|
|
@ -62,12 +62,7 @@ class Provider extends React.Component<Props> {
|
|||
<>
|
||||
<div ref={this.myRef} className={classes.root}>
|
||||
<Divider />
|
||||
<Col
|
||||
end="sm"
|
||||
middle="xs"
|
||||
className={classes.provider}
|
||||
onClick={toggle}
|
||||
>
|
||||
<Col end="sm" middle="xs" className={classes.provider} onClick={toggle}>
|
||||
{info}
|
||||
<IconButton disableRipple className={classes.expand}>
|
||||
{open ? <ExpandLess /> : <ExpandMore />}
|
||||
|
|
|
@ -13,9 +13,7 @@ import Img from '~/components/layout/Img'
|
|||
import Row from '~/components/layout/Row'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Spacer from '~/components/Spacer'
|
||||
import {
|
||||
xs, sm, md, lg, background, warning, connected as connectedBg,
|
||||
} from '~/theme/variables'
|
||||
import { xs, sm, md, lg, background, warning, connected as connectedBg } from '~/theme/variables'
|
||||
import { upperFirst } from '~/utils/css'
|
||||
import { shortVersionOf } from '~/logic/wallets/ethAddresses'
|
||||
import CircleDot from '~/components/Header/components/CircleDot'
|
||||
|
@ -93,9 +91,7 @@ const styles = () => ({
|
|||
},
|
||||
})
|
||||
|
||||
const UserDetails = ({
|
||||
provider, connected, network, userAddress, classes, onDisconnect,
|
||||
}: Props) => {
|
||||
const UserDetails = ({ provider, connected, network, userAddress, classes, onDisconnect }: Props) => {
|
||||
const status = connected ? 'Connected' : 'Connection error'
|
||||
const address = userAddress ? shortVersionOf(userAddress, 4) : 'Address not available'
|
||||
const identiconAddress = userAddress || 'random'
|
||||
|
|
|
@ -54,17 +54,9 @@ const styles = () => ({
|
|||
},
|
||||
})
|
||||
|
||||
const ProviderInfo = ({
|
||||
provider,
|
||||
network,
|
||||
userAddress,
|
||||
connected,
|
||||
classes,
|
||||
}: Props) => {
|
||||
const ProviderInfo = ({ provider, network, userAddress, connected, classes }: Props) => {
|
||||
const providerText = `${provider} [${network}]`
|
||||
const cutAddress = connected
|
||||
? shortVersionOf(userAddress, 4)
|
||||
: 'Connection Error'
|
||||
const cutAddress = connected ? shortVersionOf(userAddress, 4) : 'Connection Error'
|
||||
const color = connected ? 'primary' : 'warning'
|
||||
const identiconAddress = userAddress || 'random'
|
||||
|
||||
|
@ -72,32 +64,13 @@ const ProviderInfo = ({
|
|||
<>
|
||||
{connected && (
|
||||
<>
|
||||
<Identicon
|
||||
className={classes.identicon}
|
||||
address={identiconAddress}
|
||||
diameter={30}
|
||||
/>
|
||||
<Identicon className={classes.identicon} address={identiconAddress} diameter={30} />
|
||||
<Dot className={classes.dot} />
|
||||
</>
|
||||
)}
|
||||
{!connected && (
|
||||
<CircleDot
|
||||
keySize={14}
|
||||
circleSize={35}
|
||||
dotSize={16}
|
||||
dotTop={24}
|
||||
dotRight={11}
|
||||
mode="warning"
|
||||
/>
|
||||
)}
|
||||
{!connected && <CircleDot keySize={14} circleSize={35} dotSize={16} dotTop={24} dotRight={11} mode="warning" />}
|
||||
<Col start="sm" layout="column" className={classes.account}>
|
||||
<Paragraph
|
||||
size="xs"
|
||||
transform="capitalize"
|
||||
className={classes.network}
|
||||
noMargin
|
||||
weight="bolder"
|
||||
>
|
||||
<Paragraph size="xs" transform="capitalize" className={classes.network} noMargin weight="bolder">
|
||||
{providerText}
|
||||
</Paragraph>
|
||||
<Paragraph size="xs" className={classes.address} noMargin color={color}>
|
||||
|
|
|
@ -32,22 +32,9 @@ const styles = () => ({
|
|||
|
||||
const ProviderDisconnected = ({ classes }: Props) => (
|
||||
<>
|
||||
<CircleDot
|
||||
keySize={17}
|
||||
circleSize={35}
|
||||
dotSize={16}
|
||||
dotTop={24}
|
||||
dotRight={11}
|
||||
mode="error"
|
||||
/>
|
||||
<CircleDot keySize={17} circleSize={35} dotSize={16} dotTop={24} dotRight={11} mode="error" />
|
||||
<Col end="sm" middle="xs" layout="column" className={classes.account}>
|
||||
<Paragraph
|
||||
size="sm"
|
||||
transform="capitalize"
|
||||
className={classes.network}
|
||||
noMargin
|
||||
weight="bold"
|
||||
>
|
||||
<Paragraph size="sm" transform="capitalize" className={classes.network} noMargin weight="bold">
|
||||
Not Connected
|
||||
</Paragraph>
|
||||
<Paragraph size="sm" color="fancy" className={classes.connect} noMargin>
|
||||
|
|
|
@ -65,5 +65,5 @@ const SafeListHeader = ({ safesCount }: Props) => {
|
|||
export default connect<Object, Object, ?Function, ?Object>(
|
||||
// $FlowFixMe
|
||||
state => ({ safesCount: safesCountSelector(state) }),
|
||||
null
|
||||
null,
|
||||
)(SafeListHeader)
|
||||
|
|
|
@ -36,10 +36,7 @@ class HeaderComponent extends React.PureComponent<Props, State> {
|
|||
|
||||
async componentDidMount() {
|
||||
const lastUsedProvider = await loadLastUsedProvider()
|
||||
if (
|
||||
INJECTED_PROVIDERS.includes(lastUsedProvider) ||
|
||||
process.env.NODE_ENV === 'test'
|
||||
) {
|
||||
if (INJECTED_PROVIDERS.includes(lastUsedProvider) || process.env.NODE_ENV === 'test') {
|
||||
web3Connect.connectToInjected()
|
||||
}
|
||||
}
|
||||
|
@ -48,11 +45,7 @@ class HeaderComponent extends React.PureComponent<Props, State> {
|
|||
const { enqueueSnackbar, closeSnackbar } = this.props
|
||||
|
||||
this.setState({ hasError: true })
|
||||
showSnackbar(
|
||||
NOTIFICATIONS.CONNECT_WALLET_ERROR_MSG,
|
||||
enqueueSnackbar,
|
||||
closeSnackbar
|
||||
)
|
||||
showSnackbar(NOTIFICATIONS.CONNECT_WALLET_ERROR_MSG, enqueueSnackbar, closeSnackbar)
|
||||
|
||||
logComponentStack(error, info)
|
||||
}
|
||||
|
@ -71,14 +64,7 @@ class HeaderComponent extends React.PureComponent<Props, State> {
|
|||
return <ProviderDisconnected />
|
||||
}
|
||||
|
||||
return (
|
||||
<ProviderAccessible
|
||||
provider={provider}
|
||||
network={network}
|
||||
userAddress={userAddress}
|
||||
connected={available}
|
||||
/>
|
||||
)
|
||||
return <ProviderAccessible provider={provider} network={network} userAddress={userAddress} connected={available} />
|
||||
}
|
||||
|
||||
getProviderDetailsBased = () => {
|
||||
|
|
|
@ -62,13 +62,10 @@ export default class Identicon extends React.PureComponent<Props> {
|
|||
return image
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const { diameter, className } = this.props
|
||||
const style = this.getStyleFrom(diameter)
|
||||
|
||||
return (
|
||||
<div className={className} style={style} ref={this.identicon} />
|
||||
)
|
||||
return <div className={className} style={style} ref={this.identicon} />
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,24 +20,16 @@ const styles = {
|
|||
|
||||
class ListItemText extends React.PureComponent<Props> {
|
||||
render() {
|
||||
const {
|
||||
primary, secondary, classes, cut = false,
|
||||
} = this.props
|
||||
const { primary, secondary, classes, cut = false } = this.props
|
||||
|
||||
const cutStyle = cut ? {
|
||||
secondary: classes.itemTextSecondary,
|
||||
} : undefined
|
||||
const cutStyle = cut
|
||||
? {
|
||||
secondary: classes.itemTextSecondary,
|
||||
}
|
||||
: undefined
|
||||
|
||||
return (
|
||||
<MuiListItemText
|
||||
classes={cutStyle}
|
||||
inset
|
||||
primary={primary}
|
||||
secondary={secondary}
|
||||
/>
|
||||
)
|
||||
return <MuiListItemText classes={cutStyle} inset primary={primary} secondary={secondary} />
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default withStyles(styles)(ListItemText)
|
||||
|
|
|
@ -40,7 +40,14 @@ const styles = () => ({
|
|||
})
|
||||
|
||||
const GnoModal = ({
|
||||
title, description, open, children, handleClose, modalClassName, classes, paperClassName,
|
||||
title,
|
||||
description,
|
||||
open,
|
||||
children,
|
||||
handleClose,
|
||||
modalClassName,
|
||||
classes,
|
||||
paperClassName,
|
||||
}: Props) => (
|
||||
<Modal
|
||||
aria-labelledby={title}
|
||||
|
|
|
@ -43,7 +43,7 @@ class Notifier extends Component<Props> {
|
|||
componentDidUpdate() {
|
||||
const { notifications = [], enqueueSnackbar, removeSnackbar } = this.props
|
||||
|
||||
notifications.forEach((notification) => {
|
||||
notifications.forEach(notification => {
|
||||
// Do nothing if snackbar is already displayed
|
||||
if (this.displayed.includes(notification.key)) {
|
||||
return
|
||||
|
@ -66,7 +66,7 @@ class Notifier extends Component<Props> {
|
|||
})
|
||||
}
|
||||
|
||||
storeDisplayed = (id) => {
|
||||
storeDisplayed = id => {
|
||||
this.displayed = [...this.displayed, id]
|
||||
}
|
||||
|
||||
|
|
|
@ -24,9 +24,7 @@ type Props = {
|
|||
isOpen: boolean,
|
||||
}
|
||||
|
||||
const ScanQRModal = ({
|
||||
classes, onClose, isOpen, onScan,
|
||||
}: Props) => {
|
||||
const ScanQRModal = ({ classes, onClose, isOpen, onScan }: Props) => {
|
||||
const [hasWebcam, setHasWebcam] = useState(null)
|
||||
const scannerRef: Object = React.createRef()
|
||||
const openImageDialog = () => {
|
||||
|
@ -73,10 +71,10 @@ const ScanQRModal = ({
|
|||
<QrReader
|
||||
ref={scannerRef}
|
||||
legacyMode={!hasWebcam}
|
||||
onScan={(data) => {
|
||||
onScan={data => {
|
||||
if (data) onScan(data)
|
||||
}}
|
||||
onError={(err) => {
|
||||
onError={err => {
|
||||
console.error(err)
|
||||
}}
|
||||
style={{ width: '400px', height: '400px' }}
|
||||
|
@ -85,12 +83,7 @@ const ScanQRModal = ({
|
|||
</Col>
|
||||
<Hairline />
|
||||
<Row align="center" className={classes.buttonRow}>
|
||||
<Button
|
||||
color="secondary"
|
||||
className={classes.button}
|
||||
minWidth={154}
|
||||
onClick={onClose}
|
||||
>
|
||||
<Button color="secondary" className={classes.button} minWidth={154} onClick={onClose}>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
// @flow
|
||||
import {
|
||||
lg, sm, background, secondaryText,
|
||||
} from '~/theme/variables'
|
||||
import { lg, sm, background, secondaryText } from '~/theme/variables'
|
||||
|
||||
export const styles = () => ({
|
||||
heading: {
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
// @flow
|
||||
navigator.getMedia = navigator.getUserMedia // use the proper vendor prefix
|
||||
|| navigator.webkitGetUserMedia
|
||||
|| navigator.mozGetUserMedia
|
||||
|| navigator.msGetUserMedia
|
||||
navigator.getMedia =
|
||||
navigator.getUserMedia || // use the proper vendor prefix
|
||||
navigator.webkitGetUserMedia ||
|
||||
navigator.mozGetUserMedia ||
|
||||
navigator.msGetUserMedia
|
||||
|
||||
export const checkWebcam = (success: Function, err: Function) => navigator.getMedia(
|
||||
{ video: true },
|
||||
() => {
|
||||
success()
|
||||
},
|
||||
() => {
|
||||
err()
|
||||
},
|
||||
)
|
||||
export const checkWebcam = (success: Function, err: Function) =>
|
||||
navigator.getMedia(
|
||||
{ video: true },
|
||||
() => {
|
||||
success()
|
||||
},
|
||||
() => {
|
||||
err()
|
||||
},
|
||||
)
|
||||
|
|
|
@ -72,13 +72,7 @@ const useStyles = makeStyles({
|
|||
},
|
||||
})
|
||||
|
||||
const SafeList = ({
|
||||
safes,
|
||||
onSafeClick,
|
||||
setDefaultSafe,
|
||||
defaultSafe,
|
||||
currentSafe,
|
||||
}: SafeListProps) => {
|
||||
const SafeList = ({ safes, onSafeClick, setDefaultSafe, defaultSafe, currentSafe }: SafeListProps) => {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
|
@ -99,11 +93,7 @@ const SafeList = ({
|
|||
<div className={classes.noIcon}>placeholder</div>
|
||||
)}
|
||||
<ListItemIcon>
|
||||
<Identicon
|
||||
address={safe.address}
|
||||
diameter={32}
|
||||
className={classes.icon}
|
||||
/>
|
||||
<Identicon address={safe.address} diameter={32} className={classes.icon} />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={safe.name}
|
||||
|
|
|
@ -14,10 +14,7 @@ import Hairline from '~/components/layout/Hairline'
|
|||
import Row from '~/components/layout/Row'
|
||||
import { WELCOME_ADDRESS } from '~/routes/routes'
|
||||
import { type Safe } from '~/routes/safe/store/models/safe'
|
||||
import {
|
||||
defaultSafeSelector,
|
||||
safeParamAddressFromStateSelector,
|
||||
} from '~/routes/safe/store/selectors'
|
||||
import { defaultSafeSelector, safeParamAddressFromStateSelector } from '~/routes/safe/store/selectors'
|
||||
import setDefaultSafe from '~/routes/safe/store/actions/setDefaultSafe'
|
||||
import { sortedSafeListSelector } from './selectors'
|
||||
import SafeList from './SafeList'
|
||||
|
@ -48,16 +45,10 @@ const filterBy = (filter: string, safes: List<Safe>): List<Safe> =>
|
|||
(safe: Safe) =>
|
||||
!filter ||
|
||||
safe.address.toLowerCase().includes(filter.toLowerCase()) ||
|
||||
safe.name.toLowerCase().includes(filter.toLowerCase())
|
||||
safe.name.toLowerCase().includes(filter.toLowerCase()),
|
||||
)
|
||||
|
||||
const Sidebar = ({
|
||||
children,
|
||||
safes,
|
||||
setDefaultSafeAction,
|
||||
defaultSafe,
|
||||
currentSafe,
|
||||
}: SidebarProps) => {
|
||||
const Sidebar = ({ children, safes, setDefaultSafeAction, defaultSafe, currentSafe }: SidebarProps) => {
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false)
|
||||
const [filter, setFilter] = useState<string>('')
|
||||
const classes = useSidebarStyles()
|
||||
|
@ -154,5 +145,5 @@ export default connect<Object, Object, ?Function, ?Object>(
|
|||
defaultSafe: defaultSafeSelector(state),
|
||||
currentSafe: safeParamAddressFromStateSelector(state),
|
||||
}),
|
||||
{ setDefaultSafeAction: setDefaultSafe }
|
||||
{ setDefaultSafeAction: setDefaultSafe },
|
||||
)(Sidebar)
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
// @flow
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import {
|
||||
xs,
|
||||
mediumFontSize,
|
||||
secondaryText,
|
||||
md,
|
||||
headerHeight,
|
||||
screenSm,
|
||||
} from '~/theme/variables'
|
||||
import { xs, mediumFontSize, secondaryText, md, headerHeight, screenSm } from '~/theme/variables'
|
||||
|
||||
const sidebarWidth = '400px'
|
||||
const sidebarMarginLeft = '12px'
|
||||
|
|
|
@ -9,6 +9,5 @@ const style = {
|
|||
flexGrow: 1,
|
||||
}
|
||||
|
||||
export default ({ className }: Props) => (
|
||||
<div className={className} style={style} />
|
||||
)
|
||||
// eslint-disable-next-line react/display-name
|
||||
export default ({ className }: Props) => <div className={className} style={style} />
|
||||
|
|
|
@ -30,15 +30,7 @@ type Props = {
|
|||
buttonLabels?: Array<string>,
|
||||
}
|
||||
|
||||
const Controls = ({
|
||||
onPrevious,
|
||||
firstPage,
|
||||
penultimate,
|
||||
lastPage,
|
||||
disabled,
|
||||
currentStep,
|
||||
buttonLabels,
|
||||
}: Props) => {
|
||||
const Controls = ({ onPrevious, firstPage, penultimate, lastPage, disabled, currentStep, buttonLabels }: Props) => {
|
||||
const back = firstPage ? 'Cancel' : 'Back'
|
||||
|
||||
let next
|
||||
|
|
|
@ -23,9 +23,7 @@ type Props = {
|
|||
padding?: boolean,
|
||||
}
|
||||
|
||||
const OpenPaper = ({
|
||||
classes, children, controls, padding = true,
|
||||
}: Props) => (
|
||||
const OpenPaper = ({ classes, children, controls, padding = true }: Props) => (
|
||||
<Paper className={classes.root} elevation={1}>
|
||||
<Block className={padding ? classes.padding : ''}>{children}</Block>
|
||||
{controls}
|
||||
|
|
|
@ -5,10 +5,6 @@ type Props = {
|
|||
children: React.Node,
|
||||
}
|
||||
|
||||
const Step = ({ children }: Props) => (
|
||||
<div>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
const Step = ({ children }: Props) => <div>{children}</div>
|
||||
|
||||
export default Step
|
||||
|
|
|
@ -52,7 +52,7 @@ const GnoStepper = (props: Props) => {
|
|||
|
||||
const getPageProps = (pages: React.Node): PageProps => React.Children.toArray(pages)[page].props
|
||||
|
||||
const updateInitialProps = (newInitialProps) => {
|
||||
const updateInitialProps = newInitialProps => {
|
||||
setValues(newInitialProps)
|
||||
}
|
||||
|
||||
|
@ -105,14 +105,12 @@ const GnoStepper = (props: Props) => {
|
|||
return next(formValues)
|
||||
}
|
||||
|
||||
const isLastPage = (pageNumber) => {
|
||||
const isLastPage = pageNumber => {
|
||||
const { steps } = props
|
||||
return pageNumber === steps.length - 1
|
||||
}
|
||||
|
||||
const {
|
||||
steps, children, classes, disabledWhenValidating = false, testId, mutators, buttonLabels,
|
||||
} = props
|
||||
const { steps, children, classes, disabledWhenValidating = false, testId, mutators, buttonLabels } = props
|
||||
const activePage = getActivePageFrom(children)
|
||||
|
||||
const lastPage = isLastPage(page)
|
||||
|
|
|
@ -57,9 +57,7 @@ class GnoTableHead extends React.PureComponent<Props> {
|
|||
sortDirection={orderBy === column.id ? order : false}
|
||||
>
|
||||
{column.static ? (
|
||||
<div style={column.style}>
|
||||
{column.label}
|
||||
</div>
|
||||
<div style={column.style}>{column.label}</div>
|
||||
) : (
|
||||
<TableSortLabel
|
||||
active={orderBy === column.id}
|
||||
|
|
|
@ -157,9 +157,7 @@ class GnoTable<K> extends React.Component<Props<K>, State> {
|
|||
noBorder,
|
||||
disableLoadingOnEmptyTable,
|
||||
} = this.props
|
||||
const {
|
||||
order, orderBy, page, orderProp, rowsPerPage, fixed,
|
||||
} = this.state
|
||||
const { order, orderBy, page, orderProp, rowsPerPage, fixed } = this.state
|
||||
const orderByParam = orderBy || defaultOrderBy
|
||||
const orderParam = order || defaultOrder
|
||||
const displayRows = rowsPerPage || defaultRowsPerPage
|
||||
|
@ -189,10 +187,7 @@ class GnoTable<K> extends React.Component<Props<K>, State> {
|
|||
</Table>
|
||||
)}
|
||||
{isEmpty && (
|
||||
<Row
|
||||
className={classes.loader}
|
||||
style={this.getEmptyStyle(emptyRows + 1)}
|
||||
>
|
||||
<Row className={classes.loader} style={this.getEmptyStyle(emptyRows + 1)}>
|
||||
<CircularProgress size={60} />
|
||||
</Row>
|
||||
)}
|
||||
|
|
|
@ -39,13 +39,14 @@ export const stableSort = (dataArray: List<any>, cmp: any, fixed: boolean): List
|
|||
return a[1] - b[1]
|
||||
})
|
||||
|
||||
const sortedElems: List<any> = stabilizedThis.map((el) => el[0])
|
||||
const sortedElems: List<any> = stabilizedThis.map(el => el[0])
|
||||
|
||||
return fixedElems.concat(sortedElems)
|
||||
}
|
||||
|
||||
export type Order = 'asc' | 'desc'
|
||||
|
||||
export const getSorting = (order: Order, orderBy: string, orderProp: boolean) => (order === 'desc'
|
||||
? (a: any, b: any) => desc(a, b, orderBy, orderProp)
|
||||
: (a: any, b: any) => -desc(a, b, orderBy, orderProp))
|
||||
export const getSorting = (order: Order, orderBy: string, orderProp: boolean) =>
|
||||
order === 'desc'
|
||||
? (a: any, b: any) => desc(a, b, orderBy, orderProp)
|
||||
: (a: any, b: any) => -desc(a, b, orderBy, orderProp)
|
||||
|
|
|
@ -3,11 +3,7 @@ import * as React from 'react'
|
|||
import { Field } from 'react-final-form'
|
||||
import { OnChange } from 'react-final-form-listeners'
|
||||
import TextField from '~/components/forms/TextField'
|
||||
import {
|
||||
composeValidators,
|
||||
required,
|
||||
mustBeEthereumAddress,
|
||||
} from '~/components/forms/validator'
|
||||
import { composeValidators, required, mustBeEthereumAddress } from '~/components/forms/validator'
|
||||
import { getAddressFromENS } from '~/logic/wallets/getWeb3'
|
||||
|
||||
type Props = {
|
||||
|
@ -23,7 +19,7 @@ type Props = {
|
|||
disabled?: boolean,
|
||||
}
|
||||
|
||||
const isValidEnsName = (name) => /^([\w-]+\.)+(eth|test|xyz|luxe)$/.test(name)
|
||||
const isValidEnsName = name => /^([\w-]+\.)+(eth|test|xyz|luxe)$/.test(name)
|
||||
|
||||
// an idea for second field was taken from here
|
||||
// https://github.com/final-form/react-final-form-listeners/blob/master/src/OnBlur.js
|
||||
|
@ -45,11 +41,7 @@ const AddressInput = ({
|
|||
name={name}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={composeValidators(
|
||||
required,
|
||||
mustBeEthereumAddress,
|
||||
...validators,
|
||||
)}
|
||||
validate={composeValidators(required, mustBeEthereumAddress, ...validators)}
|
||||
inputAdornment={inputAdornment}
|
||||
placeholder={placeholder}
|
||||
text={text}
|
||||
|
@ -59,7 +51,7 @@ const AddressInput = ({
|
|||
disabled={disabled}
|
||||
/>
|
||||
<OnChange name={name}>
|
||||
{async (value) => {
|
||||
{async value => {
|
||||
if (isValidEnsName(value)) {
|
||||
try {
|
||||
const resolverAddr = await getAddressFromENS(value)
|
||||
|
|
|
@ -5,22 +5,11 @@ import Checkbox, { type CheckoxProps } from '@material-ui/core/Checkbox'
|
|||
class GnoCheckbox extends React.PureComponent<CheckoxProps> {
|
||||
render() {
|
||||
const {
|
||||
input: {
|
||||
checked, name, onChange, ...restInput
|
||||
},
|
||||
meta,
|
||||
input: { checked, name, onChange, ...restInput },
|
||||
...rest
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<Checkbox
|
||||
{...rest}
|
||||
name={name}
|
||||
inputProps={restInput}
|
||||
onChange={onChange}
|
||||
checked={!!checked}
|
||||
/>
|
||||
)
|
||||
return <Checkbox {...rest} name={name} inputProps={restInput} onChange={onChange} checked={!!checked} />
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { Form } from 'react-final-form'
|
|||
export type OnSubmit = (
|
||||
values: Object,
|
||||
form: FormApi,
|
||||
callback: ?(errors: ?Object) => ?Object
|
||||
callback: ?(errors: ?Object) => ?Object,
|
||||
) => ?Object | Promise<?Object> | void
|
||||
|
||||
type Props = {
|
||||
|
@ -25,9 +25,7 @@ const stylesBasedOn = (padding: number): $Shape<CSSStyleDeclaration> => ({
|
|||
flex: '1 0 auto',
|
||||
})
|
||||
|
||||
const GnoForm = ({
|
||||
onSubmit, validation, initialValues, children, padding = 0, formMutators, testId = '',
|
||||
}: Props) => (
|
||||
const GnoForm = ({ onSubmit, validation, initialValues, children, padding = 0, formMutators, testId = '' }: Props) => (
|
||||
<Form
|
||||
validate={validation}
|
||||
onSubmit={onSubmit}
|
||||
|
|
|
@ -10,9 +10,7 @@ const style = {
|
|||
}
|
||||
|
||||
const SelectInput = ({
|
||||
input: {
|
||||
name, value, onChange, ...restInput
|
||||
},
|
||||
input: { name, value, onChange, ...restInput },
|
||||
meta,
|
||||
label,
|
||||
formControlProps,
|
||||
|
|
|
@ -21,11 +21,8 @@ const styles = () => ({
|
|||
class TextField extends React.PureComponent<TextFieldProps> {
|
||||
render() {
|
||||
const {
|
||||
input: {
|
||||
name, onChange, value, ...restInput
|
||||
},
|
||||
input: { name, onChange, value, ...restInput },
|
||||
meta,
|
||||
render,
|
||||
text,
|
||||
inputAdornment,
|
||||
classes,
|
||||
|
|
|
@ -23,13 +23,7 @@ const styles = () => ({
|
|||
})
|
||||
|
||||
const TextareaField = ({ classes, ...props }: TextFieldProps) => (
|
||||
<Field
|
||||
{...props}
|
||||
component={TextField}
|
||||
multiline
|
||||
rows="5"
|
||||
className={classes.textarea}
|
||||
/>
|
||||
<Field {...props} component={TextField} multiline rows="5" className={classes.textarea} />
|
||||
)
|
||||
|
||||
export default withStyles(styles)(TextareaField)
|
||||
|
|
|
@ -20,7 +20,8 @@ type Field = boolean | string | null | typeof undefined
|
|||
|
||||
export const required = (value: Field) => (value ? undefined : 'Required')
|
||||
|
||||
export const mustBeInteger = (value: string) => (!Number.isInteger(Number(value)) || value.includes('.') ? 'Must be an integer' : undefined)
|
||||
export const mustBeInteger = (value: string) =>
|
||||
!Number.isInteger(Number(value)) || value.includes('.') ? 'Must be an integer' : undefined
|
||||
|
||||
export const mustBeFloat = (value: number) => (value && Number.isNaN(Number(value)) ? 'Must be a number' : undefined)
|
||||
|
||||
|
@ -75,16 +76,19 @@ export const mustBeEthereumContractAddress = simpleMemoize(async (address: strin
|
|||
: undefined
|
||||
})
|
||||
|
||||
export const minMaxLength = (minLen: string | number, maxLen: string | number) => (value: string) => (value.length >= +minLen && value.length <= +maxLen ? undefined : `Should be ${minLen} to ${maxLen} symbols`)
|
||||
export const minMaxLength = (minLen: string | number, maxLen: string | number) => (value: string) =>
|
||||
value.length >= +minLen && value.length <= +maxLen ? undefined : `Should be ${minLen} to ${maxLen} symbols`
|
||||
|
||||
export const ADDRESS_REPEATED_ERROR = 'Address already introduced'
|
||||
|
||||
export const uniqueAddress = (addresses: string[] | List<string>) => simpleMemoize((value: string) => {
|
||||
const addressAlreadyExists = addresses.some((address) => sameAddress(value, address))
|
||||
return addressAlreadyExists ? ADDRESS_REPEATED_ERROR : undefined
|
||||
})
|
||||
export const uniqueAddress = (addresses: string[] | List<string>) =>
|
||||
simpleMemoize((value: string) => {
|
||||
const addressAlreadyExists = addresses.some(address => sameAddress(value, address))
|
||||
return addressAlreadyExists ? ADDRESS_REPEATED_ERROR : undefined
|
||||
})
|
||||
|
||||
export const composeValidators = (...validators: Function[]): FieldValidator => (value: Field) => validators.reduce((error, validator) => error || validator(value), undefined)
|
||||
export const composeValidators = (...validators: Function[]): FieldValidator => (value: Field) =>
|
||||
validators.reduce((error, validator) => error || validator(value), undefined)
|
||||
|
||||
export const inLimit = (limit: number, base: number, baseText: string, symbol: string = 'ETH') => (value: string) => {
|
||||
const amount = Number(value)
|
||||
|
|
|
@ -7,10 +7,7 @@ export type Open = {
|
|||
clickAway: () => void,
|
||||
}
|
||||
|
||||
export default withStateHandlers(
|
||||
() => ({ open: false }),
|
||||
{
|
||||
toggle: ({ open }) => () => ({ open: !open }),
|
||||
clickAway: () => () => ({ open: false }),
|
||||
},
|
||||
)
|
||||
export default withStateHandlers(() => ({ open: false }), {
|
||||
toggle: ({ open }) => () => ({ open: !open }),
|
||||
clickAway: () => () => ({ open: false }),
|
||||
})
|
||||
|
|
|
@ -19,9 +19,7 @@ type Props = {
|
|||
|
||||
class Block extends PureComponent<Props> {
|
||||
render() {
|
||||
const {
|
||||
margin, padding, justify, children, className, ...props
|
||||
} = this.props
|
||||
const { margin, padding, justify, children, className, ...props } = this.props
|
||||
|
||||
const paddingStyle = padding ? capitalize(padding, 'padding') : undefined
|
||||
return (
|
||||
|
|
|
@ -14,9 +14,7 @@ const calculateStyleBased = (minWidth, minHeight) => ({
|
|||
minHeight: minHeight && `${minHeight}px`,
|
||||
})
|
||||
|
||||
const GnoButton = ({
|
||||
minWidth, minHeight = 35, testId = '', style = {}, ...props
|
||||
}: Props) => {
|
||||
const GnoButton = ({ minWidth, minHeight = 35, testId = '', style = {}, ...props }: Props) => {
|
||||
const calculatedStyle = calculateStyleBased(minWidth, minHeight)
|
||||
|
||||
return <Button style={{ ...calculatedStyle, ...style }} data-testid={testId} {...props} />
|
||||
|
|
|
@ -23,6 +23,8 @@ const GnoButtonLink = ({
|
|||
testId = '',
|
||||
className = '',
|
||||
...props
|
||||
}: Props) => <button type={type} className={cx(styles.btnLink, size, color, weight, className)} data-testid={testId} {...props} />
|
||||
}: Props) => (
|
||||
<button type={type} className={cx(styles.btnLink, size, color, weight, className)} data-testid={testId} {...props} />
|
||||
)
|
||||
|
||||
export default GnoButtonLink
|
||||
|
|
|
@ -11,8 +11,6 @@ const style = {
|
|||
height: '100%',
|
||||
}
|
||||
|
||||
const Divider = ({ className }: Props) => (
|
||||
<div className={className} style={style} />
|
||||
)
|
||||
const Divider = ({ className }: Props) => <div className={className} style={style} />
|
||||
|
||||
export default Divider
|
||||
|
|
|
@ -20,9 +20,7 @@ type Props = {
|
|||
}
|
||||
|
||||
const Heading = (props: Props) => {
|
||||
const {
|
||||
align, tag, truncate, margin, color, children, testId, className = '', ...rest
|
||||
} = props
|
||||
const { align, tag, truncate, margin, color, children, testId, className = '', ...rest } = props
|
||||
|
||||
const classes = cx(className, 'heading', align, tag, margin ? capitalize(margin, 'margin') : undefined, color, {
|
||||
truncate,
|
||||
|
|
|
@ -14,9 +14,7 @@ type Props = {
|
|||
testId?: string,
|
||||
}
|
||||
|
||||
const Img = ({
|
||||
fullwidth, alt, bordered, className, style, testId = '', ...props
|
||||
}: Props) => {
|
||||
const Img = ({ fullwidth, alt, bordered, className, style, testId = '', ...props }: Props) => {
|
||||
const classes = cx(styles.img, { fullwidth, bordered }, className)
|
||||
|
||||
return <img alt={alt} style={style} className={classes} data-testid={testId} {...props} />
|
||||
|
|
|
@ -16,9 +16,7 @@ type Props = {
|
|||
innerRef?: React.ElementRef<any>,
|
||||
}
|
||||
|
||||
const GnosisLink = ({
|
||||
to, children, color, className, padding, innerRef, ...props
|
||||
}: Props) => {
|
||||
const GnosisLink = ({ to, children, color, className, padding, innerRef, ...props }: Props) => {
|
||||
const internal = /^\/(?!\/)/.test(to)
|
||||
const classes = cx(styles.link, color || 'regular', padding ? capitalize(padding, 'padding') : undefined, className)
|
||||
const LinkElement = internal ? Link : 'a'
|
||||
|
@ -29,13 +27,7 @@ const GnosisLink = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<LinkElement
|
||||
className={classes}
|
||||
href={internal ? null : to}
|
||||
to={internal ? to : null}
|
||||
{...refs}
|
||||
{...props}
|
||||
>
|
||||
<LinkElement className={classes} href={internal ? null : to} to={internal ? to : null} {...refs} {...props}>
|
||||
{children}
|
||||
</LinkElement>
|
||||
)
|
||||
|
|
|
@ -8,13 +8,11 @@ const cx = classNames.bind(styles)
|
|||
type Props = {
|
||||
children: React.Node,
|
||||
align?: 'center',
|
||||
overflow?: boolean
|
||||
overflow?: boolean,
|
||||
}
|
||||
|
||||
const Page = ({ children, align, overflow }: Props) => (
|
||||
<main className={cx(styles.page, align, { overflow })}>
|
||||
{children}
|
||||
</main>
|
||||
<main className={cx(styles.page, align, { overflow })}>{children}</main>
|
||||
)
|
||||
|
||||
export default Page
|
||||
|
|
|
@ -78,7 +78,7 @@ const PageFrame = ({ children, classes, currentNetwork }: Props) => {
|
|||
|
||||
export default withStyles(notificationStyles)(
|
||||
connect(
|
||||
(state) => ({
|
||||
state => ({
|
||||
currentNetwork: networkSelector(state),
|
||||
}),
|
||||
null,
|
||||
|
|
|
@ -20,9 +20,7 @@ type Props = {
|
|||
|
||||
class Paragraph extends React.PureComponent<Props> {
|
||||
render() {
|
||||
const {
|
||||
weight, children, color, align, size, transform, noMargin, className, dot, ...props
|
||||
} = this.props
|
||||
const { weight, children, color, align, size, transform, noMargin, className, dot, ...props } = this.props
|
||||
|
||||
return (
|
||||
<p
|
||||
|
|
|
@ -15,9 +15,7 @@ type Props = {
|
|||
testId?: string,
|
||||
}
|
||||
|
||||
const Row = ({
|
||||
children, className, margin, align, grow, testId = '', ...props
|
||||
}: Props) => {
|
||||
const Row = ({ children, className, margin, align, grow, testId = '', ...props }: Props) => {
|
||||
const rowClassNames = cx(
|
||||
styles.row,
|
||||
margin ? capitalize(margin, 'margin') : undefined,
|
||||
|
|
|
@ -6,13 +6,11 @@ import TableCell from '@material-ui/core/TableCell'
|
|||
import TableHead from '@material-ui/core/TableHead'
|
||||
import TableRow from '@material-ui/core/TableRow'
|
||||
|
||||
export {
|
||||
TableBody, TableCell, TableHead, TableRow,
|
||||
}
|
||||
export { TableBody, TableCell, TableHead, TableRow }
|
||||
|
||||
type Props = {
|
||||
children: React.Node,
|
||||
size?: number
|
||||
size?: number,
|
||||
}
|
||||
|
||||
const buildWidthFrom = (size: number) => ({
|
||||
|
@ -29,9 +27,7 @@ const GnoTable = ({ size, children }: Props) => {
|
|||
|
||||
return (
|
||||
<div style={overflowStyle}>
|
||||
<Table style={style}>
|
||||
{children}
|
||||
</Table>
|
||||
<Table style={style}>{children}</Table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
import type { RecordOf } from 'immutable'
|
||||
|
||||
export type AddressBookEntry = {
|
||||
address: string;
|
||||
name: string;
|
||||
isOwner: boolean;
|
||||
address: string,
|
||||
name: string,
|
||||
isOwner: boolean,
|
||||
}
|
||||
|
||||
export type AddressBookProps = {
|
||||
addressBook: Map<string, AddressBookEntry>
|
||||
addressBook: Map<string, AddressBookEntry>,
|
||||
}
|
||||
|
||||
export type AddressBook = RecordOf<AddressBookProps>
|
||||
|
|
|
@ -4,7 +4,9 @@ import type { AddressBookEntryType } from '~/logic/addressBook/model/addressBook
|
|||
|
||||
export const ADD_ENTRY = 'ADD_ENTRY'
|
||||
|
||||
|
||||
export const addAddressBookEntry = createAction<string, *, *>(ADD_ENTRY, (entry: AddressBookEntryType): AddressBookEntryType => ({
|
||||
entry,
|
||||
}))
|
||||
export const addAddressBookEntry = createAction<string, *, *>(
|
||||
ADD_ENTRY,
|
||||
(entry: AddressBookEntryType): AddressBookEntryType => ({
|
||||
entry,
|
||||
}),
|
||||
)
|
||||
|
|
|
@ -4,4 +4,6 @@ import type { AddressBook } from '~/logic/addressBook/model/addressBook'
|
|||
|
||||
export const LOAD_ADDRESS_BOOK = 'LOAD_ADDRESS_BOOK'
|
||||
|
||||
export const loadAddressBook = createAction<string, *, *>(LOAD_ADDRESS_BOOK, (addressBook: AddressBook) => ({ addressBook }))
|
||||
export const loadAddressBook = createAction<string, *, *>(LOAD_ADDRESS_BOOK, (addressBook: AddressBook) => ({
|
||||
addressBook,
|
||||
}))
|
||||
|
|
|
@ -18,7 +18,7 @@ const loadAddressBookFromStorage = () => async (dispatch: ReduxDispatch<GlobalSt
|
|||
// Fetch all the current safes, in case that we don't have a safe on the adbk, we add it
|
||||
const safes = safesListSelector(state)
|
||||
const adbkEntries = addressBook.keySeq().toArray()
|
||||
safes.forEach((safe) => {
|
||||
safes.forEach(safe => {
|
||||
const { address } = safe
|
||||
const found = adbkEntries.includes(address)
|
||||
if (!found) {
|
||||
|
|
|
@ -3,7 +3,6 @@ import { createAction } from 'redux-actions'
|
|||
|
||||
export const REMOVE_ENTRY = 'REMOVE_ENTRY'
|
||||
|
||||
|
||||
export const removeAddressBookEntry = createAction<string, *, *>(REMOVE_ENTRY, (entryAddress: string): void => ({
|
||||
entryAddress,
|
||||
}))
|
||||
|
|
|
@ -4,7 +4,9 @@ import type { AddressBookEntry } from '~/logic/addressBook/model/addressBook'
|
|||
|
||||
export const UPDATE_ENTRY = 'UPDATE_ENTRY'
|
||||
|
||||
|
||||
export const updateAddressBookEntry = createAction<string, *, *>(UPDATE_ENTRY, (entry: AddressBookEntry): AddressBookEntry => ({
|
||||
entry,
|
||||
}))
|
||||
export const updateAddressBookEntry = createAction<string, *, *>(
|
||||
UPDATE_ENTRY,
|
||||
(entry: AddressBookEntry): AddressBookEntry => ({
|
||||
entry,
|
||||
}),
|
||||
)
|
||||
|
|
|
@ -11,12 +11,7 @@ import enqueueSnackbar from '~/logic/notifications/store/actions/enqueueSnackbar
|
|||
import { saveAddressBook } from '~/logic/addressBook/utils'
|
||||
import type { AddressBookProps } from '~/logic/addressBook/model/addressBook'
|
||||
|
||||
const watchedActions = [
|
||||
ADD_ENTRY,
|
||||
REMOVE_ENTRY,
|
||||
UPDATE_ENTRY,
|
||||
]
|
||||
|
||||
const watchedActions = [ADD_ENTRY, REMOVE_ENTRY, UPDATE_ENTRY]
|
||||
|
||||
const addressBookMiddleware = (store: Store<GlobalState>) => (next: Function) => async (action: AnyAction) => {
|
||||
const handledAction = next(action)
|
||||
|
|
|
@ -24,7 +24,6 @@ export const buildAddressBook = (storedAdbk: AddressBook): AddressBookProps => {
|
|||
return addressBookBuilt
|
||||
}
|
||||
|
||||
|
||||
export default handleActions<State, *>(
|
||||
{
|
||||
[LOAD_ADDRESS_BOOK]: (state: State, action: ActionType<Function>): State => {
|
||||
|
@ -44,23 +43,22 @@ export default handleActions<State, *>(
|
|||
const { entry } = action.payload
|
||||
|
||||
// Adds the entry to all the safes (if it does not already exists)
|
||||
const newState = state.withMutations((map) => {
|
||||
const newState = state.withMutations(map => {
|
||||
const adbkMap = map.get('addressBook')
|
||||
|
||||
if (adbkMap) {
|
||||
adbkMap.keySeq()
|
||||
.forEach((safeAddress) => {
|
||||
const safeAddressBook = state.getIn(['addressBook', safeAddress])
|
||||
adbkMap.keySeq().forEach(safeAddress => {
|
||||
const safeAddressBook = state.getIn(['addressBook', safeAddress])
|
||||
|
||||
if (safeAddressBook) {
|
||||
const adbkAddressList = getAddressesListFromAdbk(safeAddressBook)
|
||||
const found = adbkAddressList.includes(entry.address)
|
||||
if (!found) {
|
||||
const updatedSafeAdbkList = safeAddressBook.push(entry)
|
||||
map.setIn(['addressBook', safeAddress], updatedSafeAdbkList)
|
||||
}
|
||||
if (safeAddressBook) {
|
||||
const adbkAddressList = getAddressesListFromAdbk(safeAddressBook)
|
||||
const found = adbkAddressList.includes(entry.address)
|
||||
if (!found) {
|
||||
const updatedSafeAdbkList = safeAddressBook.push(entry)
|
||||
map.setIn(['addressBook', safeAddress], updatedSafeAdbkList)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
return newState
|
||||
|
@ -69,13 +67,13 @@ export default handleActions<State, *>(
|
|||
const { entry } = action.payload
|
||||
|
||||
// Updates the entry from all the safes
|
||||
const newState = state.withMutations((map) => {
|
||||
const newState = state.withMutations(map => {
|
||||
map
|
||||
.get('addressBook')
|
||||
.keySeq()
|
||||
.forEach((safeAddress) => {
|
||||
.forEach(safeAddress => {
|
||||
const entriesList: List<AddressBookEntry> = state.getIn(['addressBook', safeAddress])
|
||||
const entryIndex = entriesList.findIndex((entryItem) => sameAddress(entryItem.address, entry.address))
|
||||
const entryIndex = entriesList.findIndex(entryItem => sameAddress(entryItem.address, entry.address))
|
||||
const updatedEntriesList = entriesList.set(entryIndex, entry)
|
||||
map.setIn(['addressBook', safeAddress], updatedEntriesList)
|
||||
})
|
||||
|
@ -86,13 +84,13 @@ export default handleActions<State, *>(
|
|||
[REMOVE_ENTRY]: (state: State, action: ActionType<Function>): State => {
|
||||
const { entryAddress } = action.payload
|
||||
// Removes the entry from all the safes
|
||||
const newState = state.withMutations((map) => {
|
||||
const newState = state.withMutations(map => {
|
||||
map
|
||||
.get('addressBook')
|
||||
.keySeq()
|
||||
.forEach((safeAddress) => {
|
||||
.forEach(safeAddress => {
|
||||
const entriesList = state.getIn(['addressBook', safeAddress])
|
||||
const entryIndex = entriesList.findIndex((entry) => sameAddress(entry.address, entryAddress))
|
||||
const entryIndex = entriesList.findIndex(entry => sameAddress(entry.address, entryAddress))
|
||||
const updatedEntriesList = entriesList.remove(entryIndex)
|
||||
map.setIn(['addressBook', safeAddress], updatedEntriesList)
|
||||
})
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable import/named */
|
||||
// @flow
|
||||
import { List, Map } from 'immutable'
|
||||
import { createSelector, Selector } from 'reselect'
|
||||
|
@ -7,7 +8,8 @@ import type { GlobalState } from '~/store'
|
|||
import type { AddressBook } from '~/logic/addressBook/model/addressBook'
|
||||
import { safeParamAddressFromStateSelector } from '~/routes/safe/store/selectors'
|
||||
|
||||
export const addressBookMapSelector = (state: GlobalState): Map<string, AddressBook> => state[ADDRESS_BOOK_REDUCER_ID].get('addressBook')
|
||||
export const addressBookMapSelector = (state: GlobalState): Map<string, AddressBook> =>
|
||||
state[ADDRESS_BOOK_REDUCER_ID].get('addressBook')
|
||||
|
||||
export const getAddressBook: Selector<GlobalState, AddressBook> = createSelector(
|
||||
addressBookMapSelector,
|
||||
|
@ -38,7 +40,7 @@ export const getNameFromAddressBook = (userAddress: string): string | null => {
|
|||
return null
|
||||
}
|
||||
const addressBook = useSelector(getAddressBook)
|
||||
const result = addressBook.filter((addressBookItem) => addressBookItem.address === userAddress)
|
||||
const result = addressBook.filter(addressBookItem => addressBookItem.address === userAddress)
|
||||
if (result.size > 0) {
|
||||
return result.get(0).name
|
||||
}
|
||||
|
|
|
@ -21,10 +21,11 @@ export const saveAddressBook = async (addressBook: AddressBook) => {
|
|||
}
|
||||
}
|
||||
|
||||
export const getAddressesListFromAdbk = (addressBook: AddressBook) => Array.from(addressBook).map((entry) => entry.address)
|
||||
export const getAddressesListFromAdbk = (addressBook: AddressBook) =>
|
||||
Array.from(addressBook).map(entry => entry.address)
|
||||
|
||||
const getNameFromAdbk = (addressBook: AddressBook, userAddress: string): string | null => {
|
||||
const entry = addressBook.find((addressBookItem) => addressBookItem.address === userAddress)
|
||||
const entry = addressBook.find(addressBookItem => addressBookItem.address === userAddress)
|
||||
if (entry) {
|
||||
return entry.name
|
||||
}
|
||||
|
@ -39,12 +40,11 @@ export const getNameFromAddressBook = (userAddress: string): string | null => {
|
|||
return getNameFromAdbk(addressBook, userAddress)
|
||||
}
|
||||
|
||||
|
||||
export const getOwnersWithNameFromAddressBook = (addressBook: AddressBook, ownerList: List<Owner>) => {
|
||||
if (!ownerList) {
|
||||
return []
|
||||
}
|
||||
const ownersListWithAdbkNames = ownerList.map((owner) => {
|
||||
const ownersListWithAdbkNames = ownerList.map(owner => {
|
||||
const ownerName = getNameFromAdbk(addressBook, owner.address)
|
||||
return {
|
||||
address: owner.address,
|
||||
|
|
|
@ -3,4 +3,6 @@ import { createAction } from 'redux-actions'
|
|||
|
||||
export const OPEN_COOKIE_BANNER = 'OPEN_COOKIE_BANNER'
|
||||
|
||||
export const openCookieBanner = createAction<string, *, *>(OPEN_COOKIE_BANNER, (cookieBannerOpen: boolean) => ({ cookieBannerOpen }))
|
||||
export const openCookieBanner = createAction<string, *, *>(OPEN_COOKIE_BANNER, (cookieBannerOpen: boolean) => ({
|
||||
cookieBannerOpen,
|
||||
}))
|
||||
|
|
|
@ -3,7 +3,10 @@ import axios from 'axios'
|
|||
import { getExchangeRatesUrl } from '~/config'
|
||||
import { AVAILABLE_CURRENCIES } from '~/logic/currencyValues/store/model/currencyValues'
|
||||
|
||||
const fetchCurrenciesRates = async (baseCurrency: AVAILABLE_CURRENCIES, targetCurrencyValue: AVAILABLE_CURRENCIES): Promise<number> => {
|
||||
const fetchCurrenciesRates = async (
|
||||
baseCurrency: AVAILABLE_CURRENCIES,
|
||||
targetCurrencyValue: AVAILABLE_CURRENCIES,
|
||||
): Promise<number> => {
|
||||
let rate = 0
|
||||
const url = `${getExchangeRatesUrl()}?base=${baseCurrency}&symbols=${targetCurrencyValue}`
|
||||
|
||||
|
|
|
@ -7,14 +7,15 @@ import fetchCurrenciesRates from '~/logic/currencyValues/api/fetchCurrenciesRate
|
|||
import { currencyValuesListSelector } from '~/logic/currencyValues/store/selectors'
|
||||
import { setCurrencyBalances } from '~/logic/currencyValues/store/actions/setCurrencyBalances'
|
||||
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
const fetchCurrencySelectedValue = (currencyValueSelected: AVAILABLE_CURRENCIES) => async (dispatch: ReduxDispatch<GlobalState>, getState: Function) => {
|
||||
const fetchCurrencySelectedValue = (currencyValueSelected: AVAILABLE_CURRENCIES) => async (
|
||||
dispatch: ReduxDispatch<GlobalState>,
|
||||
getState: Function,
|
||||
) => {
|
||||
const state = getState()
|
||||
const currencyBalancesList = currencyValuesListSelector(state)
|
||||
const selectedCurrencyRateInBaseCurrency = await fetchCurrenciesRates(AVAILABLE_CURRENCIES.USD, currencyValueSelected)
|
||||
|
||||
|
||||
const newList = []
|
||||
for (const currencyValue of currencyBalancesList) {
|
||||
const { balanceInBaseCurrency } = currencyValue
|
||||
|
|
|
@ -10,21 +10,24 @@ import { loadFromStorage } from '~/utils/storage'
|
|||
import fetchCurrencySelectedValue from '~/logic/currencyValues/store/actions/fetchCurrencySelectedValue'
|
||||
import { CURRENCY_SELECTED_KEY } from '~/logic/currencyValues/store/actions/saveCurrencySelected'
|
||||
|
||||
|
||||
export const fetchCurrencyValues = (safeAddress: string) => async (dispatch: ReduxDispatch<GlobalState>) => {
|
||||
try {
|
||||
const tokensFetched = await fetchTokenCurrenciesBalances(safeAddress)
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
const currencyList = List(tokensFetched.data.filter((currencyBalance) => currencyBalance.balanceUsd).map((currencyBalance) => {
|
||||
const { balanceUsd, tokenAddress } = currencyBalance
|
||||
return makeBalanceCurrency({
|
||||
currencyName: balanceUsd ? AVAILABLE_CURRENCIES.USD : null,
|
||||
tokenAddress,
|
||||
balanceInBaseCurrency: balanceUsd,
|
||||
balanceInSelectedCurrency: balanceUsd,
|
||||
})
|
||||
}))
|
||||
const currencyList = List(
|
||||
tokensFetched.data
|
||||
.filter(currencyBalance => currencyBalance.balanceUsd)
|
||||
.map(currencyBalance => {
|
||||
const { balanceUsd, tokenAddress } = currencyBalance
|
||||
return makeBalanceCurrency({
|
||||
currencyName: balanceUsd ? AVAILABLE_CURRENCIES.USD : null,
|
||||
tokenAddress,
|
||||
balanceInBaseCurrency: balanceUsd,
|
||||
balanceInSelectedCurrency: balanceUsd,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
dispatch(setCurrencyBalances(currencyList))
|
||||
const currencyStored = await loadFromStorage(CURRENCY_SELECTED_KEY)
|
||||
|
|
|
@ -7,10 +7,11 @@ import type { GlobalState } from '~/store'
|
|||
import { saveToStorage } from '~/utils/storage'
|
||||
import { setCurrencySelected } from '~/logic/currencyValues/store/actions/setCurrencySelected'
|
||||
|
||||
|
||||
export const CURRENCY_SELECTED_KEY = 'CURRENCY_SELECTED_KEY'
|
||||
|
||||
const saveCurrencySelected = (currencySelected: AVAILABLE_CURRENCIES) => async (dispatch: ReduxDispatch<GlobalState>) => {
|
||||
const saveCurrencySelected = (currencySelected: AVAILABLE_CURRENCIES) => async (
|
||||
dispatch: ReduxDispatch<GlobalState>,
|
||||
) => {
|
||||
await saveToStorage(CURRENCY_SELECTED_KEY, { currencyValueSelected: currencySelected })
|
||||
dispatch(setCurrencySelected(currencySelected))
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import type { CurrencyValues, CurrencyValuesProps } from '~/logic/currencyValues
|
|||
|
||||
export const SET_CURRENCY_BALANCES = 'SET_CURRENCY_BALANCES'
|
||||
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
export const setCurrencyBalances = createAction<string, *>(SET_CURRENCY_BALANCES, (currencyBalances: Map<string, CurrencyValues>): CurrencyValuesProps => ({ currencyBalances }))
|
||||
export const setCurrencyBalances = createAction<string, *>(
|
||||
SET_CURRENCY_BALANCES,
|
||||
(currencyBalances: Map<string, CurrencyValues>): CurrencyValuesProps => ({ currencyBalances }),
|
||||
)
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
// @flow
|
||||
import { createAction } from 'redux-actions'
|
||||
import type {
|
||||
CurrencyValuesProps,
|
||||
} from '~/logic/currencyValues/store/model/currencyValues'
|
||||
import type { CurrencyValuesProps } from '~/logic/currencyValues/store/model/currencyValues'
|
||||
import { AVAILABLE_CURRENCIES } from '~/logic/currencyValues/store/model/currencyValues'
|
||||
|
||||
|
||||
export const SET_CURRENT_CURRENCY = 'SET_CURRENT_CURRENCY'
|
||||
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
export const setCurrencySelected = createAction<string, *>(SET_CURRENT_CURRENCY, (currencyValueSelected: AVAILABLE_CURRENCIES): CurrencyValuesProps => ({ currencyValueSelected }))
|
||||
export const setCurrencySelected = createAction<string, *>(
|
||||
SET_CURRENT_CURRENCY,
|
||||
(currencyValueSelected: AVAILABLE_CURRENCIES): CurrencyValuesProps => ({ currencyValueSelected }),
|
||||
)
|
||||
|
|
|
@ -38,9 +38,8 @@ export const AVAILABLE_CURRENCIES = {
|
|||
MYR: 'MYR',
|
||||
}
|
||||
|
||||
|
||||
export type BalanceCurrencyType = {
|
||||
currencyName: AVAILABLE_CURRENCIES;
|
||||
currencyName: AVAILABLE_CURRENCIES,
|
||||
tokenAddress: string,
|
||||
balanceInBaseCurrency: string,
|
||||
balanceInSelectedCurrency: string,
|
||||
|
@ -54,8 +53,8 @@ export const makeBalanceCurrency = Record({
|
|||
})
|
||||
|
||||
export type CurrencyValuesProps = {
|
||||
currencyValueSelected: AVAILABLE_CURRENCIES;
|
||||
currencyValuesList: BalanceCurrencyType[]
|
||||
currencyValueSelected: AVAILABLE_CURRENCIES,
|
||||
currencyValuesList: BalanceCurrencyType[],
|
||||
}
|
||||
|
||||
export type CurrencyValues = RecordOf<CurrencyValuesProps>
|
||||
|
|
|
@ -4,6 +4,6 @@ import { List } from 'immutable'
|
|||
import { type GlobalState } from '~/store'
|
||||
import { CURRENCY_VALUES_KEY } from '~/logic/currencyValues/store/reducer/currencyValues'
|
||||
|
||||
|
||||
export const currencyValuesListSelector = (state: GlobalState) => (state[CURRENCY_VALUES_KEY].get('currencyBalances') ? state[CURRENCY_VALUES_KEY].get('currencyBalances') : List([]))
|
||||
export const currencyValuesListSelector = (state: GlobalState) =>
|
||||
state[CURRENCY_VALUES_KEY].get('currencyBalances') ? state[CURRENCY_VALUES_KEY].get('currencyBalances') : List([])
|
||||
export const currentCurrencySelector = (state: GlobalState) => state[CURRENCY_VALUES_KEY].get('currencyValueSelected')
|
||||
|
|
|
@ -6,7 +6,7 @@ import loadCurrentSession from '~/logic/currentSession/store/actions/loadCurrent
|
|||
import { makeCurrentSession } from '~/logic/currentSession/store/model/currentSession'
|
||||
|
||||
const loadCurrentSessionFromStorage = () => async (dispatch: ReduxDispatch<GlobalState>) => {
|
||||
const currentSession = (await getCurrentSessionFromStorage())
|
||||
const currentSession = await getCurrentSessionFromStorage()
|
||||
|
||||
dispatch(loadCurrentSession(makeCurrentSession(currentSession ? currentSession : {})))
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
// @flow
|
||||
import {
|
||||
Record, type RecordFactory, type RecordOf,
|
||||
} from 'immutable'
|
||||
import { Record, type RecordFactory, type RecordOf } from 'immutable'
|
||||
|
||||
export type CurrentSessionProps = {
|
||||
viewedSafes: Array<string>,
|
||||
|
|
|
@ -11,16 +11,12 @@ export type State = Map<string, *>
|
|||
|
||||
export default handleActions<State, *>(
|
||||
{
|
||||
[LOAD_CURRENT_SESSION]: (
|
||||
state: State,
|
||||
action: ActionType<Function>,
|
||||
): State => state.merge(Map(action.payload)),
|
||||
[LOAD_CURRENT_SESSION]: (state: State, action: ActionType<Function>): State => state.merge(Map(action.payload)),
|
||||
[UPDATE_VIEWED_SAFES]: (state: State, action: ActionType<Function>): State => {
|
||||
const safeAddress = action.payload
|
||||
|
||||
const newState = state.updateIn(
|
||||
['viewedSafes'],
|
||||
(prev) => (prev.includes(safeAddress) ? prev : [...prev, safeAddress]),
|
||||
const newState = state.updateIn(['viewedSafes'], prev =>
|
||||
prev.includes(safeAddress) ? prev : [...prev, safeAddress],
|
||||
)
|
||||
|
||||
saveCurrentSessionToStorage(newState)
|
||||
|
|
|
@ -4,7 +4,8 @@ import { loadFromStorage, saveToStorage } from '~/utils/storage'
|
|||
|
||||
const CURRENT_SESSION_STORAGE_KEY = 'CURRENT_SESSION'
|
||||
|
||||
export const getCurrentSessionFromStorage = async (): Promise<CurrentSessionProps | *> => loadFromStorage(CURRENT_SESSION_STORAGE_KEY)
|
||||
export const getCurrentSessionFromStorage = async (): Promise<CurrentSessionProps | *> =>
|
||||
loadFromStorage(CURRENT_SESSION_STORAGE_KEY)
|
||||
|
||||
export const saveCurrentSessionToStorage = async (currentSession: CurrentSession): Promise<*> => {
|
||||
try {
|
||||
|
|
|
@ -200,6 +200,7 @@ export const enhanceSnackbarForAction = (notification: Notification, key?: strin
|
|||
options: {
|
||||
...notification.options,
|
||||
onClick,
|
||||
// eslint-disable-next-line react/display-name
|
||||
action: (actionKey: number) => (
|
||||
<IconButton onClick={() => store.dispatch(closeSnackbarAction({ key: actionKey }))}>
|
||||
<IconClose />
|
||||
|
@ -208,18 +209,13 @@ export const enhanceSnackbarForAction = (notification: Notification, key?: strin
|
|||
},
|
||||
})
|
||||
|
||||
export const showSnackbar = (
|
||||
notification: Notification,
|
||||
enqueueSnackbar: Function,
|
||||
closeSnackbar: Function,
|
||||
) => enqueueSnackbar(
|
||||
notification.message,
|
||||
{
|
||||
export const showSnackbar = (notification: Notification, enqueueSnackbar: Function, closeSnackbar: Function) =>
|
||||
enqueueSnackbar(notification.message, {
|
||||
...notification.options,
|
||||
action: (key) => (
|
||||
// eslint-disable-next-line react/display-name
|
||||
action: key => (
|
||||
<IconButton onClick={() => closeSnackbar(key)}>
|
||||
<IconClose />
|
||||
</IconButton>
|
||||
),
|
||||
},
|
||||
)
|
||||
})
|
||||
|
|
|
@ -144,14 +144,19 @@ export const NOTIFICATIONS: Notifications = {
|
|||
message: 'A pending transaction requires your confirmation!',
|
||||
key: 'TX_WAITING_MSG',
|
||||
options: {
|
||||
variant: WARNING, persist: true, preventDuplicate: true,
|
||||
variant: WARNING,
|
||||
persist: true,
|
||||
preventDuplicate: true,
|
||||
},
|
||||
},
|
||||
TX_INCOMING_MSG: {
|
||||
message: 'Incoming transfer: ',
|
||||
key: 'TX_INCOMING_MSG',
|
||||
options: {
|
||||
variant: SUCCESS, persist: false, autoHideDuration: longDuration, preventDuplicate: true,
|
||||
variant: SUCCESS,
|
||||
persist: false,
|
||||
autoHideDuration: longDuration,
|
||||
preventDuplicate: true,
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -21,10 +21,10 @@ export default handleActions<NotificationReducerState, *>(
|
|||
const { key, dismissAll } = action.payload
|
||||
|
||||
if (key) {
|
||||
return state.update(key, (prev) => prev.set('dismissed', true))
|
||||
return state.update(key, prev => prev.set('dismissed', true))
|
||||
}
|
||||
if (dismissAll) {
|
||||
return state.withMutations((map) => {
|
||||
return state.withMutations(map => {
|
||||
map.forEach((notification, notificationKey) => {
|
||||
map.set(notificationKey, notification.set('dismissed', true))
|
||||
})
|
||||
|
|
|
@ -11,4 +11,6 @@ export const notificationsListSelector: Selector<
|
|||
GlobalState,
|
||||
{},
|
||||
List<Notification>,
|
||||
> = createSelector(notificationsMapSelector, (notifications: Map<string, Notification>): List<Notification> => notifications.toList())
|
||||
> = createSelector(notificationsMapSelector, (notifications: Map<string, Notification>): List<Notification> =>
|
||||
notifications.toList(),
|
||||
)
|
||||
|
|
|
@ -22,7 +22,7 @@ export const generateSignaturesFromTxConfirmations = (
|
|||
let sigs = '0x'
|
||||
Object.keys(confirmationsMap)
|
||||
.sort()
|
||||
.forEach((addr) => {
|
||||
.forEach(addr => {
|
||||
const conf = confirmationsMap[addr]
|
||||
if (conf.signature) {
|
||||
sigs += conf.signature.slice(2)
|
||||
|
|
|
@ -11,30 +11,28 @@ export const getAwaitingTransactions = (
|
|||
return Map({})
|
||||
}
|
||||
|
||||
const allAwaitingTransactions = allTransactions.map((safeTransactions) => {
|
||||
const nonCancelledTransactions = safeTransactions.filter(
|
||||
(transaction: Transaction) => {
|
||||
// If transactions are not executed, but there's a transaction with the same nonce EXECUTED later
|
||||
// it means that the transaction was cancelled (Replaced) and shouldn't get executed
|
||||
if (!transaction.isExecuted) {
|
||||
if (cancellationTransactionsByNonce.get(transaction.nonce)) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
transaction = transaction.set('cancelled', true)
|
||||
}
|
||||
const allAwaitingTransactions = allTransactions.map(safeTransactions => {
|
||||
const nonCancelledTransactions = safeTransactions.filter((transaction: Transaction) => {
|
||||
// If transactions are not executed, but there's a transaction with the same nonce EXECUTED later
|
||||
// it means that the transaction was cancelled (Replaced) and shouldn't get executed
|
||||
if (!transaction.isExecuted) {
|
||||
if (cancellationTransactionsByNonce.get(transaction.nonce)) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
transaction = transaction.set('cancelled', true)
|
||||
}
|
||||
// The transaction is not executed and is not cancelled, so it's still waiting confirmations
|
||||
if (!transaction.executionTxHash && !transaction.cancelled) {
|
||||
// Then we check if the waiting confirmations are not from the current user, otherwise, filters this
|
||||
// transaction
|
||||
const transactionWaitingUser = transaction.confirmations.filter(
|
||||
(confirmation) => confirmation.owner && confirmation.owner.address !== userAccount,
|
||||
)
|
||||
}
|
||||
// The transaction is not executed and is not cancelled, so it's still waiting confirmations
|
||||
if (!transaction.executionTxHash && !transaction.cancelled) {
|
||||
// Then we check if the waiting confirmations are not from the current user, otherwise, filters this
|
||||
// transaction
|
||||
const transactionWaitingUser = transaction.confirmations.filter(
|
||||
confirmation => confirmation.owner && confirmation.owner.address !== userAccount,
|
||||
)
|
||||
|
||||
return transactionWaitingUser.size > 0
|
||||
}
|
||||
return false
|
||||
},
|
||||
)
|
||||
return transactionWaitingUser.size > 0
|
||||
}
|
||||
return false
|
||||
})
|
||||
return nonCancelledTransactions
|
||||
})
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { getWeb3 } from '~/logic/wallets/getWeb3'
|
|||
import { EMPTY_DATA } from '~/logic/wallets/ethTransactions'
|
||||
import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts'
|
||||
|
||||
const estimateDataGasCosts = (data) => {
|
||||
const estimateDataGasCosts = data => {
|
||||
const reducer = (accumulator, currentValue) => {
|
||||
if (currentValue === EMPTY_DATA) {
|
||||
return accumulator + 0
|
||||
|
|
|
@ -26,12 +26,13 @@ export const estimateTxGasCosts = async (
|
|||
let txData
|
||||
if (isExecution) {
|
||||
// https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures
|
||||
const signatures = tx && tx.confirmations
|
||||
? generateSignaturesFromTxConfirmations(tx.confirmations, preApprovingOwner)
|
||||
: `0x000000000000000000000000${from.replace(
|
||||
'0x',
|
||||
'',
|
||||
)}000000000000000000000000000000000000000000000000000000000000000001`
|
||||
const signatures =
|
||||
tx && tx.confirmations
|
||||
? generateSignaturesFromTxConfirmations(tx.confirmations, preApprovingOwner)
|
||||
: `0x000000000000000000000000${from.replace(
|
||||
'0x',
|
||||
'',
|
||||
)}000000000000000000000000000000000000000000000000000000000000000001`
|
||||
txData = await safeInstance.methods
|
||||
.execTransaction(to, tx ? tx.value : 0, data, CALL, 0, 0, 0, ZERO_ADDRESS, ZERO_ADDRESS, signatures)
|
||||
.encodeABI()
|
||||
|
|
|
@ -3,7 +3,8 @@ import {
|
|||
DEFAULT_FALLBACK_HANDLER_ADDRESS,
|
||||
getEncodedMultiSendCallData,
|
||||
getGnosisSafeInstanceAt,
|
||||
MULTI_SEND_ADDRESS, SAFE_MASTER_COPY_ADDRESS,
|
||||
MULTI_SEND_ADDRESS,
|
||||
SAFE_MASTER_COPY_ADDRESS,
|
||||
} from '~/logic/contracts/safeContracts'
|
||||
import type { MultiSendTransactionInstanceType } from '~/logic/contracts/safeContracts'
|
||||
import { DELEGATE_CALL } from '~/logic/safe/transactions'
|
||||
|
@ -25,7 +26,9 @@ export const upgradeSafeToLastVersion = async (safeAddress: string, createTransa
|
|||
})
|
||||
}
|
||||
const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
|
||||
const fallbackHandlerTxData = safeInstance.contract.methods.setFallbackHandler(DEFAULT_FALLBACK_HANDLER_ADDRESS).encodeABI()
|
||||
const fallbackHandlerTxData = safeInstance.contract.methods
|
||||
.setFallbackHandler(DEFAULT_FALLBACK_HANDLER_ADDRESS)
|
||||
.encodeABI()
|
||||
const updateSafeTxData = safeInstance.contract.methods.changeMasterCopy(SAFE_MASTER_COPY_ADDRESS).encodeABI()
|
||||
const txs = [
|
||||
{
|
||||
|
|
|
@ -23,26 +23,30 @@ const activateTokensByBalance = (safeAddress: string) => async (
|
|||
|
||||
// addresses: potentially active tokens by balance
|
||||
// balances: tokens' balance returned by the backend
|
||||
const { addresses, balances } = result.data.reduce((acc, { tokenAddress, balance }) => ({
|
||||
addresses: [...acc.addresses, tokenAddress],
|
||||
balances: [[tokenAddress, balance]],
|
||||
}), {
|
||||
addresses: [],
|
||||
balances: [],
|
||||
})
|
||||
const { addresses, balances } = result.data.reduce(
|
||||
(acc, { tokenAddress, balance }) => ({
|
||||
addresses: [...acc.addresses, tokenAddress],
|
||||
balances: [[tokenAddress, balance]],
|
||||
}),
|
||||
{
|
||||
addresses: [],
|
||||
balances: [],
|
||||
},
|
||||
)
|
||||
|
||||
// update balance list for the safe
|
||||
dispatch(updateSafe({
|
||||
address: safeAddress,
|
||||
balances: Set(balances),
|
||||
}))
|
||||
dispatch(
|
||||
updateSafe({
|
||||
address: safeAddress,
|
||||
balances: Set(balances),
|
||||
}),
|
||||
)
|
||||
|
||||
// active tokens by balance, excluding those already blacklisted and the `null` address
|
||||
const activeByBalance = addresses.filter((address) => address !== null && !blacklistedTokens.includes(address))
|
||||
const activeByBalance = addresses.filter(address => address !== null && !blacklistedTokens.includes(address))
|
||||
|
||||
// need to persist those already active tokens, despite its balances
|
||||
const activeTokens = alreadyActiveTokens.toSet()
|
||||
.union(activeByBalance)
|
||||
const activeTokens = alreadyActiveTokens.toSet().union(activeByBalance)
|
||||
|
||||
// update list of active tokens
|
||||
dispatch(updateActiveTokens(safeAddress, activeTokens))
|
||||
|
|
|
@ -15,8 +15,8 @@ export default handleActions<State, *>(
|
|||
[ADD_TOKENS]: (state: State, action: ActionType<Function>): State => {
|
||||
const { tokens } = action.payload
|
||||
|
||||
const newState = state.withMutations((map) => {
|
||||
tokens.forEach((token) => {
|
||||
const newState = state.withMutations(map => {
|
||||
tokens.forEach(token => {
|
||||
map.set(token.address, token)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -21,14 +21,16 @@ export const getEthAsToken = (balance: string) => {
|
|||
}
|
||||
|
||||
export const calculateActiveErc20TokensFrom = (tokens: List<Token>) => {
|
||||
const activeTokens = List().withMutations((list) => tokens.forEach((token: Token) => {
|
||||
const isDeactivated = isEther(token.symbol) || !token.status
|
||||
if (isDeactivated) {
|
||||
return
|
||||
}
|
||||
const activeTokens = List().withMutations(list =>
|
||||
tokens.forEach((token: Token) => {
|
||||
const isDeactivated = isEther(token.symbol) || !token.status
|
||||
if (isDeactivated) {
|
||||
return
|
||||
}
|
||||
|
||||
list.push(token)
|
||||
}))
|
||||
list.push(token)
|
||||
}),
|
||||
)
|
||||
|
||||
return activeTokens
|
||||
}
|
||||
|
@ -49,4 +51,5 @@ export const isAddressAToken = async (tokenAddress: string) => {
|
|||
return call !== '0x'
|
||||
}
|
||||
|
||||
export const isTokenTransfer = (data: string, value: number): boolean => !!data && data.substring(0, 10) === '0xa9059cbb' && value === 0
|
||||
export const isTokenTransfer = (data: string, value: number): boolean =>
|
||||
!!data && data.substring(0, 10) === '0xa9059cbb' && value === 0
|
||||
|
|
|
@ -43,7 +43,7 @@ export const removeTokenFromStorage = async (safeAddress: string, token: Token)
|
|||
|
||||
export const removeFromActiveTokens = async (safeAddress: string, token: Token) => {
|
||||
const activeTokens = await getActiveTokens()
|
||||
const index = activeTokens.findIndex((activeToken) => activeToken.name === token.name)
|
||||
const index = activeTokens.findIndex(activeToken => activeToken.name === token.name)
|
||||
|
||||
if (index !== -1) {
|
||||
await saveActiveTokens(safeAddress, activeTokens.delete(index))
|
||||
|
|
|
@ -47,4 +47,5 @@ export const isUserOwner = (safe: Safe, userAccount: string): boolean => {
|
|||
return owners.find((owner: Owner) => sameAddress(owner.address, userAccount)) !== undefined
|
||||
}
|
||||
|
||||
export const isUserOwnerOnAnySafe = (safes: List<Safe>, userAccount: string): boolean => safes.some((safe) => isUserOwner(safe, userAccount))
|
||||
export const isUserOwnerOnAnySafe = (safes: List<Safe>, userAccount: string): boolean =>
|
||||
safes.some(safe => isUserOwner(safe, userAccount))
|
||||
|
|
|
@ -60,9 +60,7 @@ export const getEtherScanLink = (type: 'address' | 'tx', value: string) => {
|
|||
const getInfuraUrl = () => {
|
||||
const isMainnet = process.env.REACT_APP_NETWORK === 'mainnet'
|
||||
|
||||
return `https://${isMainnet ? 'mainnet' : 'rinkeby'}.infura.io:443/v3/${
|
||||
process.env.REACT_APP_INFURA_TOKEN
|
||||
}`
|
||||
return `https://${isMainnet ? 'mainnet' : 'rinkeby'}.infura.io:443/v3/${process.env.REACT_APP_INFURA_TOKEN}`
|
||||
}
|
||||
|
||||
// With some wallets from web3connect you have to use their provider instance only for signing
|
||||
|
@ -130,9 +128,7 @@ const getProviderName: Function = (web3Provider): string => {
|
|||
return name
|
||||
}
|
||||
|
||||
export const getAccountFrom: Function = async (
|
||||
web3Provider
|
||||
): Promise<string | null> => {
|
||||
export const getAccountFrom: Function = async (web3Provider): Promise<string | null> => {
|
||||
const accounts = await web3Provider.eth.getAccounts()
|
||||
|
||||
if (process.env.NODE_ENV === 'test' && window.testAccountIndex) {
|
||||
|
@ -148,9 +144,7 @@ const getNetworkIdFrom = async web3Provider => {
|
|||
return networkId
|
||||
}
|
||||
|
||||
export const getProviderInfo: Function = async (
|
||||
web3Provider
|
||||
): Promise<ProviderProps> => {
|
||||
export const getProviderInfo: Function = async (web3Provider): Promise<ProviderProps> => {
|
||||
web3 = new Web3(web3Provider)
|
||||
|
||||
const name = getProviderName(web3)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue