* Fix addressbook types Restructure addressbook store type * Add more safe types * Fix imports * Removes .toJS() usage * Fix condition for saving addressBook * Types & remove send button from addressbook if user not an owner * Add types for addressBook actions Remove unused saveAndUpdateAddressBook action * Refactor addressBook: make it global and removes immutableJS Add types Removes unused addAddressBook action * Remove todo * Fix edit and remove entries style when user is not owner * Adds and updates safe name in addressBook * Adds checkIfOwnerWasDeletedFromAddressBook Let the user remove owners users without adding them again each time the safe loads * Simplify loadAddressBookFromStorage * Fix compilation errors included in pr #1301 * Uses sameAddress function * Add migration function for old stored address books * Update tests * Replaces shouldAvoidUpdatesNotifications with addAddressBookEntryOptions on addAddressBookEntry * Update tests * Unify return on getOwnersWithNameFromAddressBook * Reword shouldAvoidUpdatesNotifications * Replaces adbk with addressBook * Fix condition * Fix typos * Fix typo Co-authored-by: Daniel Sanchez <daniel.sanchez@gnosis.pm>
This commit is contained in:
parent
fffabf02ce
commit
59dc1f711c
|
@ -1,15 +1,17 @@
|
|||
import { Record, RecordOf } from 'immutable'
|
||||
|
||||
export interface AddressBookEntryProps {
|
||||
export type AddressBookEntry = {
|
||||
address: string
|
||||
name: string
|
||||
isOwner: boolean
|
||||
}
|
||||
|
||||
export const makeAddressBookEntry = Record<AddressBookEntryProps>({
|
||||
address: '',
|
||||
name: '',
|
||||
isOwner: false,
|
||||
export const makeAddressBookEntry = ({
|
||||
address = '',
|
||||
name = '',
|
||||
}: {
|
||||
address: string
|
||||
name?: string
|
||||
}): AddressBookEntry => ({
|
||||
address,
|
||||
name,
|
||||
})
|
||||
|
||||
export type AddressBookEntryRecord = RecordOf<AddressBookEntryProps>
|
||||
export type AddressBookState = AddressBookEntry[]
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
import { createAction } from 'redux-actions'
|
||||
|
||||
export const ADD_ADDRESS_BOOK = 'ADD_ADDRESS_BOOK'
|
||||
|
||||
export const addAddressBook = createAction(ADD_ADDRESS_BOOK, (addressBook, safeAddress) => ({
|
||||
addressBook,
|
||||
safeAddress,
|
||||
}))
|
|
@ -1,7 +1,22 @@
|
|||
import { createAction } from 'redux-actions'
|
||||
import { AddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
|
||||
export const ADD_ENTRY = 'ADD_ENTRY'
|
||||
|
||||
export const addAddressBookEntry = createAction(ADD_ENTRY, (entry) => ({
|
||||
entry,
|
||||
}))
|
||||
type addAddressBookEntryOptions = {
|
||||
notifyEntryUpdate: boolean
|
||||
}
|
||||
|
||||
export const addAddressBookEntry = createAction(
|
||||
ADD_ENTRY,
|
||||
(entry: AddressBookEntry, options: addAddressBookEntryOptions) => {
|
||||
let notifyEntryUpdate = true
|
||||
if (options) {
|
||||
notifyEntryUpdate = options.notifyEntryUpdate
|
||||
}
|
||||
return {
|
||||
entry,
|
||||
shouldAvoidUpdatesNotifications: !notifyEntryUpdate,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { createAction } from 'redux-actions'
|
||||
import { AddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
|
||||
export const ADD_OR_UPDATE_ENTRY = 'ADD_OR_UPDATE_ENTRY'
|
||||
|
||||
export const addOrUpdateAddressBookEntry = createAction(ADD_OR_UPDATE_ENTRY, (entryAddress, entry) => ({
|
||||
entryAddress,
|
||||
export const addOrUpdateAddressBookEntry = createAction(ADD_OR_UPDATE_ENTRY, (entry: AddressBookEntry) => ({
|
||||
entry,
|
||||
}))
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { createAction } from 'redux-actions'
|
||||
import { AddressBookState } from 'src/logic/addressBook/model/addressBook'
|
||||
|
||||
export const LOAD_ADDRESS_BOOK = 'LOAD_ADDRESS_BOOK'
|
||||
|
||||
export const loadAddressBook = createAction(LOAD_ADDRESS_BOOK, (addressBook) => ({
|
||||
export const loadAddressBook = createAction(LOAD_ADDRESS_BOOK, (addressBook: AddressBookState) => ({
|
||||
addressBook,
|
||||
}))
|
||||
|
|
|
@ -1,29 +1,17 @@
|
|||
import { List } from 'immutable'
|
||||
|
||||
import { loadAddressBook } from 'src/logic/addressBook/store/actions/loadAddressBook'
|
||||
import { buildAddressBook } from 'src/logic/addressBook/store/reducer/addressBook'
|
||||
import { getAddressBookFromStorage } from 'src/logic/addressBook/utils'
|
||||
import { safesListSelector } from 'src/logic/safe/store/selectors'
|
||||
import { Dispatch } from 'redux'
|
||||
|
||||
const loadAddressBookFromStorage = () => async (dispatch, getState) => {
|
||||
const loadAddressBookFromStorage = () => async (dispatch: Dispatch): Promise<void> => {
|
||||
try {
|
||||
const state = getState()
|
||||
let storedAdBk = await getAddressBookFromStorage()
|
||||
if (!storedAdBk) {
|
||||
storedAdBk = []
|
||||
}
|
||||
|
||||
let addressBook = buildAddressBook(storedAdBk)
|
||||
// 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) => {
|
||||
const { address } = safe
|
||||
const found = adbkEntries.includes(address)
|
||||
if (!found) {
|
||||
addressBook = addressBook.set(address, List([]))
|
||||
}
|
||||
})
|
||||
const addressBook = buildAddressBook(storedAdBk)
|
||||
|
||||
dispatch(loadAddressBook(addressBook))
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line
|
||||
|
|
|
@ -2,6 +2,6 @@ import { createAction } from 'redux-actions'
|
|||
|
||||
export const REMOVE_ENTRY = 'REMOVE_ENTRY'
|
||||
|
||||
export const removeAddressBookEntry = createAction(REMOVE_ENTRY, (entryAddress) => ({
|
||||
export const removeAddressBookEntry = createAction(REMOVE_ENTRY, (entryAddress: string) => ({
|
||||
entryAddress,
|
||||
}))
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
import { updateAddressBookEntry } from 'src/logic/addressBook/store/actions/updateAddressBookEntry'
|
||||
import { saveAddressBook } from 'src/logic/addressBook/utils'
|
||||
|
||||
const saveAndUpdateAddressBook = (addressBook) => async (dispatch) => {
|
||||
try {
|
||||
dispatch(updateAddressBookEntry(makeAddressBookEntry(addressBook)))
|
||||
await saveAddressBook(addressBook)
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line
|
||||
console.error('Error while loading active tokens from storage:', err)
|
||||
}
|
||||
}
|
||||
|
||||
export default saveAndUpdateAddressBook
|
|
@ -1,7 +1,8 @@
|
|||
import { createAction } from 'redux-actions'
|
||||
import { AddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
|
||||
export const UPDATE_ENTRY = 'UPDATE_ENTRY'
|
||||
|
||||
export const updateAddressBookEntry = createAction(UPDATE_ENTRY, (entry) => ({
|
||||
export const updateAddressBookEntry = createAction(UPDATE_ENTRY, (entry: AddressBookEntry) => ({
|
||||
entry,
|
||||
}))
|
||||
|
|
|
@ -2,7 +2,7 @@ import { ADD_ENTRY } from 'src/logic/addressBook/store/actions/addAddressBookEnt
|
|||
import { ADD_OR_UPDATE_ENTRY } from 'src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry'
|
||||
import { REMOVE_ENTRY } from 'src/logic/addressBook/store/actions/removeAddressBookEntry'
|
||||
import { UPDATE_ENTRY } from 'src/logic/addressBook/store/actions/updateAddressBookEntry'
|
||||
import { addressBookMapSelector } from 'src/logic/addressBook/store/selectors'
|
||||
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
|
||||
import { saveAddressBook } from 'src/logic/addressBook/utils'
|
||||
import { enhanceSnackbarForAction, getNotificationsFromTxType } from 'src/logic/notifications'
|
||||
import enqueueSnackbar from 'src/logic/notifications/store/actions/enqueueSnackbar'
|
||||
|
@ -16,15 +16,15 @@ const addressBookMiddleware = (store) => (next) => async (action) => {
|
|||
if (watchedActions.includes(action.type)) {
|
||||
const state = store.getState()
|
||||
const { dispatch } = store
|
||||
const addressBook = addressBookMapSelector(state)
|
||||
if (addressBook) {
|
||||
const addressBook = addressBookSelector(state)
|
||||
if (addressBook.length) {
|
||||
await saveAddressBook(addressBook)
|
||||
}
|
||||
|
||||
switch (action.type) {
|
||||
case ADD_ENTRY: {
|
||||
const { isOwner } = action.payload.entry
|
||||
if (!isOwner) {
|
||||
const { shouldAvoidUpdatesNotifications } = action.payload
|
||||
if (!shouldAvoidUpdatesNotifications) {
|
||||
const notification = getNotificationsFromTxType(TX_NOTIFICATION_TYPES.ADDRESSBOOK_NEW_ENTRY)
|
||||
dispatch(enqueueSnackbar(enhanceSnackbarForAction(notification.afterExecution.noMoreConfirmationsNeeded)))
|
||||
}
|
||||
|
|
|
@ -1,134 +1,71 @@
|
|||
import { List, Map } from 'immutable'
|
||||
import { handleActions } from 'redux-actions'
|
||||
|
||||
import {
|
||||
AddressBookEntryRecord,
|
||||
AddressBookEntryProps,
|
||||
makeAddressBookEntry,
|
||||
} from 'src/logic/addressBook/model/addressBook'
|
||||
import { ADD_ADDRESS_BOOK } from 'src/logic/addressBook/store/actions/addAddressBook'
|
||||
import { AddressBookState, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
import { ADD_ENTRY } from 'src/logic/addressBook/store/actions/addAddressBookEntry'
|
||||
import { ADD_OR_UPDATE_ENTRY } from 'src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry'
|
||||
import { LOAD_ADDRESS_BOOK } from 'src/logic/addressBook/store/actions/loadAddressBook'
|
||||
import { REMOVE_ENTRY } from 'src/logic/addressBook/store/actions/removeAddressBookEntry'
|
||||
import { UPDATE_ENTRY } from 'src/logic/addressBook/store/actions/updateAddressBookEntry'
|
||||
import { getAddressesListFromSafeAddressBook } from 'src/logic/addressBook/utils'
|
||||
import { sameAddress } from 'src/logic/wallets/ethAddresses'
|
||||
import { checksumAddress } from 'src/utils/checksumAddress'
|
||||
import { getValidAddressBookName } from 'src/logic/addressBook/utils'
|
||||
|
||||
export const ADDRESS_BOOK_REDUCER_ID = 'addressBook'
|
||||
|
||||
export type AddressBookCollection = List<AddressBookEntryRecord>
|
||||
export type AddressBookState = Map<string, Map<string, AddressBookCollection>>
|
||||
|
||||
export const buildAddressBook = (storedAdbk: AddressBookEntryProps[]): Map<string, AddressBookCollection> => {
|
||||
let addressBookBuilt: Map<string, AddressBookCollection> = Map([])
|
||||
Object.entries(storedAdbk).forEach((adbkProps: any) => {
|
||||
const safeAddress = checksumAddress(adbkProps[0])
|
||||
const adbkRecords: AddressBookEntryRecord[] = adbkProps[1].map(makeAddressBookEntry)
|
||||
const adbkSafeEntries = List(adbkRecords)
|
||||
addressBookBuilt = addressBookBuilt.set(safeAddress, adbkSafeEntries)
|
||||
export const buildAddressBook = (storedAdbk: AddressBookState): AddressBookState => {
|
||||
return storedAdbk.map((addressBookEntry) => {
|
||||
const { address, name } = addressBookEntry
|
||||
return makeAddressBookEntry({ address: checksumAddress(address), name })
|
||||
})
|
||||
return addressBookBuilt
|
||||
}
|
||||
|
||||
export default handleActions(
|
||||
{
|
||||
[LOAD_ADDRESS_BOOK]: (state, action) => {
|
||||
const { addressBook } = action.payload
|
||||
return state.set('addressBook', addressBook)
|
||||
},
|
||||
[ADD_ADDRESS_BOOK]: (state, action) => {
|
||||
const { addressBook, safeAddress } = action.payload
|
||||
// Adds the address book if it does not exists
|
||||
const found = state.getIn(['addressBook', safeAddress])
|
||||
if (!found) {
|
||||
return state.setIn(['addressBook', safeAddress], addressBook)
|
||||
}
|
||||
return state
|
||||
return addressBook
|
||||
},
|
||||
[ADD_ENTRY]: (state, action) => {
|
||||
const { entry } = action.payload
|
||||
|
||||
// Adds the entry to all the safes (if it does not already exists)
|
||||
const newState = state.withMutations((map) => {
|
||||
const adbkMap = map.get('addressBook')
|
||||
const entryFound = state.find((oldEntry) => oldEntry.address === entry.address)
|
||||
|
||||
if (adbkMap) {
|
||||
adbkMap.keySeq().forEach((safeAddress) => {
|
||||
const safeAddressBook = state.getIn(['addressBook', safeAddress])
|
||||
|
||||
if (safeAddressBook) {
|
||||
const adbkAddressList = getAddressesListFromSafeAddressBook(safeAddressBook)
|
||||
const found = adbkAddressList.includes(entry.address)
|
||||
if (!found) {
|
||||
const updatedSafeAdbkList = safeAddressBook.push(entry)
|
||||
map.setIn(['addressBook', safeAddress], updatedSafeAdbkList)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
return newState
|
||||
// Only adds entries with valid names
|
||||
const validName = getValidAddressBookName(entry.name)
|
||||
if (!entryFound && validName) {
|
||||
state.push(entry)
|
||||
}
|
||||
return state
|
||||
},
|
||||
[UPDATE_ENTRY]: (state, action) => {
|
||||
const { entry } = action.payload
|
||||
|
||||
// Updates the entry from all the safes
|
||||
const newState = state.withMutations((map) => {
|
||||
map
|
||||
.get('addressBook')
|
||||
.keySeq()
|
||||
.forEach((safeAddress) => {
|
||||
const entriesList = state.getIn(['addressBook', safeAddress])
|
||||
const entryIndex = entriesList.findIndex((entryItem) => sameAddress(entryItem.address, entry.address))
|
||||
const updatedEntriesList = entriesList.set(entryIndex, entry)
|
||||
map.setIn(['addressBook', safeAddress], updatedEntriesList)
|
||||
})
|
||||
})
|
||||
|
||||
return newState
|
||||
const entryIndex = state.findIndex((oldEntry) => oldEntry.address === entry.address)
|
||||
if (entryIndex >= 0) {
|
||||
state[entryIndex] = entry
|
||||
}
|
||||
return state
|
||||
},
|
||||
[REMOVE_ENTRY]: (state, action) => {
|
||||
const { entryAddress } = action.payload
|
||||
// Removes the entry from all the safes
|
||||
const newState = state.withMutations((map) => {
|
||||
map
|
||||
.get('addressBook')
|
||||
.keySeq()
|
||||
.forEach((safeAddress) => {
|
||||
const entriesList = state.getIn(['addressBook', safeAddress])
|
||||
const entryIndex = entriesList.findIndex((entry) => sameAddress(entry.address, entryAddress))
|
||||
const updatedEntriesList = entriesList.remove(entryIndex)
|
||||
map.setIn(['addressBook', safeAddress], updatedEntriesList)
|
||||
})
|
||||
})
|
||||
return newState
|
||||
const entryIndex = state.findIndex((oldEntry) => oldEntry.address === entryAddress)
|
||||
state.splice(entryIndex, 1)
|
||||
return state
|
||||
},
|
||||
[ADD_OR_UPDATE_ENTRY]: (state, action) => {
|
||||
const { entry, entryAddress } = action.payload
|
||||
|
||||
// Adds or Updates the entry to all the safes
|
||||
return state.withMutations((map) => {
|
||||
const addressBook = map.get('addressBook')
|
||||
if (addressBook) {
|
||||
addressBook.keySeq().forEach((safeAddress) => {
|
||||
const safeAddressBook = state.getIn(['addressBook', safeAddress])
|
||||
const entryIndex = safeAddressBook.findIndex((entryItem) => sameAddress(entryItem.address, entryAddress))
|
||||
|
||||
if (entryIndex !== -1) {
|
||||
const updatedEntriesList = safeAddressBook.update(entryIndex, (currentEntry) => currentEntry.merge(entry))
|
||||
map.setIn(['addressBook', safeAddress], updatedEntriesList)
|
||||
} else {
|
||||
const updatedSafeAdbkList = safeAddressBook.push(makeAddressBookEntry(entry))
|
||||
map.setIn(['addressBook', safeAddress], updatedSafeAdbkList)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
// Only updates entries with valid names
|
||||
const validName = getValidAddressBookName(entry.name)
|
||||
if (!validName) {
|
||||
return state
|
||||
}
|
||||
const entryIndex = state.findIndex((oldEntry) => oldEntry.address === entryAddress)
|
||||
if (entryIndex >= 0) {
|
||||
state[entryIndex] = entry
|
||||
} else {
|
||||
state.push(entry)
|
||||
}
|
||||
return state
|
||||
},
|
||||
},
|
||||
Map({
|
||||
addressBook: Map({}),
|
||||
}),
|
||||
[],
|
||||
)
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
import { AddressBookEntryRecord, AddressBookEntryProps } from 'src/logic/addressBook/model/addressBook'
|
||||
import { Map, List } from 'immutable'
|
||||
|
||||
export interface AddressBookReducerState {
|
||||
addressBook: AddressBookMap
|
||||
}
|
||||
|
||||
interface AddressBookMapSerialized {
|
||||
[key: string]: AddressBookEntryProps
|
||||
}
|
||||
|
||||
interface AddressBookReducerStateSerialized extends AddressBookReducerState {
|
||||
addressBook: Record<string, AddressBookEntryProps[]>
|
||||
}
|
||||
|
||||
export interface AddressBookMap extends Map<string> {
|
||||
toJS(): AddressBookMapSerialized
|
||||
get(key: string, notSetValue: unknown): List<AddressBookEntryRecord>
|
||||
}
|
||||
|
||||
export interface AddressBookReducerMap extends Map<string, any> {
|
||||
toJS(): AddressBookReducerStateSerialized
|
||||
get<K extends keyof AddressBookReducerState>(key: K): AddressBookReducerState[K]
|
||||
}
|
|
@ -1,36 +1,22 @@
|
|||
import { AppReduxState } from 'src/store'
|
||||
import { List } from 'immutable'
|
||||
|
||||
import { createSelector } from 'reselect'
|
||||
|
||||
import { ADDRESS_BOOK_REDUCER_ID } from 'src/logic/addressBook/store/reducer/addressBook'
|
||||
import { AddressBookMap } from 'src/logic/addressBook/store/reducer/types/addressBook.d'
|
||||
import { AddressBookEntryRecord } from 'src/logic/addressBook/model/addressBook'
|
||||
import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
|
||||
|
||||
export const addressBookMapSelector = (state: AppReduxState): AddressBookMap =>
|
||||
state[ADDRESS_BOOK_REDUCER_ID].get('addressBook')
|
||||
import { AddressBookState } from 'src/logic/addressBook/model/addressBook'
|
||||
|
||||
export const getAddressBook = createSelector(
|
||||
addressBookMapSelector,
|
||||
safeParamAddressFromStateSelector,
|
||||
(addressBook, safeAddress) => {
|
||||
let result: List<AddressBookEntryRecord> = List([])
|
||||
if (addressBook && safeAddress) {
|
||||
result = addressBook.get(safeAddress, List())
|
||||
}
|
||||
return result
|
||||
},
|
||||
)
|
||||
export const addressBookSelector = (state: AppReduxState): AddressBookState => state[ADDRESS_BOOK_REDUCER_ID]
|
||||
|
||||
export const getNameFromAddressBook = createSelector(
|
||||
getAddressBook,
|
||||
addressBookSelector,
|
||||
(_, address) => address,
|
||||
(addressBook, address) => {
|
||||
const adbkEntry = addressBook.find((addressBookItem) => addressBookItem.address === address)
|
||||
|
||||
if (adbkEntry) {
|
||||
return adbkEntry.name
|
||||
}
|
||||
|
||||
return 'UNKNOWN'
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,25 +1,31 @@
|
|||
import { Map, List } from 'immutable'
|
||||
import { List } from 'immutable'
|
||||
import {
|
||||
getAddressBookFromStorage,
|
||||
getAddressesListFromSafeAddressBook,
|
||||
getNameFromSafeAddressBook,
|
||||
getAddressesListFromAddressBook,
|
||||
getNameFromAddressBook,
|
||||
getOwnersWithNameFromAddressBook,
|
||||
migrateOldAddressBook,
|
||||
OldAddressBookEntry,
|
||||
OldAddressBookType,
|
||||
saveAddressBook,
|
||||
} from 'src/logic/addressBook/utils/index'
|
||||
import { buildAddressBook } from 'src/logic/addressBook/store/reducer/addressBook'
|
||||
import { AddressBookEntryRecord, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
import { AddressBookEntry, AddressBookState, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
|
||||
const getMockAddressBookEntry = (
|
||||
address: string,
|
||||
name: string = 'test',
|
||||
isOwner: boolean = false,
|
||||
): AddressBookEntryRecord =>
|
||||
const getMockAddressBookEntry = (address: string, name: string = 'test'): AddressBookEntry =>
|
||||
makeAddressBookEntry({
|
||||
address,
|
||||
name,
|
||||
isOwner,
|
||||
})
|
||||
|
||||
const getMockOldAddressBookEntry = ({ address = '', name = '', isOwner = false }): OldAddressBookEntry => {
|
||||
return {
|
||||
address,
|
||||
name,
|
||||
isOwner,
|
||||
}
|
||||
}
|
||||
|
||||
describe('getAddressesListFromAdbk', () => {
|
||||
const entry1 = getMockAddressBookEntry('123456', 'test1')
|
||||
const entry2 = getMockAddressBookEntry('78910', 'test2')
|
||||
|
@ -27,11 +33,11 @@ describe('getAddressesListFromAdbk', () => {
|
|||
|
||||
it('It should returns the list of addresses within the addressBook given a safeAddressBook', () => {
|
||||
// given
|
||||
const safeAddressBook = List([entry1, entry2, entry3])
|
||||
const safeAddressBook = [entry1, entry2, entry3]
|
||||
const expectedResult = [entry1.address, entry2.address, entry3.address]
|
||||
|
||||
// when
|
||||
const result = getAddressesListFromSafeAddressBook(safeAddressBook)
|
||||
const result = getAddressesListFromAddressBook(safeAddressBook)
|
||||
|
||||
// then
|
||||
expect(result).toStrictEqual(expectedResult)
|
||||
|
@ -44,11 +50,11 @@ describe('getNameFromSafeAddressBook', () => {
|
|||
const entry3 = getMockAddressBookEntry('4781321', 'test3')
|
||||
it('It should returns the user name given a safeAddressBook and an user account', () => {
|
||||
// given
|
||||
const safeAddressBook = List([entry1, entry2, entry3])
|
||||
const safeAddressBook = [entry1, entry2, entry3]
|
||||
const expectedResult = entry2.name
|
||||
|
||||
// when
|
||||
const result = getNameFromSafeAddressBook(safeAddressBook, entry2.address)
|
||||
const result = getNameFromAddressBook(safeAddressBook, entry2.address)
|
||||
|
||||
// then
|
||||
expect(result).toBe(expectedResult)
|
||||
|
@ -61,7 +67,7 @@ describe('getOwnersWithNameFromAddressBook', () => {
|
|||
const entry3 = getMockAddressBookEntry('4781321', 'test3')
|
||||
it('It should returns the list of owners with their names given a safeAddressBook and a list of owners', () => {
|
||||
// given
|
||||
const safeAddressBook = List([entry1, entry2, entry3])
|
||||
const safeAddressBook = [entry1, entry2, entry3]
|
||||
const ownerList = List([
|
||||
{ address: entry1.address, name: '' },
|
||||
{ address: entry2.address, name: '' },
|
||||
|
@ -80,14 +86,15 @@ describe('getOwnersWithNameFromAddressBook', () => {
|
|||
})
|
||||
|
||||
describe('saveAddressBook', () => {
|
||||
const safeAddress1 = '0xdfA693da0D16F5E7E78FdCBeDe8FC6eBEa44f1Cf'
|
||||
const safeAddress2 = '0x344B941b1aAE2e4Be73987212FC4741687Bf0503'
|
||||
const entry1 = getMockAddressBookEntry('123456', 'test1')
|
||||
const entry2 = getMockAddressBookEntry('78910', 'test2')
|
||||
const entry3 = getMockAddressBookEntry('4781321', 'test3')
|
||||
const mockAdd1 = '0x696fd93D725d84acfFf6c62a1fe8C94E1c9E934A'
|
||||
const mockAdd2 = '0x2C7aC78b01Be0FC66AD29b684ffAb0C93B381D00'
|
||||
const mockAdd3 = '0x537BD452c3505FC07bA242E437bD29D4E1DC9126'
|
||||
const entry1 = getMockAddressBookEntry(mockAdd1, 'test1')
|
||||
const entry2 = getMockAddressBookEntry(mockAdd2, 'test2')
|
||||
const entry3 = getMockAddressBookEntry(mockAdd3, 'test3')
|
||||
it('It should save a given addressBook to the localStorage', async () => {
|
||||
// given
|
||||
const addressBook = Map({ [safeAddress1]: List([entry1, entry2]), [safeAddress2]: List([entry3]) })
|
||||
const addressBook: AddressBookState = [entry1, entry2, entry3]
|
||||
|
||||
// when
|
||||
// @ts-ignore
|
||||
|
@ -100,3 +107,95 @@ describe('saveAddressBook', () => {
|
|||
expect(result).toStrictEqual(addressBook)
|
||||
})
|
||||
})
|
||||
|
||||
describe('migrateOldAddressBook', () => {
|
||||
const safeAddress1 = '0x696fd93D725d84acfFf6c62a1fe8C94E1c9E934A'
|
||||
const safeAddress2 = '0x2C7aC78b01Be0FC66AD29b684ffAb0C93B381D00'
|
||||
const mockAdd1 = '0x9163c2F4452E3399CB60AAf737231Af87548DA91'
|
||||
const mockAdd2 = '0xC4e446Da9C3D37385C86488294C6758c4e25dbD8'
|
||||
|
||||
it('It should receive an addressBook in old format and return the same addressBook in new format', () => {
|
||||
// given
|
||||
const entry1 = getMockOldAddressBookEntry({ name: 'test1', address: mockAdd1 })
|
||||
const entry2 = getMockOldAddressBookEntry({ name: 'test2', address: mockAdd2 })
|
||||
|
||||
const oldAddressBook: OldAddressBookType = {
|
||||
[safeAddress1]: [entry1],
|
||||
[safeAddress2]: [entry2],
|
||||
}
|
||||
|
||||
const expectedEntry1 = getMockAddressBookEntry(mockAdd1, 'test1')
|
||||
const expectedEntry2 = getMockAddressBookEntry(mockAdd2, 'test2')
|
||||
const expectedResult = [expectedEntry1, expectedEntry2]
|
||||
|
||||
// when
|
||||
const result = migrateOldAddressBook(oldAddressBook)
|
||||
|
||||
// then
|
||||
expect(result).toStrictEqual(expectedResult)
|
||||
})
|
||||
})
|
||||
|
||||
jest.mock('src/utils/storage/index')
|
||||
describe('getAddressBookFromStorage', () => {
|
||||
const safeAddress1 = '0x696fd93D725d84acfFf6c62a1fe8C94E1c9E934A'
|
||||
const safeAddress2 = '0x2C7aC78b01Be0FC66AD29b684ffAb0C93B381D00'
|
||||
const mockAdd1 = '0x9163c2F4452E3399CB60AAf737231Af87548DA91'
|
||||
const mockAdd2 = '0xC4e446Da9C3D37385C86488294C6758c4e25dbD8'
|
||||
afterAll(() => {
|
||||
jest.unmock('src/utils/storage/index')
|
||||
})
|
||||
it('It should return null if no addressBook in storage', async () => {
|
||||
// given
|
||||
|
||||
const expectedResult = null
|
||||
const storageUtils = require('src/utils/storage/index')
|
||||
const spy = storageUtils.loadFromStorage.mockImplementationOnce(() => null)
|
||||
|
||||
// when
|
||||
const result = await getAddressBookFromStorage()
|
||||
|
||||
// then
|
||||
expect(result).toStrictEqual(expectedResult)
|
||||
expect(spy).toHaveBeenCalled()
|
||||
})
|
||||
it('It should return migrated addressBook if old addressBook in storage', async () => {
|
||||
// given
|
||||
const expectedEntry1 = getMockAddressBookEntry(mockAdd1, 'test1')
|
||||
const expectedEntry2 = getMockAddressBookEntry(mockAdd2, 'test2')
|
||||
const entry1 = getMockOldAddressBookEntry({ name: 'test1', address: mockAdd1 })
|
||||
const entry2 = getMockOldAddressBookEntry({ name: 'test2', address: mockAdd2 })
|
||||
const oldAddressBook: OldAddressBookType = {
|
||||
[safeAddress1]: [entry1],
|
||||
[safeAddress2]: [entry2],
|
||||
}
|
||||
const expectedResult = [expectedEntry1, expectedEntry2]
|
||||
|
||||
const storageUtils = require('src/utils/storage/index')
|
||||
const spy = storageUtils.loadFromStorage.mockImplementationOnce(() => oldAddressBook)
|
||||
|
||||
// when
|
||||
const result = await getAddressBookFromStorage()
|
||||
|
||||
// then
|
||||
expect(result).toStrictEqual(expectedResult)
|
||||
expect(spy).toHaveBeenCalled()
|
||||
})
|
||||
it('It should return addressBook if addressBook in storage', async () => {
|
||||
// given
|
||||
const expectedEntry1 = getMockAddressBookEntry(mockAdd1, 'test1')
|
||||
const expectedEntry2 = getMockAddressBookEntry(mockAdd2, 'test2')
|
||||
|
||||
const expectedResult = [expectedEntry1, expectedEntry2]
|
||||
|
||||
const storageUtils = require('src/utils/storage/index')
|
||||
const spy = storageUtils.loadFromStorage.mockImplementationOnce(() => JSON.stringify(expectedResult))
|
||||
|
||||
// when
|
||||
const result = await getAddressBookFromStorage()
|
||||
|
||||
// then
|
||||
expect(result).toStrictEqual(expectedResult)
|
||||
expect(spy).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,28 +1,62 @@
|
|||
import { List } from 'immutable'
|
||||
import { loadFromStorage, saveToStorage } from 'src/utils/storage'
|
||||
import { AddressBookEntryRecord, AddressBookEntryProps } from '../model/addressBook'
|
||||
import { AddressBookState, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
import { SafeOwner } from 'src/logic/safe/store/models/safe'
|
||||
import { AddressBookCollection } from '../store/reducer/addressBook'
|
||||
import { AddressBookMap } from '../store/reducer/types/addressBook'
|
||||
import { sameAddress } from 'src/logic/wallets/ethAddresses'
|
||||
|
||||
const ADDRESS_BOOK_STORAGE_KEY = 'ADDRESS_BOOK_STORAGE_KEY'
|
||||
|
||||
export const getAddressBookFromStorage = async (): Promise<Array<AddressBookEntryProps> | undefined> => {
|
||||
return await loadFromStorage<Array<AddressBookEntryProps>>(ADDRESS_BOOK_STORAGE_KEY)
|
||||
export type OldAddressBookEntry = {
|
||||
address: string
|
||||
name: string
|
||||
isOwner: boolean
|
||||
}
|
||||
|
||||
export const saveAddressBook = async (addressBook: AddressBookMap): Promise<void> => {
|
||||
export type OldAddressBookType = {
|
||||
[safeAddress: string]: [OldAddressBookEntry]
|
||||
}
|
||||
|
||||
export const migrateOldAddressBook = (oldAddressBook: OldAddressBookType): AddressBookState => {
|
||||
const values: AddressBookState = []
|
||||
const adbkValues = Object.values(oldAddressBook)
|
||||
|
||||
for (const safeIterator of adbkValues) {
|
||||
for (const safeAddressBook of safeIterator) {
|
||||
if (!values.find((entry) => sameAddress(entry.address, safeAddressBook.address))) {
|
||||
values.push(makeAddressBookEntry({ address: safeAddressBook.address, name: safeAddressBook.name }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
export const getAddressBookFromStorage = async (): Promise<AddressBookState | null> => {
|
||||
const result: OldAddressBookType | string | undefined = await loadFromStorage(ADDRESS_BOOK_STORAGE_KEY)
|
||||
|
||||
if (!result) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (typeof result === 'string') {
|
||||
return JSON.parse(result)
|
||||
}
|
||||
|
||||
return migrateOldAddressBook(result as OldAddressBookType)
|
||||
}
|
||||
|
||||
export const saveAddressBook = async (addressBook: AddressBookState): Promise<void> => {
|
||||
try {
|
||||
await saveToStorage(ADDRESS_BOOK_STORAGE_KEY, addressBook.toJS())
|
||||
await saveToStorage(ADDRESS_BOOK_STORAGE_KEY, JSON.stringify(addressBook))
|
||||
} catch (err) {
|
||||
console.error('Error storing addressBook in localstorage', err)
|
||||
}
|
||||
}
|
||||
|
||||
export const getAddressesListFromSafeAddressBook = (addressBook: AddressBookCollection): string[] =>
|
||||
Array.from(addressBook).map((entry: AddressBookEntryRecord) => entry.address)
|
||||
export const getAddressesListFromAddressBook = (addressBook: AddressBookState): string[] =>
|
||||
addressBook.map((entry) => entry.address)
|
||||
|
||||
export const getNameFromSafeAddressBook = (addressBook: AddressBookCollection, userAddress: string): string | null => {
|
||||
export const getNameFromAddressBook = (addressBook: AddressBookState, userAddress: string): string | null => {
|
||||
const entry = addressBook.find((addressBookItem) => addressBookItem.address === userAddress)
|
||||
if (entry) {
|
||||
return entry.name
|
||||
|
@ -30,15 +64,22 @@ export const getNameFromSafeAddressBook = (addressBook: AddressBookCollection, u
|
|||
return null
|
||||
}
|
||||
|
||||
export const getValidAddressBookName = (addressbookName: string): string | null => {
|
||||
const INVALID_NAMES = ['UNKNOWN', 'OWNER #', 'MY WALLET']
|
||||
const isInvalid = INVALID_NAMES.find((invalidName) => addressbookName.toUpperCase().includes(invalidName))
|
||||
if (isInvalid) return null
|
||||
return addressbookName
|
||||
}
|
||||
|
||||
export const getOwnersWithNameFromAddressBook = (
|
||||
addressBook: AddressBookCollection,
|
||||
addressBook: AddressBookState,
|
||||
ownerList: List<SafeOwner>,
|
||||
): List<SafeOwner> | [] => {
|
||||
): List<SafeOwner> => {
|
||||
if (!ownerList) {
|
||||
return []
|
||||
return List([])
|
||||
}
|
||||
return ownerList.map((owner) => {
|
||||
const ownerName = getNameFromSafeAddressBook(addressBook, owner.address)
|
||||
const ownerName = getNameFromAddressBook(addressBook, owner.address)
|
||||
return {
|
||||
address: owner.address,
|
||||
name: ownerName || owner.name,
|
||||
|
|
|
@ -18,15 +18,16 @@ export const buildOwnersFrom = (names: string[], addresses: string[]): List<Safe
|
|||
return List(owners)
|
||||
}
|
||||
|
||||
export const addSafe = createAction(ADD_SAFE, (safe) => ({
|
||||
export const addSafe = createAction(ADD_SAFE, (safe: SafeRecordProps, loadedFromStorage = false) => ({
|
||||
safe,
|
||||
loadedFromStorage,
|
||||
}))
|
||||
|
||||
const saveSafe = (safe: SafeRecordProps) => (dispatch: Dispatch, getState: () => AppReduxState): void => {
|
||||
const state = getState()
|
||||
const safeList = safesListSelector(state)
|
||||
|
||||
dispatch(addSafe(safe))
|
||||
dispatch(addSafe(safe, true))
|
||||
|
||||
if (safeList.size === 0) {
|
||||
dispatch(setDefaultSafe(safe.address))
|
||||
|
|
|
@ -13,7 +13,7 @@ const loadSafesFromStorage = () => async (dispatch: Dispatch): Promise<void> =>
|
|||
|
||||
if (safes) {
|
||||
Object.values(safes).forEach((safeProps) => {
|
||||
dispatch(addSafe(buildSafe(safeProps)))
|
||||
dispatch(addSafe(buildSafe(safeProps), true))
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
import { addAddressBookEntry } from 'src/logic/addressBook/store/actions/addAddressBookEntry'
|
||||
import { saveDefaultSafe, saveSafes } from 'src/logic/safe/utils'
|
||||
import { tokensSelector } from 'src/logic/tokens/store/selectors'
|
||||
|
@ -13,6 +12,12 @@ import { REPLACE_SAFE_OWNER } from 'src/logic/safe/store/actions/replaceSafeOwne
|
|||
import { SET_DEFAULT_SAFE } from 'src/logic/safe/store/actions/setDefaultSafe'
|
||||
import { UPDATE_SAFE } from 'src/logic/safe/store/actions/updateSafe'
|
||||
import { getActiveTokensAddressesForAllSafes, safesMapSelector } from 'src/logic/safe/store/selectors'
|
||||
import { checksumAddress } from 'src/utils/checksumAddress'
|
||||
import { AddressBookEntry, AddressBookState, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
import { addOrUpdateAddressBookEntry } from 'src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry'
|
||||
import { getValidAddressBookName } from 'src/logic/addressBook/utils'
|
||||
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
|
||||
import { sameAddress } from 'src/logic/wallets/ethAddresses'
|
||||
|
||||
const watchedActions = [
|
||||
ADD_SAFE,
|
||||
|
@ -41,6 +46,30 @@ const recalculateActiveTokens = (state) => {
|
|||
saveActiveTokens(activeTokens)
|
||||
}
|
||||
|
||||
/**
|
||||
* If the owner has a valid name that means that should be on the addressBook
|
||||
* if the owner is not currently on the addressBook, that means the user deleted it
|
||||
* or that it's a new safe with valid names, so we also check if it's a new safe or an already loaded one
|
||||
* @param name
|
||||
* @param address
|
||||
* @param addressBook
|
||||
* @param safeAlreadyLoaded -> true if the safe was loaded from the localStorage
|
||||
*/
|
||||
// TODO TEST
|
||||
const checkIfOwnerWasDeletedFromAddressBook = (
|
||||
{ name, address }: AddressBookEntry,
|
||||
addressBook: AddressBookState,
|
||||
safeAlreadyLoaded: boolean,
|
||||
) => {
|
||||
if (!safeAlreadyLoaded) {
|
||||
return false
|
||||
}
|
||||
|
||||
const addressShouldBeOnTheAddressBook = !!getValidAddressBookName(name)
|
||||
const isAlreadyInAddressBook = !!addressBook.find((entry) => sameAddress(entry.address, address))
|
||||
return addressShouldBeOnTheAddressBook && !isAlreadyInAddressBook
|
||||
}
|
||||
|
||||
const safeStorageMware = (store) => (next) => async (action) => {
|
||||
const handledAction = next(action)
|
||||
|
||||
|
@ -48,6 +77,7 @@ const safeStorageMware = (store) => (next) => async (action) => {
|
|||
const state = store.getState()
|
||||
const { dispatch } = store
|
||||
const safes = safesMapSelector(state)
|
||||
const addressBook = addressBookSelector(state)
|
||||
await saveSafes(safes.toJSON())
|
||||
|
||||
switch (action.type) {
|
||||
|
@ -56,19 +86,42 @@ const safeStorageMware = (store) => (next) => async (action) => {
|
|||
break
|
||||
}
|
||||
case ADD_SAFE: {
|
||||
const { safe } = action.payload
|
||||
const ownersArray = safe.owners.toJS()
|
||||
// Adds the owners to the address book
|
||||
ownersArray.forEach((owner) => {
|
||||
dispatch(addAddressBookEntry(makeAddressBookEntry({ ...owner, isOwner: true })))
|
||||
const { safe, loadedFromStorage } = action.payload
|
||||
safe.owners.forEach((owner) => {
|
||||
const checksumEntry = makeAddressBookEntry({ address: checksumAddress(owner.address), name: owner.name })
|
||||
const ownerWasAlreadyInAddressBook = checkIfOwnerWasDeletedFromAddressBook(
|
||||
checksumEntry,
|
||||
addressBook,
|
||||
loadedFromStorage,
|
||||
)
|
||||
|
||||
if (!ownerWasAlreadyInAddressBook) {
|
||||
dispatch(addAddressBookEntry(checksumEntry, { notifyEntryUpdate: false }))
|
||||
}
|
||||
})
|
||||
const safeWasAlreadyInAddressBook = checkIfOwnerWasDeletedFromAddressBook(
|
||||
{ address: safe.address, name: safe.name },
|
||||
addressBook,
|
||||
loadedFromStorage,
|
||||
)
|
||||
|
||||
if (!safeWasAlreadyInAddressBook) {
|
||||
dispatch(
|
||||
addAddressBookEntry(makeAddressBookEntry({ address: safe.address, name: safe.name }), {
|
||||
notifyEntryUpdate: true,
|
||||
}),
|
||||
)
|
||||
}
|
||||
break
|
||||
}
|
||||
case UPDATE_SAFE: {
|
||||
const { activeTokens } = action.payload
|
||||
const { activeTokens, name, address } = action.payload
|
||||
if (activeTokens) {
|
||||
recalculateActiveTokens(state)
|
||||
}
|
||||
if (name) {
|
||||
dispatch(addOrUpdateAddressBookEntry(makeAddressBookEntry({ name, address })), { notifyEntryUpdate: false })
|
||||
}
|
||||
break
|
||||
}
|
||||
case SET_DEFAULT_SAFE: {
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { handleActions } from 'redux-actions'
|
||||
|
||||
import { Transaction } from '../models/types/transactions'
|
||||
import { LOAD_MORE_TRANSACTIONS, LoadMoreTransactionsAction } from '../actions/allTransactions/pagination'
|
||||
import { Transaction } from 'src/logic/safe/store/models/types/transactions'
|
||||
import {
|
||||
LOAD_MORE_TRANSACTIONS,
|
||||
LoadMoreTransactionsAction,
|
||||
} from 'src/logic/safe/store/actions/allTransactions/pagination'
|
||||
|
||||
export const TRANSACTIONS = 'allTransactions'
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@ import Col from 'src/components/layout/Col'
|
|||
import Hairline from 'src/components/layout/Hairline'
|
||||
import Paragraph from 'src/components/layout/Paragraph'
|
||||
import Row from 'src/components/layout/Row'
|
||||
import { getAddressBook } from 'src/logic/addressBook/store/selectors'
|
||||
import { getAddressesListFromSafeAddressBook } from 'src/logic/addressBook/utils'
|
||||
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
|
||||
import { getAddressesListFromAddressBook } from 'src/logic/addressBook/utils'
|
||||
|
||||
export const CREATE_ENTRY_INPUT_NAME_ID = 'create-entry-input-name'
|
||||
export const CREATE_ENTRY_INPUT_ADDRESS_ID = 'create-entry-input-address'
|
||||
|
@ -42,8 +42,8 @@ const CreateEditEntryModalComponent = ({
|
|||
}
|
||||
}
|
||||
|
||||
const addressBook = useSelector(getAddressBook)
|
||||
const addressBookAddressesList = getAddressesListFromSafeAddressBook(addressBook)
|
||||
const addressBook = useSelector(addressBookSelector)
|
||||
const addressBookAddressesList = getAddressesListFromAddressBook(addressBook)
|
||||
const entryDoesntExist = uniqueAddress(addressBookAddressesList)
|
||||
|
||||
const formMutators = {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { List } from 'immutable'
|
||||
import { TableCellProps } from '@material-ui/core/TableCell/TableCell'
|
||||
|
||||
export const ADDRESS_BOOK_ROW_ID = 'address-book-row'
|
||||
export const TX_TABLE_ADDRESS_BOOK_ID = 'idAddressBook'
|
||||
|
@ -9,7 +10,17 @@ 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 = () => {
|
||||
type AddressBookColumn = {
|
||||
id: string
|
||||
order: boolean
|
||||
disablePadding?: boolean
|
||||
label: string
|
||||
width?: number
|
||||
custom?: boolean
|
||||
align?: TableCellProps['align']
|
||||
}
|
||||
|
||||
export const generateColumns = (): List<AddressBookColumn> => {
|
||||
const nameColumn = {
|
||||
id: AB_NAME_ID,
|
||||
order: false,
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import TableCell from '@material-ui/core/TableCell'
|
||||
import TableContainer from '@material-ui/core/TableContainer'
|
||||
import TableRow from '@material-ui/core/TableRow'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
// import CallMade from '@material-ui/icons/CallMade'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import cn from 'classnames'
|
||||
// import classNames from 'classnames/bind'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
|
||||
|
@ -18,11 +16,11 @@ import ButtonLink from 'src/components/layout/ButtonLink'
|
|||
import Col from 'src/components/layout/Col'
|
||||
import Img from 'src/components/layout/Img'
|
||||
import Row from 'src/components/layout/Row'
|
||||
import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
import { AddressBookEntry, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
import { addAddressBookEntry } from 'src/logic/addressBook/store/actions/addAddressBookEntry'
|
||||
import { removeAddressBookEntry } from 'src/logic/addressBook/store/actions/removeAddressBookEntry'
|
||||
import { updateAddressBookEntry } from 'src/logic/addressBook/store/actions/updateAddressBookEntry'
|
||||
import { getAddressBook } from 'src/logic/addressBook/store/selectors'
|
||||
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
|
||||
import { isUserAnOwnerOfAnySafe } from 'src/logic/wallets/ethAddresses'
|
||||
import CreateEditEntryModal from 'src/routes/safe/components/AddressBook/CreateEditEntryModal'
|
||||
import DeleteEntryModal from 'src/routes/safe/components/AddressBook/DeleteEntryModal'
|
||||
|
@ -38,19 +36,32 @@ import SendModal from 'src/routes/safe/components/Balances/SendModal'
|
|||
import OwnerAddressTableCell from 'src/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell'
|
||||
import RenameOwnerIcon from 'src/routes/safe/components/Settings/ManageOwners/assets/icons/rename-owner.svg'
|
||||
import RemoveOwnerIcon from 'src/routes/safe/components/Settings/assets/icons/bin.svg'
|
||||
import RemoveOwnerIconDisabled from 'src/routes/safe/components/Settings/assets/icons/disabled-bin.svg'
|
||||
import { addressBookQueryParamsSelector, safesListSelector } from 'src/logic/safe/store/selectors'
|
||||
import { checksumAddress } from 'src/utils/checksumAddress'
|
||||
import { grantedSelector } from 'src/routes/safe/container/selector'
|
||||
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
|
||||
import { getValidAddressBookName } from 'src/logic/addressBook/utils'
|
||||
|
||||
const AddressBookTable = ({ classes }) => {
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
interface AddressBookSelectedEntry extends AddressBookEntry {
|
||||
isNew?: boolean
|
||||
}
|
||||
|
||||
const AddressBookTable = (): React.ReactElement => {
|
||||
const classes = useStyles()
|
||||
const columns = generateColumns()
|
||||
const autoColumns = columns.filter((c) => !c.custom)
|
||||
const dispatch = useDispatch()
|
||||
const safesList = useSelector(safesListSelector)
|
||||
const entryAddressToEditOrCreateNew = useSelector(addressBookQueryParamsSelector)
|
||||
const addressBook = useSelector(getAddressBook)
|
||||
const [selectedEntry, setSelectedEntry] = useState<any>(null)
|
||||
const addressBook = useSelector(addressBookSelector)
|
||||
const granted = useSelector(grantedSelector)
|
||||
const [selectedEntry, setSelectedEntry] = useState<{
|
||||
entry?: AddressBookSelectedEntry
|
||||
index?: number
|
||||
isOwnerAddress?: boolean
|
||||
} | null>(null)
|
||||
const [editCreateEntryModalOpen, setEditCreateEntryModalOpen] = useState(false)
|
||||
const [deleteEntryModalOpen, setDeleteEntryModalOpen] = useState(false)
|
||||
const [sendFundsModalOpen, setSendFundsModalOpen] = useState(false)
|
||||
|
@ -69,11 +80,10 @@ const AddressBookTable = ({ classes }) => {
|
|||
useEffect(() => {
|
||||
if (entryAddressToEditOrCreateNew) {
|
||||
const checksumEntryAdd = checksumAddress(entryAddressToEditOrCreateNew)
|
||||
const key = addressBook.findKey((entry) => entry.address === checksumEntryAdd)
|
||||
if (key && key >= 0) {
|
||||
const oldEntryIndex = addressBook.findIndex((entry) => entry.address === checksumEntryAdd)
|
||||
if (oldEntryIndex >= 0) {
|
||||
// Edit old entry
|
||||
const value = addressBook.get(key)
|
||||
setSelectedEntry({ entry: value, index: key })
|
||||
setSelectedEntry({ entry: addressBook[oldEntryIndex], index: oldEntryIndex })
|
||||
} else {
|
||||
// Create new entry
|
||||
setSelectedEntry({
|
||||
|
@ -107,7 +117,7 @@ const AddressBookTable = ({ classes }) => {
|
|||
}
|
||||
|
||||
const deleteEntryModalHandler = () => {
|
||||
const entryAddress = checksumAddress(selectedEntry.entry.address)
|
||||
const entryAddress = selectedEntry && selectedEntry.entry ? checksumAddress(selectedEntry.entry.address) : ''
|
||||
setSelectedEntry(null)
|
||||
setDeleteEntryModalOpen(false)
|
||||
dispatch(removeAddressBookEntry(entryAddress))
|
||||
|
@ -138,7 +148,7 @@ const AddressBookTable = ({ classes }) => {
|
|||
defaultRowsPerPage={25}
|
||||
disableLoadingOnEmptyTable
|
||||
label="Owners"
|
||||
size={addressBook?.size || 0}
|
||||
size={addressBook?.length || 0}
|
||||
>
|
||||
{(sortedData) =>
|
||||
sortedData.map((row, index) => {
|
||||
|
@ -151,20 +161,22 @@ const AddressBookTable = ({ classes }) => {
|
|||
key={index}
|
||||
tabIndex={-1}
|
||||
>
|
||||
{autoColumns.map((column: any) => (
|
||||
<TableCell align={column.align} component="td" key={column.id} style={cellWidth(column.width)}>
|
||||
{column.id === AB_ADDRESS_ID ? (
|
||||
<OwnerAddressTableCell address={row[column.id]} showLinks />
|
||||
) : (
|
||||
row[column.id]
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
{autoColumns.map((column) => {
|
||||
return (
|
||||
<TableCell align={column.align} component="td" key={column.id} style={cellWidth(column.width)}>
|
||||
{column.id === AB_ADDRESS_ID ? (
|
||||
<OwnerAddressTableCell address={row[column.id]} showLinks />
|
||||
) : (
|
||||
getValidAddressBookName(row[column.id])
|
||||
)}
|
||||
</TableCell>
|
||||
)
|
||||
})}
|
||||
<TableCell component="td">
|
||||
<Row align="end" className={classes.actions}>
|
||||
<Img
|
||||
alt="Edit entry"
|
||||
className={classes.editEntryButton}
|
||||
className={granted ? classes.editEntryButton : classes.editEntryButtonNonOwner}
|
||||
onClick={() => {
|
||||
setSelectedEntry({
|
||||
entry: row,
|
||||
|
@ -177,33 +189,29 @@ const AddressBookTable = ({ classes }) => {
|
|||
/>
|
||||
<Img
|
||||
alt="Remove entry"
|
||||
className={userOwner ? classes.removeEntryButtonDisabled : classes.removeEntryButton}
|
||||
onClick={() => {
|
||||
if (!userOwner) {
|
||||
setSelectedEntry({ entry: row })
|
||||
setDeleteEntryModalOpen(true)
|
||||
}
|
||||
}}
|
||||
src={userOwner ? RemoveOwnerIconDisabled : RemoveOwnerIcon}
|
||||
testId={REMOVE_ENTRY_BUTTON}
|
||||
/>
|
||||
<Button
|
||||
className={classes.send}
|
||||
color="primary"
|
||||
className={granted ? classes.removeEntryButton : classes.removeEntryButtonNonOwner}
|
||||
onClick={() => {
|
||||
setSelectedEntry({ entry: row })
|
||||
setSendFundsModalOpen(true)
|
||||
setDeleteEntryModalOpen(true)
|
||||
}}
|
||||
size="small"
|
||||
testId={SEND_ENTRY_BUTTON}
|
||||
variant="contained"
|
||||
>
|
||||
{/* <CallMade
|
||||
alt="Send Transaction"
|
||||
className={classNames(classes.leftIcon, classes.iconSmall)}
|
||||
/> */}
|
||||
Send
|
||||
</Button>
|
||||
src={RemoveOwnerIcon}
|
||||
testId={REMOVE_ENTRY_BUTTON}
|
||||
/>
|
||||
{granted ? (
|
||||
<Button
|
||||
className={classes.send}
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
setSelectedEntry({ entry: row })
|
||||
setSendFundsModalOpen(true)
|
||||
}}
|
||||
size="small"
|
||||
testId={SEND_ENTRY_BUTTON}
|
||||
variant="contained"
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
) : null}
|
||||
</Row>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
@ -236,4 +244,4 @@ const AddressBookTable = ({ classes }) => {
|
|||
)
|
||||
}
|
||||
|
||||
export default withStyles(styles as any)(AddressBookTable)
|
||||
export default AddressBookTable
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { lg, marginButtonImg, md, sm } from 'src/theme/variables'
|
||||
import { createStyles } from '@material-ui/core'
|
||||
|
||||
export const styles = () => ({
|
||||
export const styles = createStyles({
|
||||
formContainer: {
|
||||
minHeight: '250px',
|
||||
},
|
||||
|
@ -38,6 +39,9 @@ export const styles = () => ({
|
|||
cursor: 'pointer',
|
||||
marginBottom: marginButtonImg,
|
||||
},
|
||||
editEntryButtonNonOwner: {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
removeEntryButton: {
|
||||
marginLeft: lg,
|
||||
marginRight: lg,
|
||||
|
@ -50,6 +54,11 @@ export const styles = () => ({
|
|||
marginBottom: marginButtonImg,
|
||||
cursor: 'default',
|
||||
},
|
||||
removeEntryButtonNonOwner: {
|
||||
marginLeft: lg,
|
||||
marginRight: lg,
|
||||
cursor: 'pointer',
|
||||
},
|
||||
message: {
|
||||
padding: `${md} 0`,
|
||||
maxHeight: '54px',
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import MuiTextField from '@material-ui/core/TextField'
|
||||
import makeStyles from '@material-ui/core/styles/makeStyles'
|
||||
import Autocomplete from '@material-ui/lab/Autocomplete'
|
||||
import { List } from 'immutable'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { trimSpaces } from 'src/utils/strings'
|
||||
|
@ -10,10 +9,10 @@ import { styles } from './style'
|
|||
|
||||
import Identicon from 'src/components/Identicon'
|
||||
import { mustBeEthereumAddress, mustBeEthereumContractAddress } from 'src/components/forms/validator'
|
||||
import { getAddressBook } from 'src/logic/addressBook/store/selectors'
|
||||
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
|
||||
import { getAddressFromENS } from 'src/logic/wallets/getWeb3'
|
||||
import { isValidEnsName } from 'src/logic/wallets/ethAddresses'
|
||||
import { AddressBookEntryRecord } from 'src/logic/addressBook/model/addressBook'
|
||||
import { AddressBookEntry, AddressBookState } from 'src/logic/addressBook/model/addressBook'
|
||||
|
||||
export interface AddressBookProps {
|
||||
fieldMutator: (address: string) => void
|
||||
|
@ -44,12 +43,10 @@ const textFieldInputStyle = makeStyles(() => ({
|
|||
},
|
||||
}))
|
||||
|
||||
const filterAddressBookWithContractAddresses = async (
|
||||
addressBook: List<AddressBookEntryRecord>,
|
||||
): Promise<List<AddressBookEntryRecord>> => {
|
||||
const filterAddressBookWithContractAddresses = async (addressBook: AddressBookState): Promise<AddressBookEntry[]> => {
|
||||
const abFlags = await Promise.all(
|
||||
addressBook.map(
|
||||
async ({ address }: AddressBookEntryRecord): Promise<boolean> => {
|
||||
async ({ address }: AddressBookEntry): Promise<boolean> => {
|
||||
return (await mustBeEthereumContractAddress(address)) === undefined
|
||||
},
|
||||
),
|
||||
|
@ -58,11 +55,6 @@ const filterAddressBookWithContractAddresses = async (
|
|||
return addressBook.filter((_, index) => abFlags[index])
|
||||
}
|
||||
|
||||
interface FilteredAddressBookEntry {
|
||||
name: string
|
||||
address: string
|
||||
}
|
||||
|
||||
const AddressBookInput = ({
|
||||
fieldMutator,
|
||||
isCustomTx,
|
||||
|
@ -72,12 +64,12 @@ const AddressBookInput = ({
|
|||
setSelectedEntry,
|
||||
}: AddressBookProps): React.ReactElement => {
|
||||
const classes = useStyles()
|
||||
const addressBook = useSelector(getAddressBook)
|
||||
const addressBook = useSelector(addressBookSelector)
|
||||
const [isValidForm, setIsValidForm] = useState(true)
|
||||
const [validationText, setValidationText] = useState<string>('')
|
||||
const [inputTouched, setInputTouched] = useState(false)
|
||||
const [blurred, setBlurred] = useState(pristine)
|
||||
const [adbkList, setADBKList] = useState<List<FilteredAddressBookEntry>>(List([]))
|
||||
const [adbkList, setADBKList] = useState<AddressBookEntry[]>([])
|
||||
|
||||
const [inputAddValue, setInputAddValue] = useState(recipientAddress)
|
||||
|
||||
|
@ -168,7 +160,7 @@ const AddressBookInput = ({
|
|||
freeSolo
|
||||
getOptionLabel={(adbkEntry) => adbkEntry.address || ''}
|
||||
id="free-solo-demo"
|
||||
onChange={(_, value: FilteredAddressBookEntry) => {
|
||||
onChange={(_, value: AddressBookEntry) => {
|
||||
let address = ''
|
||||
let name = ''
|
||||
if (value) {
|
||||
|
@ -184,7 +176,7 @@ const AddressBookInput = ({
|
|||
setBlurred(false)
|
||||
}}
|
||||
open={!blurred}
|
||||
options={adbkList.toArray()}
|
||||
options={adbkList}
|
||||
renderInput={(params) => (
|
||||
<MuiTextField
|
||||
{...params}
|
||||
|
|
|
@ -20,8 +20,8 @@ import Col from 'src/components/layout/Col'
|
|||
import Hairline from 'src/components/layout/Hairline'
|
||||
import Paragraph from 'src/components/layout/Paragraph'
|
||||
import Row from 'src/components/layout/Row'
|
||||
import { getAddressBook } from 'src/logic/addressBook/store/selectors'
|
||||
import { getNameFromSafeAddressBook } from 'src/logic/addressBook/utils'
|
||||
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
|
||||
import { getNameFromAddressBook } from 'src/logic/addressBook/utils'
|
||||
import { nftTokensSelector, safeActiveSelectorMap } from 'src/logic/collectibles/store/selectors'
|
||||
import SafeInfo from 'src/routes/safe/components/Balances/SendModal/SafeInfo'
|
||||
import AddressBookInput from 'src/routes/safe/components/Balances/SendModal/screens/AddressBookInput'
|
||||
|
@ -41,14 +41,20 @@ const formMutators = {
|
|||
},
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(styles as any)
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const SendCollectible = ({ initialValues, onClose, onNext, recipientAddress, selectedToken = {} }) => {
|
||||
const SendCollectible = ({
|
||||
initialValues,
|
||||
onClose,
|
||||
onNext,
|
||||
recipientAddress,
|
||||
selectedToken = {},
|
||||
}): React.ReactElement => {
|
||||
const classes = useStyles()
|
||||
const nftAssets = useSelector(safeActiveSelectorMap)
|
||||
const nftTokens = useSelector(nftTokensSelector)
|
||||
const addressBook = useSelector(getAddressBook)
|
||||
const [selectedEntry, setSelectedEntry] = useState({
|
||||
const addressBook = useSelector(addressBookSelector)
|
||||
const [selectedEntry, setSelectedEntry] = useState<{ address?: string; name?: string | null } | null>({
|
||||
address: recipientAddress || initialValues.recipientAddress,
|
||||
name: '',
|
||||
})
|
||||
|
@ -64,7 +70,7 @@ const SendCollectible = ({ initialValues, onClose, onNext, recipientAddress, sel
|
|||
const handleSubmit = (values) => {
|
||||
// If the input wasn't modified, there was no mutation of the recipientAddress
|
||||
if (!values.recipientAddress) {
|
||||
values.recipientAddress = selectedEntry.address
|
||||
values.recipientAddress = selectedEntry?.address
|
||||
}
|
||||
|
||||
values.assetName = nftAssets[values.assetAddress].name
|
||||
|
@ -97,10 +103,10 @@ const SendCollectible = ({ initialValues, onClose, onNext, recipientAddress, sel
|
|||
if (scannedAddress.startsWith('ethereum:')) {
|
||||
scannedAddress = scannedAddress.replace('ethereum:', '')
|
||||
}
|
||||
const scannedName = addressBook ? getNameFromSafeAddressBook(addressBook, scannedAddress) : ''
|
||||
const scannedName = addressBook ? getNameFromAddressBook(addressBook, scannedAddress) : ''
|
||||
mutators.setRecipient(scannedAddress)
|
||||
setSelectedEntry({
|
||||
name: scannedName || '',
|
||||
name: scannedName,
|
||||
address: scannedAddress,
|
||||
})
|
||||
closeQrModal()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { lg, md, secondaryText } from 'src/theme/variables'
|
||||
import { createStyles } from '@material-ui/core'
|
||||
|
||||
export const styles = () => ({
|
||||
export const styles = createStyles({
|
||||
heading: {
|
||||
padding: `${md} ${lg}`,
|
||||
justifyContent: 'flex-start',
|
||||
|
|
|
@ -25,8 +25,8 @@ import Col from 'src/components/layout/Col'
|
|||
import Hairline from 'src/components/layout/Hairline'
|
||||
import Paragraph from 'src/components/layout/Paragraph'
|
||||
import Row from 'src/components/layout/Row'
|
||||
import { getAddressBook } from 'src/logic/addressBook/store/selectors'
|
||||
import { getNameFromSafeAddressBook } from 'src/logic/addressBook/utils'
|
||||
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
|
||||
import { getNameFromAddressBook } from 'src/logic/addressBook/utils'
|
||||
|
||||
import SafeInfo from 'src/routes/safe/components/Balances/SendModal/SafeInfo'
|
||||
import AddressBookInput from 'src/routes/safe/components/Balances/SendModal/screens/AddressBookInput'
|
||||
|
@ -69,8 +69,8 @@ const SendFunds = ({
|
|||
}: SendFundsProps): React.ReactElement => {
|
||||
const classes = useStyles()
|
||||
const tokens = useSelector(extendedSafeTokensSelector)
|
||||
const addressBook = useSelector(getAddressBook)
|
||||
const [selectedEntry, setSelectedEntry] = useState({
|
||||
const addressBook = useSelector(addressBookSelector)
|
||||
const [selectedEntry, setSelectedEntry] = useState<{ address?: string; name?: string | null } | null>({
|
||||
address: recipientAddress || initialValues.recipientAddress,
|
||||
name: '',
|
||||
})
|
||||
|
@ -88,7 +88,7 @@ const SendFunds = ({
|
|||
const submitValues = values
|
||||
// If the input wasn't modified, there was no mutation of the recipientAddress
|
||||
if (!values.recipientAddress) {
|
||||
submitValues.recipientAddress = selectedEntry.address
|
||||
submitValues.recipientAddress = selectedEntry?.address
|
||||
}
|
||||
onNext(submitValues)
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ const SendFunds = ({
|
|||
if (scannedAddress.startsWith('ethereum:')) {
|
||||
scannedAddress = scannedAddress.replace('ethereum:', '')
|
||||
}
|
||||
const scannedName = addressBook ? getNameFromSafeAddressBook(addressBook, scannedAddress) : ''
|
||||
const scannedName = addressBook ? getNameFromAddressBook(addressBook, scannedAddress) : ''
|
||||
mutators.setRecipient(scannedAddress)
|
||||
setSelectedEntry({
|
||||
name: scannedName || '',
|
||||
|
|
|
@ -20,12 +20,12 @@ import Hairline from 'src/components/layout/Hairline'
|
|||
import Paragraph from 'src/components/layout/Paragraph'
|
||||
import Row from 'src/components/layout/Row'
|
||||
import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
import { updateAddressBookEntry } from 'src/logic/addressBook/store/actions/updateAddressBookEntry'
|
||||
import { NOTIFICATIONS } from 'src/logic/notifications'
|
||||
import editSafeOwner from 'src/logic/safe/store/actions/editSafeOwner'
|
||||
import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
|
||||
import { sm } from 'src/theme/variables'
|
||||
import enqueueSnackbar from 'src/logic/notifications/store/actions/enqueueSnackbar'
|
||||
import { addOrUpdateAddressBookEntry } from 'src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry'
|
||||
|
||||
export const RENAME_OWNER_INPUT_TEST_ID = 'rename-owner-input'
|
||||
export const SAVE_OWNER_CHANGES_BTN_TEST_ID = 'save-owner-changes-btn'
|
||||
|
@ -47,7 +47,7 @@ const EditOwnerComponent = ({ isOpen, onClose, ownerAddress, selectedOwnerName }
|
|||
const { ownerName } = values
|
||||
|
||||
dispatch(editSafeOwner({ safeAddress, ownerAddress, ownerName }))
|
||||
dispatch(updateAddressBookEntry(makeAddressBookEntry({ address: ownerAddress, name: ownerName })))
|
||||
dispatch(addOrUpdateAddressBookEntry(makeAddressBookEntry({ address: ownerAddress, name: ownerName })))
|
||||
dispatch(enqueueSnackbar(NOTIFICATIONS.OWNER_NAME_CHANGE_EXECUTED_MSG))
|
||||
|
||||
onClose()
|
||||
|
|
|
@ -6,6 +6,7 @@ import Block from 'src/components/layout/Block'
|
|||
import Paragraph from 'src/components/layout/Paragraph'
|
||||
import { useWindowDimensions } from 'src/logic/hooks/useWindowDimensions'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { getValidAddressBookName } from 'src/logic/addressBook/utils'
|
||||
|
||||
type OwnerAddressTableCellProps = {
|
||||
address: string
|
||||
|
@ -34,7 +35,7 @@ const OwnerAddressTableCell = (props: OwnerAddressTableCellProps): React.ReactEl
|
|||
<Identicon address={address} diameter={32} />
|
||||
{showLinks ? (
|
||||
<div style={{ marginLeft: 10, flexShrink: 1, minWidth: 0 }}>
|
||||
{!userName || userName === 'UNKNOWN' ? null : userName}
|
||||
{userName && getValidAddressBookName(userName)}
|
||||
<EtherScanLink knownAddress={knownAddress} type="address" value={address} cut={cut} />
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
@ -30,8 +30,8 @@ import Paragraph from 'src/components/layout/Paragraph/index'
|
|||
import Row from 'src/components/layout/Row'
|
||||
import { getOwnersWithNameFromAddressBook } from 'src/logic/addressBook/utils'
|
||||
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
|
||||
import { AddressBookState } from 'src/logic/addressBook/model/addressBook'
|
||||
import { SafeOwner } from 'src/logic/safe/store/models/safe'
|
||||
import { AddressBookCollection } from 'src/logic/addressBook/store/reducer/addressBook'
|
||||
|
||||
export const RENAME_OWNER_BTN_TEST_ID = 'rename-owner-btn'
|
||||
export const REMOVE_OWNER_BTN_TEST_ID = 'remove-owner-btn'
|
||||
|
@ -42,7 +42,7 @@ export const OWNERS_ROW_TEST_ID = 'owners-row'
|
|||
const useStyles = makeStyles(styles)
|
||||
|
||||
type Props = {
|
||||
addressBook: unknown
|
||||
addressBook: AddressBookState
|
||||
granted: boolean
|
||||
owners: List<SafeOwner>
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ const ManageOwners = ({ addressBook, granted, owners }: Props): React.ReactEleme
|
|||
|
||||
const columns = generateColumns()
|
||||
const autoColumns = columns.filter((c) => !c.custom)
|
||||
const ownersAdbk = getOwnersWithNameFromAddressBook(addressBook as AddressBookCollection, owners)
|
||||
const ownersAdbk = getOwnersWithNameFromAddressBook(addressBook, owners)
|
||||
const ownerData = getOwnerData(ownersAdbk)
|
||||
|
||||
return (
|
||||
|
|
|
@ -23,7 +23,7 @@ import Img from 'src/components/layout/Img'
|
|||
import Paragraph from 'src/components/layout/Paragraph'
|
||||
import Row from 'src/components/layout/Row'
|
||||
import Span from 'src/components/layout/Span'
|
||||
import { getAddressBook } from 'src/logic/addressBook/store/selectors'
|
||||
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
|
||||
import { grantedSelector } from 'src/routes/safe/container/selector'
|
||||
import { safeNeedsUpdateSelector, safeOwnersSelector } from 'src/logic/safe/store/selectors'
|
||||
|
||||
|
@ -42,7 +42,7 @@ const Settings: React.FC = () => {
|
|||
const owners = useSelector(safeOwnersSelector)
|
||||
const needsUpdate = useSelector(safeNeedsUpdateSelector)
|
||||
const granted = useSelector(grantedSelector)
|
||||
const addressBook = useSelector(getAddressBook)
|
||||
const addressBook = useSelector(addressBookSelector)
|
||||
|
||||
const handleChange = (menuOptionIndex) => () => {
|
||||
setState((prevState) => ({ ...prevState, menuOptionIndex }))
|
||||
|
|
|
@ -6,7 +6,6 @@ import thunk from 'redux-thunk'
|
|||
|
||||
import addressBookMiddleware from 'src/logic/addressBook/store/middleware/addressBookMiddleware'
|
||||
import addressBook, { ADDRESS_BOOK_REDUCER_ID } from 'src/logic/addressBook/store/reducer/addressBook'
|
||||
import { AddressBookReducerMap } from 'src/logic/addressBook/store/reducer/types/addressBook.d'
|
||||
import {
|
||||
NFT_ASSETS_REDUCER_ID,
|
||||
NFT_TOKENS_REDUCER_ID,
|
||||
|
@ -19,8 +18,10 @@ import currencyValues, {
|
|||
CURRENCY_VALUES_KEY,
|
||||
CurrencyValuesState,
|
||||
} from 'src/logic/currencyValues/store/reducer/currencyValues'
|
||||
import { CurrentSessionState } from 'src/logic/currentSession/store/reducer/currentSession'
|
||||
import currentSession, { CURRENT_SESSION_REDUCER_ID } from 'src/logic/currentSession/store/reducer/currentSession'
|
||||
import currentSession, {
|
||||
CURRENT_SESSION_REDUCER_ID,
|
||||
CurrentSessionState,
|
||||
} from 'src/logic/currentSession/store/reducer/currentSession'
|
||||
import notifications, { NOTIFICATIONS_REDUCER_ID } from 'src/logic/notifications/store/reducer/notifications'
|
||||
import tokens, { TOKEN_REDUCER_ID, TokenState } from 'src/logic/tokens/store/reducer/tokens'
|
||||
import providerWatcher from 'src/logic/wallets/store/middlewares/providerWatcher'
|
||||
|
@ -38,7 +39,8 @@ import safe, { SAFE_REDUCER_ID } from 'src/logic/safe/store/reducer/safe'
|
|||
import transactions, { TRANSACTIONS_REDUCER_ID } from 'src/logic/safe/store/reducer/transactions'
|
||||
import { NFTAssets, NFTTokens } from 'src/logic/collectibles/sources/OpenSea'
|
||||
import { SafeReducerMap } from 'src/routes/safe/store/reducer/types/safe'
|
||||
import allTransactions, { TRANSACTIONS, TransactionsState } from 'src/logic/safe/store/reducer/allTransactions'
|
||||
import allTransactions, { TRANSACTIONS, TransactionsState } from '../logic/safe/store/reducer/allTransactions'
|
||||
import { AddressBookState } from 'src/logic/addressBook/model/addressBook'
|
||||
|
||||
export const history = createHashHistory()
|
||||
|
||||
|
@ -86,7 +88,7 @@ export type AppReduxState = CombinedState<{
|
|||
[NOTIFICATIONS_REDUCER_ID]: Map<string, any>
|
||||
[CURRENCY_VALUES_KEY]: CurrencyValuesState
|
||||
[COOKIES_REDUCER_ID]: Map<string, any>
|
||||
[ADDRESS_BOOK_REDUCER_ID]: AddressBookReducerMap
|
||||
[ADDRESS_BOOK_REDUCER_ID]: AddressBookState
|
||||
[CURRENT_SESSION_REDUCER_ID]: CurrentSessionState
|
||||
[TRANSACTIONS]: TransactionsState
|
||||
router: RouterState
|
||||
|
|
Loading…
Reference in New Issue