(add) cookie banner

This commit is contained in:
Gabriel Rodriguez Alsina 2019-11-22 20:23:57 -03:00
parent cb8cabb8cf
commit 8848ecd54d
20 changed files with 277 additions and 132 deletions

View File

@ -1,19 +1,25 @@
[ignore]
.*/migrations/**
.*/scripts/**
<PROJECT_ROOT>/migrations/**/.*
<PROJECT_ROOT>/contracts/**/.*
<PROJECT_ROOT>/scripts/**/.*
<PROJECT_ROOT>/public/**/.*
<PROJECT_ROOT>/src/test/**/.*
<PROJECT_ROOT>/babel.config.js
<PROJECT_ROOT>/jest.config.js
<PROJECT_ROOT>/truffle.js
[untyped]
.*/config/**
<PROJECT_ROOT>/config/**/.*
[declarations]
.*/node_modules/**
.*/flow-typed/**
<PROJECT_ROOT>/node_modules/**/.*
<PROJECT_ROOT>/flow-typed/**/.*
[include]
<PROJECT_ROOT>/src/**
<PROJECT_ROOT>/src/**/.*
[libs]
<PROJECT_ROOT>/flow-typed
<PROJECT_ROOT>/flow-typed/**/.*
[lints]

View File

@ -0,0 +1,145 @@
// @flow
import Checkbox from '@material-ui/core/Checkbox'
import Close from '@material-ui/icons/Close'
import FormControlLabel from '@material-ui/core/FormControlLabel'
import IconButton from '@material-ui/core/IconButton'
import React, { useState } from 'react'
import { makeStyles } from '@material-ui/core/styles'
import { useSelector, useDispatch } from 'react-redux'
import Link from '~/components/layout/Link'
import { WELCOME_ADDRESS } from '~/routes/routes'
import Button from '~/components/layout/Button'
import { primary, mainFontFamily } from '~/theme/variables'
import saveCookiesToStorage from '~/logic/cookies/store/actions/saveCookiesToStorage'
import type { CookiesProps } from '~/logic/cookies/store/model/cookie'
import { cookiesSelector } from '~/logic/cookies/store/selectors'
const useStyles = makeStyles({
container: {
backgroundColor: '#fff',
bottom: '0',
boxShadow: '0 2px 4px 0 rgba(212, 212, 211, 0.59)',
boxSizing: 'border-box',
display: 'flex',
justifyContent: 'center',
left: '0',
minHeight: '200px',
padding: '27px 15px',
position: 'fixed',
width: '100%',
},
content: {
maxWidth: '100%',
width: '830px',
},
text: {
color: primary,
fontFamily: mainFontFamily,
fontSize: '16px',
fontWeight: 'normal',
lineHeight: '1.38',
margin: '0 0 25px',
textAlign: 'center',
},
form: {
columnGap: '10px',
display: 'grid',
gridTemplateColumns: '1fr',
rowGap: '10px',
'@media (min-width: 960px)': {
gridTemplateColumns: '1fr 1fr 1fr',
},
},
formItem: {
alignItems: 'center',
display: 'flex',
justifyContent: 'center',
},
link: {
textDecoration: 'underline',
'&:hover': {
textDecoration: 'none',
},
},
close: {
position: 'absolute',
right: '12px',
top: '12px',
},
})
const acceptCookiesHandler = (newState: CookiesProps) => {
const dispatch = useDispatch()
// Aca tenes q pasarle el estado nuevo de los dos switches
dispatch(saveCookiesToStorage(newState))
}
const CookiesBanner = () => {
const classes = useStyles()
const cookiesState: CookiesProps = useSelector(cookiesSelector)
const { acceptedNecessary, acceptedAnalytics } = cookiesState
const showBanner = !acceptedNecessary || !acceptedAnalytics
const [localNecessary, setLocalNecessary] = useState(true)
const [localAnalytics, setLocalAnalytics] = useState(false)
return showBanner ? (
<div className={classes.container}>
<IconButton onClick={() => {}} className={classes.close}><Close /></IconButton>
<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
{' '}
<Link className={classes.link} to={WELCOME_ADDRESS}>Cookie Policy</Link>
{' '}
for more information. By clicking &quot;Accept cookies&quot;,
you agree to the storing of cookies on your device to enhance site
navigation and analyze site usage.
</p>
<div className={classes.form}>
<div className={classes.formItem}>
<FormControlLabel
checked={localNecessary}
label="Necessary"
name="Necessary"
onChange={() => setLocalNecessary((prev) => !prev)}
value={localNecessary}
control={(
<Checkbox />
)}
/>
</div>
<div className={classes.formItem}>
<FormControlLabel
label="Analytics"
name="Analytics"
onChange={() => setLocalAnalytics((prev) => !prev)}
value={localAnalytics}
control={(
<Checkbox />
)}
/>
</div>
<div className={classes.formItem}>
<Button
color="primary"
component={Link}
disabled={!localNecessary}
minWidth={180}
variant="outlined"
onClick={() => acceptCookiesHandler({
acceptedNecessary: localNecessary,
acceptedAnalytics: localAnalytics,
})}
>
Accept Cookies
</Button>
</div>
</div>
</div>
</div>
) : null
}
export default CookiesBanner

View File

@ -75,9 +75,4 @@ class Notifier extends Component<Props> {
}
}
export default withSnackbar(
connect(
selector,
actions,
)(Notifier),
)
export default withSnackbar(connect(selector, actions)(Notifier))

View File

@ -15,6 +15,7 @@ import AlertIcon from './assets/alert.svg'
import CheckIcon from './assets/check.svg'
import ErrorIcon from './assets/error.svg'
import InfoIcon from './assets/info.svg'
import CookiesBanner from '~/components/CookiesBanner'
import styles from './index.scss'
const notificationStyles = {
@ -92,6 +93,7 @@ const PageFrame = ({ children, classes, currentNetwork }: Props) => {
{children}
</SidebarProvider>
</SnackbarProvider>
<CookiesBanner />
</div>
)
}

View File

@ -16,9 +16,14 @@ if (process.env.NODE_ENV !== 'production') {
whyDidYouRender(React)
}
// $FlowFixMe
store.dispatch(loadActiveTokens())
store.dispatch(loadSafesFromStorage())
store.dispatch(loadDefaultSafe())
store.dispatch(loadCookiesFromStorage())
ReactDOM.render(<Root />, document.getElementById('root'))
const root = document.getElementById('root')
if (root !== null) {
ReactDOM.render(<Root />, root)
}

View File

@ -75,7 +75,10 @@ export const deploySafeContract = async (safeAccounts: string[], numConfirmation
const gasPrice = await calculateGasPrice()
return proxyFactoryMaster.createProxy(safeMaster.address, gnosisSafeData, {
from: userAccount, gas, gasPrice, value: 0,
from: userAccount,
gas,
gasPrice,
value: 0,
})
}

View File

@ -5,6 +5,8 @@ import type { Cookie, CookiesProps } from '~/logic/cookies/store/model/cookie'
export const SET_COOKIES_PERMISSIONS = 'SET_COOKIES_PERMISSIONS'
// eslint-disable-next-line max-len
export const setCookiesPermissions = createAction<string, *>(SET_COOKIES_PERMISSIONS, (cookies: Map<string, Cookie>): CookiesProps => cookies)
export const setCookiesPermissions = createAction<string, *>(
SET_COOKIES_PERMISSIONS,
(cookies: Map<string, Cookie>): CookiesProps => cookies,
)

View File

@ -2,8 +2,8 @@
import type { RecordOf } from 'immutable'
export type CookiesProps = {
acceptedNecessary: boolean;
acceptedAnalytics: boolean;
acceptedNecessary: boolean,
acceptedAnalytics: boolean,
}
export type Cookie = RecordOf<CookiesProps>

View File

@ -2,5 +2,4 @@
import { type GlobalState } from '~/store'
import { COOKIE_REDUCER_ID } from '~/logic/cookies/store/reducer/cookies'
export const cookiesSelector = (state: GlobalState) => state[COOKIE_REDUCER_ID]

View File

@ -8,9 +8,7 @@ export const ENQUEUE_SNACKBAR = 'ENQUEUE_SNACKBAR'
const addSnackbar = createAction<string, *>(ENQUEUE_SNACKBAR)
const enqueueSnackbar = (notification: NotificationProps) => (
dispatch: ReduxDispatch<GlobalState>,
) => {
const enqueueSnackbar = (notification: NotificationProps) => (dispatch: ReduxDispatch<GlobalState>) => {
const newNotification = {
...notification,
key: new Date().getTime(),

View File

@ -5,11 +5,10 @@ import { type GlobalState } from '~/store'
import { NOTIFICATIONS_REDUCER_ID } from '~/logic/notifications/store/reducer/notifications'
import { type Notification } from '~/logic/notifications/store/models/notification'
const notificationsMapSelector = (
state: GlobalState,
): Map<string, Notification> => state[NOTIFICATIONS_REDUCER_ID]
const notificationsMapSelector = (state: GlobalState): Map<string, Notification> => state[NOTIFICATIONS_REDUCER_ID]
export const notificationsListSelector: Selector<GlobalState, {}, List<Notification>> = createSelector(
notificationsMapSelector,
(notifications: Map<string, Notification>): List<Notification> => notifications.toList(),
)
export const notificationsListSelector: Selector<
GlobalState,
{},
List<Notification>,
> = createSelector(notificationsMapSelector, (notifications: Map<string, Notification>): List<Notification> => notifications.toList())

View File

@ -13,7 +13,8 @@ export const tokenListSelector: Selector<GlobalState, Map<string, Token>, List<T
(tokens: Map<string, Token>) => tokens.toList(),
)
export const orderedTokenListSelector: Selector<GlobalState, RouterProps, List<Token>> = createSelector(
tokenListSelector,
(tokens: List<Token>) => tokens.sortBy((token: Token) => token.get('symbol')),
)
export const orderedTokenListSelector: Selector<
GlobalState,
RouterProps,
List<Token>,
> = createSelector(tokenListSelector, (tokens: List<Token>) => tokens.sortBy((token: Token) => token.get('symbol')))

View File

@ -6,40 +6,25 @@ import { ETHEREUM_NETWORK_IDS, ETHEREUM_NETWORK } from '~/logic/wallets/getWeb3'
const providerSelector = (state: any): Provider => state[PROVIDER_REDUCER_ID]
export const userAccountSelector = createSelector(
providerSelector,
(provider: Provider) => {
const account = provider.get('account')
export const userAccountSelector = createSelector(providerSelector, (provider: Provider) => {
const account = provider.get('account')
return account || ''
},
)
return account || ''
})
export const providerNameSelector = createSelector(
providerSelector,
(provider: Provider) => {
const name = provider.get('name')
export const providerNameSelector = createSelector(providerSelector, (provider: Provider) => {
const name = provider.get('name')
return name ? name.toLowerCase() : undefined
},
)
return name ? name.toLowerCase() : undefined
})
export const networkSelector = createSelector(
providerSelector,
(provider: Provider) => {
const networkId = provider.get('network')
const network = ETHEREUM_NETWORK_IDS[networkId] || ETHEREUM_NETWORK.UNKNOWN
export const networkSelector = createSelector(providerSelector, (provider: Provider) => {
const networkId = provider.get('network')
const network = ETHEREUM_NETWORK_IDS[networkId] || ETHEREUM_NETWORK.UNKNOWN
return network
},
)
return network
})
export const loadedSelector = createSelector(
providerSelector,
(provider: Provider) => provider.get('loaded'),
)
export const loadedSelector = createSelector(providerSelector, (provider: Provider) => provider.get('loaded'))
export const availableSelector = createSelector(
providerSelector,
(provider: Provider) => provider.get('available'),
)
export const availableSelector = createSelector(providerSelector, (provider: Provider) => provider.get('available'))

View File

@ -72,8 +72,8 @@ const Routes = ({ defaultSafe, location }: RoutesProps) => {
)
}
// $FlowFixMe
export default connect<Object, Object, ?Function, ?Object>(
// $FlowFixMe
(state) => ({ defaultSafe: defaultSafeSelector(state) }),
null,
)(withRouter(Routes))

View File

@ -16,11 +16,7 @@ import {
TX_TYPE_EXECUTION,
TX_TYPE_CONFIRMATION,
} from '~/logic/safe/transactions'
import {
type NotificationsQueue,
getNotificationsFromTxType,
showSnackbar,
} from '~/logic/notifications'
import { type NotificationsQueue, getNotificationsFromTxType, showSnackbar } from '~/logic/notifications'
import { getErrorMessage } from '~/test/utils/ethereumErrors'
// https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures

View File

@ -90,7 +90,10 @@ const safeStorageMware = (store: Store<GlobalState>) => (next: Function) => asyn
case REMOVE_SAFE_OWNER: {
const { safeAddress, ownerAddress } = action.payload
const { owners } = safes.get(safeAddress)
setOwners(safeAddress, owners.filter((o) => o.address.toLowerCase() !== ownerAddress.toLowerCase()))
setOwners(
safeAddress,
owners.filter((o) => o.address.toLowerCase() !== ownerAddress.toLowerCase()),
)
break
}
case REPLACE_SAFE_OWNER: {
@ -110,7 +113,10 @@ const safeStorageMware = (store: Store<GlobalState>) => (next: Function) => asyn
const { safeAddress, ownerAddress, ownerName } = action.payload
const { owners } = safes.get(safeAddress)
const ownerToUpdateIndex = owners.findIndex((o) => o.address.toLowerCase() === ownerAddress.toLowerCase())
setOwners(safeAddress, owners.update(ownerToUpdateIndex, (owner) => owner.set('name', ownerName)))
setOwners(
safeAddress,
owners.update(ownerToUpdateIndex, (owner) => owner.set('name', ownerName)),
)
break
}
case SET_DEFAULT_SAFE: {

View File

@ -46,12 +46,15 @@ export default handleActions<SafeReducerState, *>(
const tokenAddress = action.payload
const newState = state.withMutations((map) => {
map.get('safes').keySeq().forEach((safeAddress) => {
const safeActiveTokens = map.getIn(['safes', safeAddress, 'activeTokens'])
const activeTokens = safeActiveTokens.add(tokenAddress)
map
.get('safes')
.keySeq()
.forEach((safeAddress) => {
const safeActiveTokens = map.getIn(['safes', safeAddress, 'activeTokens'])
const activeTokens = safeActiveTokens.add(tokenAddress)
map.updateIn(['safes', safeAddress], (prevSafe) => prevSafe.merge({ activeTokens }))
})
map.updateIn(['safes', safeAddress], (prevSafe) => prevSafe.merge({ activeTokens }))
})
})
return newState

View File

@ -115,7 +115,7 @@ export const whenSafeDeployed = (): Promise<string> => new Promise((resolve, rej
const interval = setInterval(() => {
if (times >= MAX_TIMES_EXECUTED) {
clearInterval(interval)
reject(new Error('Didn\'t load the safe'))
reject(new Error("Didn't load the safe"))
}
const url = `${window.location}`
console.log(url)

View File

@ -12,9 +12,7 @@ import { fillAndSubmitSendFundsForm } from './utils/transactions'
import { TRANSACTIONS_TAB_BTN_TEST_ID } from '~/routes/safe/components/Layout'
import { TRANSACTION_ROW_TEST_ID } from '~/routes/safe/components/Transactions/TxsTable'
import { useTestAccountAt, resetTestAccount } from './utils/accounts'
import {
CONFIRM_TX_BTN_TEST_ID,
} from '~/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/ButtonRow'
import { CONFIRM_TX_BTN_TEST_ID } from '~/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/ButtonRow'
import { APPROVE_TX_MODAL_SUBMIT_BTN_TEST_ID } from '~/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal'
afterEach(resetTestAccount)

View File

@ -1,65 +1,67 @@
// @flow
const border = '#e8e7e6'
const background = '#f7f5f5'
const primary = '#001428'
const secondary = '#008C73'
const fontColor = '#001428'
const fancyColor = '#f02525'
const warningColor = '#ffc05f'
const errorColor = '#f02525'
const secondaryTextOrSvg = '#B2B5B2'
const border = '#e8e7e6'
const connectedColor = '#008C73'
const disabled = '#5D6D74'
const xs = '4px'
const sm = '8px'
const md = '16px'
const lg = '24px'
const xl = '32px'
const xxl = '40px'
const marginButtonImg = '12px'
const errorColor = '#f02525'
const fancyColor = '#f02525'
const fontColor = '#001428'
const headerHeight = '53px'
const lg = '24px'
const mainFontFamily = 'Averta, sans-serif'
const marginButtonImg = '12px'
const md = '16px'
const primary = '#001428'
const secondary = '#008C73'
const secondaryTextOrSvg = '#B2B5B2'
const sm = '8px'
const warningColor = '#ffc05f'
const xl = '32px'
const xs = '4px'
const xxl = '40px'
module.exports = {
primary,
secondary,
disabled,
background,
fontColor,
secondaryText: secondaryTextOrSvg,
fancy: fancyColor,
warning: warningColor,
error: errorColor,
connected: connectedColor,
headerHeight,
xs,
sm,
md,
lg,
xl,
xxl,
border,
marginButtonImg,
fontSizeHeadingXs: 13,
fontSizeHeadingSm: 16,
fontSizeHeadingMd: 20,
fontSizeHeadingLg: 32,
buttonLargeFontSize: '16px',
lightFont: 300,
regularFont: 400,
bolderFont: 500,
boldFont: 700,
bolderFont: 500,
border,
buttonLargeFontSize: '16px',
connected: connectedColor,
disabled,
error: errorColor,
extraBoldFont: 800,
extraSmallFontSize: '11px',
smallFontSize: '12px',
mediumFontSize: '14px',
largeFontSize: '16px',
extraLargeFontSize: '20px',
xxlFontSize: '32px',
screenXs: 480,
screenXsMax: 767,
screenSm: 768,
screenSmMax: 991,
extraSmallFontSize: '11px',
fancy: fancyColor,
fontColor,
fontSizeHeadingLg: 32,
fontSizeHeadingMd: 20,
fontSizeHeadingSm: 16,
fontSizeHeadingXs: 13,
headerHeight,
largeFontSize: '16px',
lg,
lightFont: 300,
mainFontFamily,
marginButtonImg,
md,
mediumFontSize: '14px',
primary,
regularFont: 400,
screenLg: 1200,
screenMd: 992,
screenMdMax: 1199,
screenLg: 1200,
screenSm: 768,
screenSmMax: 991,
screenXs: 480,
screenXsMax: 767,
secondary,
secondaryText: secondaryTextOrSvg,
sm,
smallFontSize: '12px',
warning: warningColor,
xl,
xs,
xxl,
xxlFontSize: '32px',
}