#391: Display address book (#398)

* Development (#378)

* Adds cookie permissions to localStorage/redux state

* Adds action

* Adds files to git

* (fix) linting issues

* (update) flow-typed

* (update) .eslint and .flowconfig

* (add) cookie banner

* Finish cookie banner implementation

* (Add) checkbox's disabled style.

* Removes redux for cookiesStorage

* Fix cookieStore deletion

* Fixs cookies acceptance

* Fixs cookies banner verbiage
Fix "x" in wrong place for snackbar messages

* (remove) unused library

* Adds cookies utils
Replaces localStorage with cookies
Adds js-cookie

* (fix) added correct polished library and import, updated flow-typed

* (update) removed polish flow type, added js-cookie flow type

* Add link to cookie policy, use generic links for legal docs

* Remove link to cookie policy from sidebar, link cookie policy in the banner

* Let the user re-open the cookie banner

* remove withMutations from cookies reducer, move utils/cookies to logic/cookies

* Now the sidebar closes when the cookie banner is toggled

* Feature #169: Intercom (#301)

* Implements intercom
Adds REACT_APP_INTERCOM_ID_MAINNET and REACT_APP_INTERCOM_ID_RINKEBY env vars

* Adds .env.example

* Adds intercom env vars

* Updates env vars
Replaces "rinkeby" and "mainnet" with "non-production" and "production"

* Now loads intercom after the user accepted the analytics

* Add env variable for production intercom id

* Update .env.example

* Removes react-intercom
Fixs getIntercomId with default dev appID
Now loads intercom as script

* Renegerate flow-types

* Remove 'Hide zero balances' (#310)

* Use medium font size for 'select an asset' label (#312)

* Feature #272: Google Analytics (#299)

* Adds google analytics tracking for every route

* Adds cookies acceptance check before tracking

* Fix react-ga dependency

* Fix cookieStore deletion

* Merge with #189-cookie-banner

* Fixs react ga version
Refactored HOC with hooks

* Fix TYPO

* Fix path for cookies utils

* Fix imports

* remove flow type definition for polish

* Add GA ID log

* Fix load GA After cookies acceptance

* Feature #224: Activate tokens automatically (#300)

* Replace 'Manage Tokens' with 'Manage List'

* prevent 301 redirects

* Add `BLACKLISTED_TOKENS` key to persist through immortal

* Add store/action to extract _activate tokens by its balance_

- keeps already activated tokens
- discards blacklisted tokens
- adds tokens whose vales are bigger than zero and are not blacklisted

* Add `blacklistedTokens` list to safe's store

* Display activeTokensByBalance in 'Balances' screen

* Enable token's blacklisting functionality in Tokens List

* Retrieve balance from API

* Rename action to `activateTokensByBalance`

* Fix linting errors

- line too long
- required return

* Do not persist a separate list into `BLACKLISTED_TOKENS`

* Typo fix (#326)

* Fix security vulnerability: Remove uglifyjs, use terser plugin (#327)

* Remove uglifyjs, use terser plugin

* fix css-loader config

* Feature #256: Sticky header (#308)

* Add sticky header

* Remove react-headroom, set position to fixed for header

* Regenerate yarn lock

* Remove unused headroom style from root.scss

* Pull from dev, conflict fixes

* Update welcome text (#323)

* Feature #137: Tx list improvements (#222)

* Fix swapOwners threshold displayed as hex in tx list

* Refactor spinner in empty table

* Fix number of rows per page in table pagination

* Add use of EtherscanLink component

* Set short version of strings in tx list

* Adjust styles in tx list

* Add more styles to table

* WIP

* An attempt to fix #204 by showing UNKNOWN instead of failed to fetch token symbol

* Table pagination style fixes

* Show confirm transaction button in owner list

* Update dependencies

* Add confirmation icons to owner list in tx list

* exclude unneeded stuff from travis.yml

* Adds cookie permissions to localStorage/redux state

* Update dependencies

* Adds action

* Adds files to git

* (fix) linting issues

* (update) flow-typed

* (update) .eslint and .flowconfig

* (add) cookie banner

* Finish cookie banner implementation

* (Add) checkbox's disabled style.

* Removes redux for cookiesStorage

* Fix cookieStore deletion

* Increase TO_EXP for bignumber.js

* Fixs cookies acceptance

* Fixs cookies banner verbiage
Fix "x" in wrong place for snackbar messages

* (fix) added correct polished library and import, updated flow-typed

* (update) removed polish flow type, added js-cookie flow type

* Add link to cookie policy, use generic links for legal docs

* Remove link to cookie policy from sidebar, link cookie policy in the banner

* Mock Safe creation transaction

* Format code

* Fix break statement

* Remove deployment of storybook

* Let the user re-open the cookie banner

* Update tx status messages and visual confirmation progress

* Fix svg in tx confirmation progress

* Add styles to tx type in tx list

* Replace nonce in tx list with tx id

* Update opacity of cancelled tx

* Fix short version of address

* remove withMutations from cookies reducer, move utils/cookies to logic/cookies

* Now the sidebar closes when the cookie banner is toggled

* Fix styles in tx list

* Add Pending status in tx description

* (remove) unused library

* Adds cookies utils
Replaces localStorage with cookies
Adds js-cookie

* Set 25 rows per page in tx list by default

* Align tx table

* Adjust tx table and tx details borders

* Fix fetching transactions to show Safe creation tx alone

* Fix failed Safe creation transaction

* Add styles to tx data

* Refactor and fix owner list in transaction

* Refactor use of theme variables

* Remove storybook files

* Update dependencies

* Fix warnings

* Fix dependencies

* Update file-loader config

* Fix owner colors in the tx confirmation progress

* Fix transaction type icon height

* Tx list adjustments

* Update readme

* (Feature) Etherscan button icon (#331)

* (add) new open on etherscan button icon

* (remove) unused asset

* (fix) icon background

* Feature #239: Replace early access label with network label (#311)

* Remove early access label

* Revert "Remove early access label"

This reverts commit 34682f0f6d9c1974a6e45c2a31358864931d9c1e.

* Replace early access label with network label

* Capitalzie first letter of the network name

* Adds threshold update on checkAndUpdateSafe (#320)

* Feature #159: Pending transaction that requires user confirmation (#330)

* Creates a new notification: waitingConfirmation
Adds key as optional parameter for notification
Implemented getAwaitingTransactions to get the transactions that needs to be confirmed by the current user
Not fetchTransactions action also dispatch a notification for awaiting transactions
Improved performance of routes/safe/container/index to avoid re-rendering

* Removes notification logic on fetchTransactions
Adds notificationsMiddleware

* Moves fetchTransaction to container

* Removes unused param on fetchTransactions

* Fixs null safe check

* Fixs middleware declaration

* Removes lodash

* Changes cancelled transaction detection logic

* Feature #122: Multisig migration (#315)

* Adds query-string package.json
Parses query string on open layout

* Implements load all the values on openSafe view from param querys

* Adds query params validation

* Moves query parse logic to open.jsx

* Changes default no metamask component on open page

* Replaces global isNaN

* Fix threshold parsing validation

* Updates the welcome component with new verbiage for open

* Renames isOpenSafe to isOldMultisigMigration

* Merge branch 'development' of https://github.com/gnosis/safe-react into 122-multisig-migration

# Conflicts:
#	src/routes/open/components/Layout.jsx

* Merge branch 'development' of https://github.com/gnosis/safe-react into 159-pending-transactions

# Conflicts:
#	src/routes/safe/components/Transactions/index.jsx
#	yarn.lock

* set anonymizeIp to true (#335)

* Feature #180: Predict transaction nonce (#293)

* Dep bump

* Fetch transactions when safe view is mounted

* eslint fix

* Calculate new tx nonce from latest tx in service

* Fix tx cancellation, allow passing nonce to createTransaction

* dep bump

* Refactor createTransaction/processTransaction to use object as argument

* Adopting transactions table to new send tx flow with predicted nonces

* dep bump, disable esModule in file-loader options after new v5 release

* Don't show older tx annotation for already executed txs

* sort tx by nonce

* get new safe nonce after tx execution

* Bugfixes

* remove whitespace for showOlderTxAnnotation

* Feature #329: Rename to Multisig (#334)

* Rename to Multisig

* migration text fix

* replace safe for teams with multisig

* Fixs race condition (#341)

Fixs typo

* (Feature) Incoming transactions (#333)

* Add `blockNumber` to transactions model

* Create `incomingTransaction` node in store and load it along with `transactions`

* Add incoming transfers to the Transactions table

* Rename `transactionHash` to `executionTxHash` for better incoming/outgoing txs unification in Transactions table

* Add incoming transactions details

* Add transaction type icon in table row

* Add snackbar notification for incoming txs

* Make incoming transaction snackbar to show on any tab

* Use makeStyles hooks

* Fix incoming amounts conversion from wei

* Make concurrent promise calls

* Use date to calculate transactions ids

* Prevent repeating messages

- also move logic to display snack bar into the notifications middleware

* Merge transactions and incomingTxs to the transactions selector

* Show 'Multiple incoming transfers' if they are more than 3

* Prevent incoming transactions snack bar for first-timer users

* Set ID as the default order

* Use constant for _incoming_ type

* Feature #154: Fiat Balances (#290)

* Adds DropdownCurrency
Adds redux store for currencyValues
Adds Value column on the assets table
Adds mocked currency values

* (add) base currency dropdown

* (add) dropdown styles

* Refactors data fetching of the balances list
Now uses the endpoint

* Fix column value styling

* Adds support for ECB currency values

* Fixs list overflow

* Changes endpoint url
Adds decimals for balance values

* (fix) remove inline style

* (add) currencies dropdown search field

* (fix) list items' hover color

* Implements filter search

* Fix warning on dropdown template

* Saves selected currency in localStorage

* Remove spaces on curly braces
Add alt
Renames rowItem to cellItem
Improves fetchCurrenciesRates handling

* Removes withMutations

* Removes middleware
Export style to another file for dropdownCurrency

* Adds classNames

* Fix incomming transactions fetching (#346)

* Feature: Activate fortmatic (#339)

* Add fortmatic integration to web3connect

* add fortmatic

* Safe open form improvements: limit calling initContracts to 1 time

* update .env.example

* Feature #336: Confirmation required notification for non-owners fix (#338)

* Refactors grantedSelector with isUserOwner function
Checks if the user is owner of the safe before sending notification

* Adds safeParamAddressFromStateSelector
Refactors notificationsMiddleware with new selector

* Remove old size check

* safe notifications middleware fixes

* add apt-get update to travis yml

* (Fix) Incoming transactions inline-styles (#344)

* Remove inline styles

* Replace ternary with logical && operator

* use cn as shortcut for classnames

* Makes minMaxLength 2 to AddCustomToken (#363)

* Fixs ETH display on balances list (#360)

* Bug #348: Safelist entries get removed (#358)

* Fix balances saved to localStorage not in format [tokenAdd, balance] but [balance]

* Updates localStorage version value

* Use submission instead of execution date to sort outgoing txs (#364)

* Feature #190: Sidebar improvements (#347)

* Change icons
Adds checked icon

* Adds safeParamAddressFromStateSelector for get current safe selected
Implements check icon on sidebar

* Remove overflow on sidebar
Start alignments

* Removes headerPlaceholder

* Improves header

* Improves header

* Fix header style

* use sameAddress function to check address eqaulity when fetching transactions (#365)

* Bug #352: Owner shown multiple times (#367)

* Ensure lowercased string comparison for owners' addresses

* Use `sameAddress` for addresses comparison

* Use transaction value as a string (#369)

* Update isTokenTransfer to use value as a string

* Rename error message

* Update dependencies

* Refactor

* Fix alternative token abi and token address for incoming transactions (#373)

* Bug #313: Payload breaks ui (#371)

* Makes minMaxLength 2 to AddCustomToken

* Fix styling

* Fix typo

* Feature #200: Show version number (#370)

* Add `dotenv-expand` as a dependency

* Add app version to sidebar

* Add hardcoded latest safe version to env variables

* Add `semver` to compare current vs latest version

* Add Safe version to Safe Details

* Adjustments in version number

* Fix transaction description value (#377)

* Fix transaction description value

* Remove duplicated symbols

* fix checkAndUpdateSafe logic (#379)

* Update .env.example

* update package json version

* update package json version

* Fix app version in side bar

* add REACT_APP_APP_VERSION global env var

* add react_app_version to build script

* remove react_app_app_version from build-mainnet

* Adds basic addressBook table

* Implements redux and localStorage for addressBook

* Disables loading page on empty address book

* Fix address display

* Updates defaultRowsPerPage to 25

* Removes unused pops
Renames AddressBookEntry with AddressBookEntry

* Replaces updateAddressBook with addAddressBook

Co-authored-by: Germán Martínez <germartinez@users.noreply.github.com>
This commit is contained in:
Agustin Pane 2020-01-09 05:08:40 -03:00 committed by Mikhail Mikheev
parent f1d1b78531
commit 18f8c44741
13 changed files with 368 additions and 3 deletions

View File

@ -24,6 +24,7 @@ type Props<K> = {
defaultOrder: 'desc' | 'asc',
noBorder: boolean,
disablePagination: boolean,
disableLoadingOnEmptyTable?: boolean,
}
type State = {
@ -154,6 +155,7 @@ class GnoTable<K> extends React.Component<Props<K>, State> {
defaultFixed,
defaultRowsPerPage,
noBorder,
disableLoadingOnEmptyTable,
} = this.props
const {
order, orderBy, page, orderProp, rowsPerPage, fixed,
@ -176,7 +178,7 @@ class GnoTable<K> extends React.Component<Props<K>, State> {
}
const emptyRows = displayRows - Math.min(displayRows, data.size - page * displayRows)
const isEmpty = size === 0
const isEmpty = size === 0 && !disableLoadingOnEmptyTable
return (
<>

View File

@ -0,0 +1,13 @@
// @flow
import type { RecordOf } from 'immutable'
export type AddressBookEntry = {
address: string;
name: string;
}
export type AddressBookProps = {
addressBookList: AddressBookEntry[]
}
export type AddressBook = RecordOf<AddressBookProps>

View File

@ -0,0 +1,7 @@
// @flow
import { createAction } from 'redux-actions'
import type { AddressBook } from '~/logic/addressBook/model/addressBook'
export const ADD_ADDRESS_BOOK = 'ADD_ADDRESS_BOOK'
export const addAddressBook = createAction<string, *, *>(ADD_ADDRESS_BOOK, (addressBook: AddressBook) => ({ addressBook }))

View File

@ -0,0 +1,18 @@
// @flow
import type { Dispatch as ReduxDispatch } from 'redux'
import { type GlobalState } from '~/store/index'
import { getAddressBookFromStorage } from '~/logic/addressBook/utils'
import { addAddressBook } from '~/logic/addressBook/store/actions/addAddressBook'
const loadAddressBook = () => async (dispatch: ReduxDispatch<GlobalState>) => {
try {
const addressBook = await getAddressBookFromStorage()
dispatch(addAddressBook(addressBook))
} catch (err) {
// eslint-disable-next-line
console.error('Error while loading active tokens from storage:', err)
}
}
export default loadAddressBook

View File

@ -0,0 +1,20 @@
// @flow
import { Map } from 'immutable'
import { handleActions, type ActionType } from 'redux-actions'
import type { Cookie } from '~/logic/cookies/model/cookie'
import { ADD_ADDRESS_BOOK } from '~/logic/addressBook/store/actions/addAddressBook'
export const ADDRESS_BOOK_REDUCER_ID = 'addressBook'
export type State = Map<string, Map<string, Cookie>>
export default handleActions<State, *>(
{
[ADD_ADDRESS_BOOK]: (state: State, action: ActionType<Function>): State => {
const { addressBook } = action.payload
return state.set('addressBook', addressBook)
},
},
Map(),
)

View File

@ -0,0 +1,14 @@
// @flow
import { List } from 'immutable'
import { createSelector, Selector } from 'reselect'
import type { Provider } from '~/logic/wallets/store/model/provider'
import { ADDRESS_BOOK_REDUCER_ID } from '~/logic/addressBook/store/reducer/addressBook'
import type { GlobalState } from '~/store'
import type { AddressBook } from '~/logic/addressBook/model/addressBook'
export const getAddressBook = (state: any): Provider => state[ADDRESS_BOOK_REDUCER_ID].get('addressBook') || []
export const getAddressBookListSelector: Selector<GlobalState, AddressBook> = createSelector(
getAddressBook,
(addressBook: AddressBook) => (addressBook ? List(addressBook) : List([])),
)

View File

@ -0,0 +1,21 @@
// @flow
import { Map } from 'immutable'
import type { AddressBookProps } from '~/logic/addressBook/model/addressBook'
import { loadFromStorage, saveToStorage } from '~/utils/storage'
import type { Token } from '~/logic/tokens/store/model/token'
const ADDRESS_BOOK_STORAGE_KEY = 'ADDRESS_BOOK_STORAGE_KEY'
export const getAddressBookFromStorage = async (): Promise<AddressBookProps> => {
const data = await loadFromStorage(ADDRESS_BOOK_STORAGE_KEY)
return data || []
}
export const saveAddressBook = async (tokens: Map<string, Token>) => {
try {
await saveToStorage(ADDRESS_BOOK_STORAGE_KEY, tokens.toJS())
} catch (err) {
console.error('Error storing tokens in localstorage', err)
}
}

View File

@ -0,0 +1,44 @@
// @flow
import { List } from 'immutable'
import { type Column } from '~/components/Table/TableHead'
export const ADDRESS_BOOK_ROW_ID = 'address-book-row'
export const TX_TABLE_ADDRESS_BOOK_ID = 'idAddressBook'
export const AB_NAME_ID = 'name'
export const AB_ADDRESS_ID = 'address'
export const AB_ADDRESS_ACTIONS_ID = 'actions'
export const EDIT_ENTRY_BUTTON = 'edit-entry-btn'
export const REMOVE_ENTRY_BUTTON = 'remove-entry-btn'
export const SEND_ENTRY_BUTTON = 'send-entry-btn'
export const generateColumns = () => {
const nameColumn: Column = {
id: AB_NAME_ID,
order: false,
disablePadding: false,
label: 'Name',
width: 150,
custom: false,
align: 'left',
}
const addressColumn: Column = {
id: AB_ADDRESS_ID,
order: false,
disablePadding: false,
label: 'Address',
custom: false,
align: 'left',
}
const actionsColumn: Column = {
id: AB_ADDRESS_ACTIONS_ID,
order: false,
disablePadding: false,
label: '',
custom: true,
}
return List([nameColumn, addressColumn, actionsColumn])
}

View File

@ -0,0 +1,125 @@
// @flow
import React, { useEffect } from 'react'
import cn from 'classnames'
import { List } from 'immutable'
import TableRow from '@material-ui/core/TableRow'
import TableCell from '@material-ui/core/TableCell'
import { withStyles } from '@material-ui/core/styles'
import classNames from 'classnames/bind'
import CallMade from '@material-ui/icons/CallMade'
import { useDispatch, useSelector } from 'react-redux'
import Block from '~/components/layout/Block'
import Row from '~/components/layout/Row'
import { type Column, cellWidth } from '~/components/Table/TableHead'
import Table from '~/components/Table'
import Button from '~/components/layout/Button'
import { styles } from './style'
import type { OwnerRow } from '~/routes/safe/components/Settings/ManageOwners/dataFetcher'
import OwnerAddressTableCell from '~/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell'
import Img from '~/components/layout/Img'
import RenameOwnerIcon from '~/routes/safe/components/Settings/ManageOwners/assets/icons/rename-owner.svg'
import RemoveOwnerIcon from '~/routes/safe/components/Settings/assets/icons/bin.svg'
import {
AB_ADDRESS_ID,
ADDRESS_BOOK_ROW_ID,
EDIT_ENTRY_BUTTON,
generateColumns, REMOVE_ENTRY_BUTTON, SEND_ENTRY_BUTTON,
} from '~/routes/safe/components/AddressBook/columns'
import loadAddressBook from '~/logic/addressBook/store/actions/loadAddressBook'
import { getAddressBookListSelector } from '~/logic/addressBook/store/selectors'
import Col from '~/components/layout/Col'
import ButtonLink from '~/components/layout/ButtonLink'
type Props = {
classes: Object
}
const AddressBookTable = ({
classes,
}: Props) => {
const columns = generateColumns()
const autoColumns = columns.filter((c) => !c.custom)
const dispatch = useDispatch()
useEffect(() => {
dispatch(loadAddressBook())
}, [])
const addressBook = useSelector(getAddressBookListSelector)
return (
<>
<Row align="center" className={classes.message}>
<Col xs={12} end="sm">
<ButtonLink size="lg" onClick={() => {}} testId="manage-tokens-btn">
+ Create entry
</ButtonLink>
</Col>
</Row>
<Block className={classes.formContainer}>
<Table
label="Owners"
columns={columns}
data={addressBook}
size={addressBook.size}
defaultFixed
disableLoadingOnEmptyTable
defaultRowsPerPage={25}
>
{(sortedData: List<OwnerRow>) => sortedData.map((row: any, index: number) => (
<TableRow
tabIndex={-1}
key={index}
className={cn(classes.hide, index >= 3 && index === sortedData.size - 1 && classes.noBorderBottom)}
data-testid={ADDRESS_BOOK_ROW_ID}
>
{autoColumns.map((column: Column) => (
<TableCell key={column.id} style={cellWidth(column.width)} align={column.align} component="td">
{column.id === AB_ADDRESS_ID ? (
<OwnerAddressTableCell address={row[column.id]} showLinks />
) : (
row[column.id]
)}
</TableCell>
))}
<TableCell component="td">
<Row align="end" className={classes.actions}>
<Img
alt="Edit entry"
className={classes.editEntryButton}
src={RenameOwnerIcon}
onClick={() => {}}
testId={EDIT_ENTRY_BUTTON}
/>
<Img
alt="Remove entry"
className={classes.removeEntryButton}
src={RemoveOwnerIcon}
onClick={() => {}}
testId={REMOVE_ENTRY_BUTTON}
/>
<Button
variant="contained"
size="small"
color="primary"
className={classes.send}
onClick={() => {}}
testId={SEND_ENTRY_BUTTON}
>
<CallMade alt="Send Transaction" className={classNames(classes.leftIcon, classes.iconSmall)} />
Send
</Button>
</Row>
</TableCell>
</TableRow>
))}
</Table>
</Block>
</>
)
}
export default withStyles(styles)(AddressBookTable)

View File

@ -0,0 +1,71 @@
// @flow
import {
lg, md, sm, marginButtonImg,
} from '~/theme/variables'
export const styles = () => ({
formContainer: {
minHeight: '420px',
},
title: {
padding: lg,
paddingBottom: 0,
},
annotation: {
paddingLeft: lg,
},
hide: {
'&:hover': {
backgroundColor: '#fff3e2',
},
'&:hover $actions': {
visibility: 'initial',
},
},
actions: {
justifyContent: 'flex-end',
visibility: 'hidden',
minWidth: '100px',
},
noBorderBottom: {
'& > td': {
borderBottom: 'none',
},
},
controlsRow: {
backgroundColor: 'white',
padding: lg,
borderRadius: sm,
},
editEntryButton: {
cursor: 'pointer',
marginBottom: marginButtonImg,
},
removeEntryButton: {
marginLeft: lg,
marginRight: lg,
marginBottom: marginButtonImg,
cursor: 'pointer',
},
message: {
margin: `${sm} 0`,
padding: `${md} 0`,
maxHeight: '54px',
boxSizing: 'border-box',
justifyContent: 'flex-end',
},
send: {
width: '75px',
minWidth: '75px',
borderRadius: '4px',
'& > span': {
fontSize: '14px',
},
},
leftIcon: {
marginRight: sm,
},
iconSmall: {
fontSize: 16,
},
})

View File

@ -30,10 +30,12 @@ import Balances from './Balances'
import Transactions from './Transactions'
import Settings from './Settings'
import { styles } from './style'
import AddressBookTable from '~/routes/safe/components/AddressBook'
export const BALANCES_TAB_BTN_TEST_ID = 'balances-tab-btn'
export const SETTINGS_TAB_BTN_TEST_ID = 'settings-tab-btn'
export const TRANSACTIONS_TAB_BTN_TEST_ID = 'transactions-tab-btn'
export const ADDRESS_BOOK_TAB_BTN_TEST_ID = 'address-book-tab-btn'
export const SAFE_VIEW_NAME_HEADING_TEST_ID = 'safe-name-heading'
type Props = SelectorProps &
@ -149,6 +151,7 @@ const Layout = (props: Props) => {
>
<Tab label="Balances" value={`${match.url}/balances`} data-testid={BALANCES_TAB_BTN_TEST_ID} />
<Tab label="Transactions" value={`${match.url}/transactions`} data-testid={TRANSACTIONS_TAB_BTN_TEST_ID} />
<Tab label="Address Book" value={`${match.url}/address-book`} data-testid={ADDRESS_BOOK_TAB_BTN_TEST_ID} />
<Tab label="Settings" value={`${match.url}/settings`} data-testid={SETTINGS_TAB_BTN_TEST_ID} />
</Tabs>
</Row>
@ -212,6 +215,25 @@ const Layout = (props: Props) => {
/>
)}
/>
<Route
exact
path={`${match.path}/address-book`}
render={() => (
<AddressBookTable
granted={granted}
safeAddress={address}
safeName={name}
etherScanLink={etherScanLink}
updateSafe={updateSafe}
threshold={safe.threshold}
owners={safe.owners}
network={network}
userAddress={userAddress}
createTransaction={createTransaction}
safe={safe}
/>
)}
/>
<Redirect to={`${match.path}/balances`} />
</Switch>
<SendModal

View File

@ -3,17 +3,23 @@ import * as React from 'react'
import Block from '~/components/layout/Block'
import Paragraph from '~/components/layout/Paragraph'
import Identicon from '~/components/Identicon'
import EtherScanLink from '~/components/EtherscanLink'
type Props = {
address: string,
showLinks?: boolean,
}
const OwnerAddressTableCell = (props: Props) => {
const { address } = props
const { address, showLinks } = props
return (
<Block justify="left">
<Identicon address={address} diameter={32} />
<Paragraph style={{ marginLeft: 10 }}>{address}</Paragraph>
{ showLinks ? (
<div style={{ marginLeft: 10 }}>
<EtherScanLink type="address" value={address} />
</div>
) : <Paragraph style={{ marginLeft: 10 }}>{address}</Paragraph> }
</Block>
)
}

View File

@ -25,6 +25,7 @@ import notifications, {
import currencyValues, { CURRENCY_VALUES_KEY } from '~/logic/currencyValues/store/reducer/currencyValues'
import cookies, { COOKIES_REDUCER_ID } from '~/logic/cookies/store/reducer/cookies'
import notificationsMiddleware from '~/routes/safe/store/middleware/notificationsMiddleware'
import addressBook, { ADDRESS_BOOK_REDUCER_ID } from '~/logic/addressBook/store/reducer/addressBook'
export const history = createBrowserHistory()
@ -56,6 +57,7 @@ const reducers: Reducer<GlobalState> = combineReducers({
[NOTIFICATIONS_REDUCER_ID]: notifications,
[CURRENCY_VALUES_KEY]: currencyValues,
[COOKIES_REDUCER_ID]: cookies,
[ADDRESS_BOOK_REDUCER_ID]: addressBook,
})
export const store: Store<GlobalState> = createStore(reducers, finalCreateStore)