Merge branch 'development' into dont-fetch-ownername-in-fetchTransactions

# Conflicts:
#	yarn.lock
This commit is contained in:
fernandomg 2020-05-05 16:50:53 -03:00
commit e402b51ff8
17 changed files with 614 additions and 1561 deletions

View File

@ -27,5 +27,6 @@ REACT_APP_APP_VERSION=$npm_package_version
# all environments # all environments
REACT_APP_INFURA_TOKEN= REACT_APP_INFURA_TOKEN=
# For Apps # For Apps
REACT_APP_GNOSIS_APPS_URL=http://localhost:3002 REACT_APP_GNOSIS_APPS_URL=https://safe-apps.staging.gnosisdev.com
REACT_APP_APPS_DISABLED=false

View File

@ -48,11 +48,11 @@
"prettier --write" "prettier --write"
] ]
}, },
"productName": "Safe Electron", "productName": "Safe Multisig",
"build": { "build": {
"appId": "io.gnosis.safe.macos", "appId": "io.gnosis.safe.macos",
"afterSign": "scripts/notarize.js", "afterSign": "scripts/notarize.js",
"productName": "Safe Electron", "productName": "Safe Multisig",
"asar": true, "asar": true,
"publish": [ "publish": [
{ {
@ -136,9 +136,9 @@
}, },
"dependencies": { "dependencies": {
"@gnosis.pm/safe-contracts": "1.1.1-dev.2", "@gnosis.pm/safe-contracts": "1.1.1-dev.2",
"@gnosis.pm/util-contracts": "2.0.6", "@gnosis.pm/util-contracts": "2.0.6",
"@gnosis.pm/safe-react-components": "https://github.com/gnosis/safe-react-components.git#a057248", "@gnosis.pm/safe-react-components": "https://github.com/gnosis/safe-react-components.git#a057248",
"@ledgerhq/hw-transport-node-hid": "5.12.0", "@ledgerhq/hw-transport-node-hid": "5.12.0",
"@material-ui/core": "4.9.10", "@material-ui/core": "4.9.10",
"@material-ui/icons": "4.9.1", "@material-ui/icons": "4.9.1",
"@material-ui/lab": "4.0.0-alpha.39", "@material-ui/lab": "4.0.0-alpha.39",

View File

@ -129,14 +129,18 @@ function createWindow() {
autoUpdater.init(mainWindow); autoUpdater.init(mainWindow);
}); });
mainWindow.webContents.on('crashed', () => { mainWindow.webContents.on('crashed', (event) => {
log.info('App Crashed'); log.info(`App Crashed: ${event}`);
mainWindow.reload(); mainWindow.reload();
}); });
mainWindow.on("closed", () => (mainWindow = null)); mainWindow.on("closed", () => (mainWindow = null));
} }
process.on('uncaughtException',function(error){
log.error(error);
});
app.userAgentFallback = process.platform ==='win32' ? app.userAgentFallback = process.platform ==='win32' ?
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.100 Safari/537.36' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.100 Safari/537.36' :
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) old-airport-include/1.0.0 Chrome Electron/7.1.7 Safari/537.36'; 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) old-airport-include/1.0.0 Chrome Electron/7.1.7 Safari/537.36';
@ -144,7 +148,7 @@ app.userAgentFallback = process.platform ==='win32' ?
app.commandLine.appendSwitch('ignore-certificate-errors'); app.commandLine.appendSwitch('ignore-certificate-errors');
app.on("ready", () =>{ app.on("ready", () =>{
// Hide the menu // Hide the menu
//Menu.setApplicationMenu(null); Menu.setApplicationMenu(null);
if(!isDev) createServer(); if(!isDev) createServer();
createWindow(); createWindow();
}); });

View File

@ -4,7 +4,7 @@ The most trusted platform to store digital assets on Ethereum
## Getting Started ## Getting Started
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system. These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.
### Prerequisites ### Prerequisites

View File

@ -2,11 +2,21 @@
// It has the same sandbox as a Chrome extension. // It has the same sandbox as a Chrome extension.
const TransportNodeHid = require("@ledgerhq/hw-transport-node-hid").default; const TransportNodeHid = require("@ledgerhq/hw-transport-node-hid").default;
const log = require('electron-log');
window.TransportNodeHid = TransportNodeHid; window.TransportNodeHid = TransportNodeHid;
window.isDesktop = true; window.isDesktop = true;
window.addEventListener('DOMContentLoaded', () => { window.addEventListener('DOMContentLoaded', () => {
console.error = (...args) => {
log.error(...args)
}
console.warn = (...args) => {
log.warn(...args)
}
console.log = (...args) => {
log.info(...args)
}
const replaceText = (selector, text) => { const replaceText = (selector, text) => {
const element = document.getElementById(selector) const element = document.getElementById(selector)
if (element) element.innerText = text if (element) element.innerText = text

View File

@ -28,6 +28,22 @@ export const Menu = styled.div.attrs(() => ({ className: 'background' }))`
border-top-left-radius: 8px; border-top-left-radius: 8px;
border-bottom-left-radius: 8px; border-bottom-left-radius: 8px;
background-color: white; background-color: white;
overflow-y: auto;
::-webkit-scrollbar {
width: 0.7em !important;
scroll-behavior: smooth !important;
}
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3) !important;
}
::-webkit-scrollbar-thumb {
background-color: darkgrey !important;
outline: 1px solid slategrey !important;
border-radius: 10px !important;
}
` `
export const Content = styled.div.attrs(() => ({ className: 'background' }))` export const Content = styled.div.attrs(() => ({ className: 'background' }))`

View File

@ -29,6 +29,7 @@ type Props = {
userAddress: string, userAddress: string,
classes: Object, classes: Object,
onDisconnect: Function, onDisconnect: Function,
openDashboard?: Function,
} }
const styles = () => ({ const styles = () => ({
@ -72,6 +73,15 @@ const styles = () => ({
}, },
disconnect: { disconnect: {
padding: `${md} ${lg}`, padding: `${md} ${lg}`,
'& button': {
background: '#f02525',
},
},
dashboard: {
padding: `${md} ${lg} ${xs}`,
},
dashboardText: {
letterSpacing: '1px',
}, },
disconnectText: { disconnectText: {
letterSpacing: '1px', letterSpacing: '1px',
@ -92,7 +102,7 @@ const styles = () => ({
}, },
}) })
const UserDetails = ({ classes, connected, network, onDisconnect, provider, userAddress }: Props) => { const UserDetails = ({ classes, connected, network, onDisconnect, openDashboard, provider, userAddress }: Props) => {
const status = connected ? 'Connected' : 'Connection error' const status = connected ? 'Connected' : 'Connection error'
const address = userAddress ? shortVersionOf(userAddress, 4) : 'Address not available' const address = userAddress ? shortVersionOf(userAddress, 4) : 'Address not available'
const identiconAddress = userAddress || 'random' const identiconAddress = userAddress || 'random'
@ -154,6 +164,15 @@ const UserDetails = ({ classes, connected, network, onDisconnect, provider, user
</Paragraph> </Paragraph>
</Row> </Row>
<Hairline margin="xs" /> <Hairline margin="xs" />
{openDashboard && (
<Row className={classes.dashboard}>
<Button color="primary" fullWidth onClick={openDashboard} size="medium" variant="contained">
<Paragraph className={classes.dashboardText} color="white" noMargin size="md">
{upperFirst(provider)} Wallet
</Paragraph>
</Button>
</Row>
)}
<Row className={classes.disconnect}> <Row className={classes.disconnect}>
<Button color="primary" fullWidth onClick={onDisconnect} size="medium" variant="contained"> <Button color="primary" fullWidth onClick={onDisconnect} size="medium" variant="contained">
<Paragraph className={classes.disconnectText} color="white" noMargin size="md"> <Paragraph className={classes.disconnectText} color="white" noMargin size="md">

View File

@ -54,6 +54,10 @@ class HeaderComponent extends React.PureComponent<Props, State> {
logComponentStack(error, info) logComponentStack(error, info)
} }
getOpenDashboard = () => {
const { wallet } = onboard.getState()
return wallet.type === 'sdk' && wallet.dashboard
}
onDisconnect = () => { onDisconnect = () => {
const { closeSnackbar, enqueueSnackbar, removeProvider } = this.props const { closeSnackbar, enqueueSnackbar, removeProvider } = this.props
@ -84,6 +88,7 @@ class HeaderComponent extends React.PureComponent<Props, State> {
connected={available} connected={available}
network={network} network={network}
onDisconnect={this.onDisconnect} onDisconnect={this.onDisconnect}
openDashboard={this.getOpenDashboard()}
provider={provider} provider={provider}
userAddress={userAddress} userAddress={userAddress}
/> />

View File

@ -2,7 +2,6 @@
import type { Dispatch as ReduxDispatch } from 'redux' import type { Dispatch as ReduxDispatch } from 'redux'
import fetchCollectibles from '~/logic/collectibles/store/actions/fetchCollectibles'
import { nftAssetsSelector } from '~/logic/collectibles/store/selectors' import { nftAssetsSelector } from '~/logic/collectibles/store/selectors'
import updateActiveAssets from '~/routes/safe/store/actions/updateActiveAssets' import updateActiveAssets from '~/routes/safe/store/actions/updateActiveAssets'
import { import {
@ -24,7 +23,6 @@ const activateAssetsByBalance = (safeAddress: string) => async (
return return
} }
await dispatch(fetchCollectibles())
const availableAssets = nftAssetsSelector(state) const availableAssets = nftAssetsSelector(state)
const alreadyActiveAssets = safeActiveAssetsSelectorBySafe(safeAddress, safes) const alreadyActiveAssets = safeActiveAssetsSelectorBySafe(safeAddress, safes)
const blacklistedAssets = safeBlacklistedAssetsSelectorBySafe(safeAddress, safes) const blacklistedAssets = safeBlacklistedAssetsSelectorBySafe(safeAddress, safes)

View File

@ -43,7 +43,6 @@ const wallets = [
{ {
walletName: 'portis', walletName: 'portis',
apiKey: PORTIS_DAPP_ID, apiKey: PORTIS_DAPP_ID,
label: 'Login with Email',
desktop: true, desktop: true,
}, },
{ walletName: 'authereum', desktop: false }, { walletName: 'authereum', desktop: false },

View File

@ -1,4 +1,5 @@
// @flow // @flow
import { BigNumber } from 'bignumber.js'
import React from 'react' import React from 'react'
import styled from 'styled-components' import styled from 'styled-components'
@ -8,6 +9,8 @@ import Heading from '~/components/layout/Heading'
import Img from '~/components/layout/Img' import Img from '~/components/layout/Img'
import { getEthAsToken } from '~/logic/tokens/utils/tokenHelpers' import { getEthAsToken } from '~/logic/tokens/utils/tokenHelpers'
const humanReadableBalance = (balance, decimals) => BigNumber(balance).times(`1e-${decimals}`).toFixed()
const Wrapper = styled.div` const Wrapper = styled.div`
margin-bottom: 15px; margin-bottom: 15px;
` `
@ -28,13 +31,14 @@ const confirmTransactions = (
safeAddress: string, safeAddress: string,
safeName: string, safeName: string,
ethBalance: string, ethBalance: string,
nameApp: string,
iconApp: string, iconApp: string,
txs: Array<any>, txs: Array<any>,
openModal: () => void, openModal: () => void,
closeModal: () => void, closeModal: () => void,
onConfirm: () => void, onConfirm: () => void,
) => { ) => {
const title = <ModalTitle iconUrl={iconApp} title="Compound" /> const title = <ModalTitle iconUrl={iconApp} title={nameApp} />
const body = ( const body = (
<> <>
@ -49,7 +53,7 @@ const confirmTransactions = (
<Heading tag="h3">Value</Heading> <Heading tag="h3">Value</Heading>
<div className="value-section"> <div className="value-section">
<Img alt="Ether" height={40} src={getEthAsToken('0').logoUri} /> <Img alt="Ether" height={40} src={getEthAsToken('0').logoUri} />
<Bold>{tx.value} ETH</Bold> <Bold>{humanReadableBalance(tx.value, 18)} ETH</Bold>
</div> </div>
</div> </div>
<div className="section"> <div className="section">

View File

@ -21,6 +21,7 @@ import {
safeParamAddressFromStateSelector, safeParamAddressFromStateSelector,
} from '~/routes/safe/store/selectors' } from '~/routes/safe/store/selectors'
import { loadFromStorage, saveToStorage } from '~/utils/storage' import { loadFromStorage, saveToStorage } from '~/utils/storage'
import { isSameHref } from '~/utils/url'
const APPS_STORAGE_KEY = 'APPS_STORAGE_KEY' const APPS_STORAGE_KEY = 'APPS_STORAGE_KEY'
const APPS_LEGAL_DISCLAIMER_STORAGE_KEY = 'APPS_LEGAL_DISCLAIMER_STORAGE_KEY' const APPS_LEGAL_DISCLAIMER_STORAGE_KEY = 'APPS_LEGAL_DISCLAIMER_STORAGE_KEY'
@ -30,7 +31,6 @@ const StyledIframe = styled.iframe`
box-sizing: border-box; box-sizing: border-box;
width: 100%; width: 100%;
height: 100%; height: 100%;
display: ${(props) => (props.shouldDisplay ? 'block' : 'none')};
` `
const Centered = styled.div` const Centered = styled.div`
display: flex; display: flex;
@ -68,7 +68,8 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
const getSelectedApp = () => appList.find((e) => e.id === selectedApp) const getSelectedApp = () => appList.find((e) => e.id === selectedApp)
const sendMessageToIframe = (messageId, data) => { const sendMessageToIframe = (messageId, data) => {
iframeEl.contentWindow.postMessage({ messageId, data }, getSelectedApp().url) const app = getSelectedApp()
iframeEl.contentWindow.postMessage({ messageId, data }, app.url)
} }
const handleIframeMessage = async (data) => { const handleIframeMessage = async (data) => {
@ -89,6 +90,7 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
safeAddress, safeAddress,
safeName, safeName,
ethBalance, ethBalance,
getSelectedApp().name,
getSelectedApp().iconUrl, getSelectedApp().iconUrl,
data.data, data.data,
openModal, openModal,
@ -172,16 +174,18 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
) )
} }
const app = getSelectedApp()
return ( return (
<> <>
{appIsLoading && <Loader />} {appIsLoading && <Loader />}
<StyledIframe <StyledIframe
frameBorder="0" frameBorder="0"
id="iframeId" id={`iframe-${app.name}`}
ref={iframeRef} ref={iframeRef}
shouldDisplay={!appIsLoading} shouldDisplay={!appIsLoading}
src={getSelectedApp().url} src={app.url}
title={getSelectedApp().name} title={app.name}
/> />
</> </>
) )
@ -250,8 +254,9 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
return return
} }
if (!getSelectedApp().url.includes(origin)) { const app = getSelectedApp()
console.error(`ThirdPartyApp: A message from was received from an unknown origin ${origin}`) if (!app.url.includes(origin)) {
console.error(`ThirdPartyApp: A message was received from an unknown origin ${origin}`)
return return
} }
@ -263,7 +268,7 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
return () => { return () => {
window.removeEventListener('message', onIframeMessage) window.removeEventListener('message', onIframeMessage)
} }
}) }, [selectedApp])
// load legalDisclaimer // load legalDisclaimer
useEffect(() => { useEffect(() => {
@ -276,7 +281,7 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
} }
checkLegalDisclaimer() checkLegalDisclaimer()
}) }, [])
// Load apps list // Load apps list
useEffect(() => { useEffect(() => {
@ -333,7 +338,8 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
}) })
} }
if (!iframeEl) { const app = getSelectedApp()
if (!iframeEl || !selectedApp || !isSameHref(iframeEl.src, app.url)) {
return return
} }
@ -342,7 +348,7 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
return () => { return () => {
iframeEl.removeEventListener('load', onIframeLoaded) iframeEl.removeEventListener('load', onIframeLoaded)
} }
}, [iframeEl]) }, [iframeEl, selectedApp])
if (loading) { if (loading) {
return <Loader /> return <Loader />

View File

@ -100,7 +100,7 @@ const TabsComponent = (props: Props) => {
label={labelTransactions} label={labelTransactions}
value={`${match.url}/transactions`} value={`${match.url}/transactions`}
/> />
{process.env.REACT_APP_ENV !== 'production' && ( {process.env.REACT_APP_APPS_DISABLED !== 'true' && (
<Tab <Tab
classes={{ classes={{
selected: classes.tabWrapperSelected, selected: classes.tabWrapperSelected,

View File

@ -91,7 +91,7 @@ const Layout = (props: Props) => {
<Switch> <Switch>
<Route exact path={`${match.path}/balances/:assetType?`} render={() => wrapInSuspense(<Balances />, null)} /> <Route exact path={`${match.path}/balances/:assetType?`} render={() => wrapInSuspense(<Balances />, null)} />
<Route exact path={`${match.path}/transactions`} render={() => wrapInSuspense(<TxsTable />, null)} /> <Route exact path={`${match.path}/transactions`} render={() => wrapInSuspense(<TxsTable />, null)} />
{process.env.REACT_APP_ENV !== 'production' && ( {process.env.REACT_APP_APPS_DISABLED !== 'true' && (
<Route <Route
exact exact
path={`${match.path}/apps`} path={`${match.path}/apps`}

View File

@ -2,6 +2,7 @@
import { useMemo } from 'react' import { useMemo } from 'react'
import { batch, useDispatch, useSelector } from 'react-redux' import { batch, useDispatch, useSelector } from 'react-redux'
import fetchCollectibles from '~/logic/collectibles/store/actions/fetchCollectibles'
import { fetchCurrencyValues } from '~/logic/currencyValues/store/actions/fetchCurrencyValues' import { fetchCurrencyValues } from '~/logic/currencyValues/store/actions/fetchCurrencyValues'
import activateAssetsByBalance from '~/logic/tokens/store/actions/activateAssetsByBalance' import activateAssetsByBalance from '~/logic/tokens/store/actions/activateAssetsByBalance'
import fetchSafeTokens from '~/logic/tokens/store/actions/fetchSafeTokens' import fetchSafeTokens from '~/logic/tokens/store/actions/fetchSafeTokens'
@ -24,7 +25,11 @@ export const useFetchTokens = () => {
} }
if (COLLECTIBLES_LOCATION_REGEX.test(history.location.pathname)) { if (COLLECTIBLES_LOCATION_REGEX.test(history.location.pathname)) {
dispatch(activateAssetsByBalance(address)) batch(() => {
dispatch(fetchCollectibles()).then(() => {
dispatch(activateAssetsByBalance(address))
})
})
} }
}, [history.location.pathname]) }, [history.location.pathname])
} }

20
src/utils/url.js Normal file
View File

@ -0,0 +1,20 @@
// @flow
export const isValid = (url: string, protocolsAllowed = ['https:', 'http:']): boolean => {
try {
const urlInfo = new URL(url)
return protocolsAllowed.includes(urlInfo.protocol)
} catch (error) {
return false
}
}
export const isSameHref = (url1: string, url2: string) => {
try {
const a = new URL(url1)
const b = new URL(url2)
return a.href === b.href
} catch (error) {
return false
}
}

2026
yarn.lock

File diff suppressed because it is too large Load Diff