[Address Book V2] - Export address book (#2284)
* Add export feature * Improve Address book export modal * Use styled-components instead of material-ui styles * Add comment * Improve filename + set loading state false when error
This commit is contained in:
parent
5ee9ef7b84
commit
839c947e82
|
@ -214,6 +214,7 @@
|
||||||
"react-ga": "3.3.0",
|
"react-ga": "3.3.0",
|
||||||
"react-hot-loader": "4.13.0",
|
"react-hot-loader": "4.13.0",
|
||||||
"react-intersection-observer": "^8.31.0",
|
"react-intersection-observer": "^8.31.0",
|
||||||
|
"react-papaparse": "^3.14.0",
|
||||||
"react-qr-reader": "^2.2.1",
|
"react-qr-reader": "^2.2.1",
|
||||||
"react-redux": "7.2.3",
|
"react-redux": "7.2.3",
|
||||||
"react-router-dom": "5.2.0",
|
"react-router-dom": "5.2.0",
|
||||||
|
|
|
@ -184,13 +184,21 @@ const Body = ({ children, withoutPadding = false }: BodyProps): ReactElement =>
|
||||||
)
|
)
|
||||||
|
|
||||||
/*** Footer ***/
|
/*** Footer ***/
|
||||||
const FooterSection = styled.div`
|
const FooterSection = styled.div<{
|
||||||
|
withoutPadding: FooterProps['withoutPadding']
|
||||||
|
withoutBorder: FooterProps['withoutBorder']
|
||||||
|
}>`
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-top: 2px solid ${({ theme }) => theme.colors.separator};
|
border-top: ${({ withoutBorder }) => (withoutBorder ? 0 : `2px solid ${({ theme }) => theme.colors.separator}`)};
|
||||||
padding: 24px;
|
padding: ${({ withoutPadding }) => (withoutPadding ? 0 : '24px')};
|
||||||
`
|
`
|
||||||
|
|
||||||
|
interface FooterProps {
|
||||||
|
withoutPadding?: boolean
|
||||||
|
withoutBorder?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
const ButtonStyled = styled(Button)`
|
const ButtonStyled = styled(Button)`
|
||||||
&.MuiButtonBase-root {
|
&.MuiButtonBase-root {
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
|
@ -240,8 +248,10 @@ interface FooterProps {
|
||||||
children: ReactNode | ReactNodeArray
|
children: ReactNode | ReactNodeArray
|
||||||
}
|
}
|
||||||
|
|
||||||
const Footer = ({ children }: FooterProps): ReactElement => (
|
const Footer = ({ children, withoutPadding = false, withoutBorder = false }: FooterProps): ReactElement => (
|
||||||
<FooterSection className="modal-footer">{children}</FooterSection>
|
<FooterSection className="modal-footer" withoutPadding={withoutPadding} withoutBorder={withoutBorder}>
|
||||||
|
{children}
|
||||||
|
</FooterSection>
|
||||||
)
|
)
|
||||||
|
|
||||||
Footer.Buttons = Buttons
|
Footer.Buttons = Buttons
|
||||||
|
|
|
@ -153,6 +153,17 @@ const addressBookDeleteEntry = {
|
||||||
afterExecutionError: null,
|
afterExecutionError: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const addressBookExportEntries = {
|
||||||
|
beforeExecution: null,
|
||||||
|
afterRejection: null,
|
||||||
|
waitingConfirmation: null,
|
||||||
|
afterExecution: {
|
||||||
|
noMoreConfirmationsNeeded: NOTIFICATIONS.ADDRESS_BOOK_EXPORT_ENTRIES_SUCCESS,
|
||||||
|
moreConfirmationsNeeded: null,
|
||||||
|
},
|
||||||
|
afterExecutionError: NOTIFICATIONS.ADDRESS_BOOK_EXPORT_ENTRIES_ERROR,
|
||||||
|
}
|
||||||
|
|
||||||
export const getNotificationsFromTxType: any = (txType, origin) => {
|
export const getNotificationsFromTxType: any = (txType, origin) => {
|
||||||
let notificationsQueue
|
let notificationsQueue
|
||||||
|
|
||||||
|
@ -205,6 +216,10 @@ export const getNotificationsFromTxType: any = (txType, origin) => {
|
||||||
notificationsQueue = addressBookDeleteEntry
|
notificationsQueue = addressBookDeleteEntry
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case TX_NOTIFICATION_TYPES.ADDRESSBOOK_EXPORT_ENTRIES: {
|
||||||
|
notificationsQueue = addressBookExportEntries
|
||||||
|
break
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
notificationsQueue = defaultNotificationsQueue
|
notificationsQueue = defaultNotificationsQueue
|
||||||
break
|
break
|
||||||
|
|
|
@ -52,6 +52,8 @@ const NOTIFICATION_IDS = {
|
||||||
ADDRESS_BOOK_NEW_ENTRY_SUCCESS: 'ADDRESS_BOOK_NEW_ENTRY_SUCCESS',
|
ADDRESS_BOOK_NEW_ENTRY_SUCCESS: 'ADDRESS_BOOK_NEW_ENTRY_SUCCESS',
|
||||||
ADDRESS_BOOK_EDIT_ENTRY_SUCCESS: 'ADDRESS_BOOK_EDIT_ENTRY_SUCCESS',
|
ADDRESS_BOOK_EDIT_ENTRY_SUCCESS: 'ADDRESS_BOOK_EDIT_ENTRY_SUCCESS',
|
||||||
ADDRESS_BOOK_DELETE_ENTRY_SUCCESS: 'ADDRESS_BOOK_DELETE_ENTRY_SUCCESS',
|
ADDRESS_BOOK_DELETE_ENTRY_SUCCESS: 'ADDRESS_BOOK_DELETE_ENTRY_SUCCESS',
|
||||||
|
ADDRESS_BOOK_EXPORT_ENTRIES_SUCCESS: 'ADDRESS_BOOK_EXPORT_ENTRIES_SUCCESS',
|
||||||
|
ADDRESS_BOOK_EXPORT_ENTRIES_ERROR: 'ADDRESS_BOOK_EXPORT_ENTRIES_ERROR',
|
||||||
SAFE_NEW_VERSION_AVAILABLE: 'SAFE_NEW_VERSION_AVAILABLE',
|
SAFE_NEW_VERSION_AVAILABLE: 'SAFE_NEW_VERSION_AVAILABLE',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,6 +208,14 @@ export const NOTIFICATIONS: Record<NotificationId, Notification> = {
|
||||||
message: 'Entry deleted successfully',
|
message: 'Entry deleted successfully',
|
||||||
options: { variant: SUCCESS, persist: false, preventDuplicate: false },
|
options: { variant: SUCCESS, persist: false, preventDuplicate: false },
|
||||||
},
|
},
|
||||||
|
ADDRESS_BOOK_EXPORT_ENTRIES_SUCCESS: {
|
||||||
|
message: 'Address book exported',
|
||||||
|
options: { variant: SUCCESS, persist: false, preventDuplicate: false },
|
||||||
|
},
|
||||||
|
ADDRESS_BOOK_EXPORT_ENTRIES_ERROR: {
|
||||||
|
message: 'An error occurred while generating the address book CSV.',
|
||||||
|
options: { variant: ERROR, persist: false, preventDuplicate: false },
|
||||||
|
},
|
||||||
|
|
||||||
// Safe Version
|
// Safe Version
|
||||||
SAFE_NEW_VERSION_AVAILABLE: {
|
SAFE_NEW_VERSION_AVAILABLE: {
|
||||||
|
|
|
@ -11,4 +11,5 @@ export const TX_NOTIFICATION_TYPES = {
|
||||||
ADDRESSBOOK_NEW_ENTRY: 'ADDRESSBOOK_NEW_ENTRY',
|
ADDRESSBOOK_NEW_ENTRY: 'ADDRESSBOOK_NEW_ENTRY',
|
||||||
ADDRESSBOOK_EDIT_ENTRY: 'ADDRESSBOOK_EDIT_ENTRY',
|
ADDRESSBOOK_EDIT_ENTRY: 'ADDRESSBOOK_EDIT_ENTRY',
|
||||||
ADDRESSBOOK_DELETE_ENTRY: 'ADDRESSBOOK_DELETE_ENTRY',
|
ADDRESSBOOK_DELETE_ENTRY: 'ADDRESSBOOK_DELETE_ENTRY',
|
||||||
|
ADDRESSBOOK_EXPORT_ENTRIES: 'ADDRESSBOOK_EXPORT_ENTRIES',
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<svg width="108" height="96" viewBox="0 0 108 96" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M54 4C79.405 4 100 24.595 100 50C100 75.405 79.405 96 54 96C28.595 96 8 75.405 8 50C8 24.595 28.595 4 54 4Z" fill="#F7F5F5"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M37.3327 60.8333L37.3327 66.6663H70.6663V60.8333C70.6663 60.6037 70.6663 58.3333 72.7496 58.3333C74.833 58.3333 74.833 60.6037 74.833 60.8333V68.7503C74.8325 68.7503 74.8321 68.7503 74.8317 68.7503C74.8313 69.8959 73.8961 70.833 72.7484 70.833H35.2484C34.1025 70.833 33.165 69.8955 33.165 68.7496C33.165 68.7284 33.1654 68.7073 33.166 68.6862L33.166 60.8333C33.166 60.6037 33.166 58.3333 35.2493 58.3333C37.3327 58.3333 37.3327 60.6037 37.3327 60.8333Z" fill="#B2B5B2"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M51.9156 36.2792V60.4105C51.9156 61.5626 52.849 62.4938 53.999 62.4938C55.151 62.4938 56.0823 61.5626 56.0823 60.4105V36.2792L64.6698 44.8647C65.4844 45.6793 66.8031 45.6793 67.6156 44.8647C68.4302 44.0501 68.4302 42.7334 67.6156 41.9188L55.8302 30.1334C55.7698 30.073 55.7052 30.0167 55.6406 29.9667C55.2573 29.4772 54.6656 29.1667 53.999 29.1667C53.3344 29.1667 52.7406 29.4772 52.3594 29.9667C52.2948 30.0167 52.2281 30.073 52.1698 30.1334L40.3844 41.9188C39.5698 42.7334 39.5698 44.0501 40.3844 44.8647C41.1969 45.6793 42.5156 45.6793 43.3302 44.8647L51.9156 36.2792Z" fill="#B2B5B2"/>
|
||||||
|
<circle cx="80" cy="74" r="17" fill="#F7F5F5"/>
|
||||||
|
<rect x="78.25" y="63.5" width="3.5" height="14" rx="1" fill="#F02525"/>
|
||||||
|
<path d="M80 80.5625C81.2081 80.5625 82.1875 81.5419 82.1875 82.75C82.1875 83.9581 81.2081 84.9375 80 84.9375C78.7919 84.9375 77.8125 83.9581 77.8125 82.75C77.8125 81.5419 78.7919 80.5625 80 80.5625Z" fill="#F02525"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M97.5 74C97.5 83.665 89.665 91.5 80 91.5C70.335 91.5 62.5 83.665 62.5 74C62.5 64.335 70.335 56.5 80 56.5C89.665 56.5 97.5 64.335 97.5 74ZM66 74C66 81.732 72.268 88 80 88C87.732 88 94 81.732 94 74C94 66.268 87.732 60 80 60C72.268 60 66 66.268 66 74Z" fill="#F02525"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
|
@ -0,0 +1,8 @@
|
||||||
|
<svg width="108" height="96" viewBox="0 0 108 96" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M54 4C79.405 4 100 24.595 100 50C100 75.405 79.405 96 54 96C28.595 96 8 75.405 8 50C8 24.595 28.595 4 54 4Z" fill="#F7F5F5"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M37.3329 60.8333L37.3329 66.6663H70.6665V60.8333C70.6665 60.6037 70.6665 58.3333 72.7499 58.3333C74.8332 58.3333 74.8332 60.6037 74.8332 60.8333V68.7503C74.8328 68.7503 74.8324 68.7503 74.8319 68.7503C74.8316 69.8959 73.8963 70.833 72.7486 70.833H35.2486C34.1028 70.833 33.1653 69.8955 33.1653 68.7496C33.1653 68.7284 33.1656 68.7073 33.1662 68.6862L33.1662 60.8333C33.1662 60.6037 33.1662 58.3333 35.2496 58.3333C37.3329 58.3333 37.3329 60.6037 37.3329 60.8333Z" fill="#B2B5B2"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M51.9161 36.2791V60.4104C51.9161 61.5625 52.8494 62.4937 53.9994 62.4937C55.1515 62.4937 56.0828 61.5625 56.0828 60.4104V36.2791L64.6703 44.8645C65.4849 45.6791 66.8036 45.6791 67.6161 44.8645C68.4307 44.05 68.4307 42.7333 67.6161 41.9187L55.8307 30.1333C55.7703 30.0729 55.7057 30.0166 55.6411 29.9666C55.2578 29.477 54.6661 29.1666 53.9994 29.1666C53.3349 29.1666 52.7411 29.477 52.3599 29.9666C52.2953 30.0166 52.2286 30.0729 52.1703 30.1333L40.3849 41.9187C39.5703 42.7333 39.5703 44.05 40.3849 44.8645C41.1974 45.6791 42.5161 45.6791 43.3307 44.8645L51.9161 36.2791Z" fill="#B2B5B2"/>
|
||||||
|
<circle cx="80" cy="74" r="17" fill="#F7F5F5"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M97.5 74C97.5 83.665 89.665 91.5 80 91.5C70.335 91.5 62.5 83.665 62.5 74C62.5 64.335 70.335 56.5 80 56.5C89.665 56.5 97.5 64.335 97.5 74ZM66 74C66 81.732 72.268 88 80 88C87.732 88 94 81.732 94 74C94 66.268 87.732 60 80 60C72.268 60 66 66.268 66 74Z" fill="#008C73"/>
|
||||||
|
<path d="M72.443 70.9826C71.7373 70.3222 70.6299 70.3588 69.9694 71.0644C69.309 71.77 69.3456 72.8775 70.0512 73.5379L77.2979 80.3209C77.9934 80.9719 79.0818 80.947 79.7468 80.265L89.1041 70.668C89.7788 69.976 89.7648 68.868 89.0728 68.1933C88.3808 67.5186 87.2728 67.5326 86.5981 68.2246L78.4379 76.5939L72.443 70.9826Z" fill="#008C73"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,8 @@
|
||||||
|
<svg width="108" height="96" viewBox="0 0 108 96" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M54 4C79.405 4 100 24.595 100 50C100 75.405 79.405 96 54 96C28.595 96 8 75.405 8 50C8 24.595 28.595 4 54 4Z" fill="#F7F5F5"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M37.3327 60.8333L37.3327 66.6663H70.6663V60.8333C70.6663 60.6037 70.6663 58.3333 72.7496 58.3333C74.833 58.3333 74.833 60.6037 74.833 60.8333V68.7503C74.8325 68.7503 74.8321 68.7503 74.8317 68.7503C74.8313 69.8959 73.8961 70.833 72.7484 70.833H35.2484C34.1025 70.833 33.165 69.8955 33.165 68.7496C33.165 68.7284 33.1654 68.7073 33.166 68.6862L33.166 60.8333C33.166 60.6037 33.166 58.3333 35.2493 58.3333C37.3327 58.3333 37.3327 60.6037 37.3327 60.8333Z" fill="#B2B5B2"/>
|
||||||
|
<circle cx="80" cy="74" r="17" fill="#F7F5F5"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M73.871 80C73.243 80 72.63 79.7078 72.2767 79.1771C71.7366 78.3629 72.0114 77.2989 72.8898 76.7984L78.2621 73.7429V66.7287C78.2621 65.7736 79.0994 65 80.131 65C81.1636 65 82 65.7736 82 66.7287V74.7093C82 75.3091 81.6636 75.8675 81.1104 76.1821L74.8484 79.7442C74.5429 79.917 74.2046 80 73.871 80Z" fill="#5D6D74"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M97.5 74C97.5 83.665 89.665 91.5 80 91.5C70.335 91.5 62.5 83.665 62.5 74C62.5 64.335 70.335 56.5 80 56.5C89.665 56.5 97.5 64.335 97.5 74ZM66 74C66 81.732 72.268 88 80 88C87.732 88 94 81.732 94 74C94 66.268 87.732 60 80 60C72.268 60 66 66.268 66 74Z" fill="#5D6D74"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M51.9156 36.2791V60.4104C51.9156 61.5625 52.849 62.4937 53.999 62.4937C55.151 62.4937 56.0823 61.5625 56.0823 60.4104V36.2791L64.6698 44.8645C65.4844 45.6791 66.8031 45.6791 67.6156 44.8645C68.4302 44.05 68.4302 42.7333 67.6156 41.9187L55.8302 30.1333C55.7698 30.0729 55.7052 30.0166 55.6406 29.9666C55.2573 29.477 54.6656 29.1666 53.999 29.1666C53.3344 29.1666 52.7406 29.477 52.3594 29.9666C52.2948 30.0166 52.2281 30.0729 52.1698 30.1333L40.3844 41.9187C39.5698 42.7333 39.5698 44.05 40.3844 44.8645C41.1969 45.6791 42.5156 45.6791 43.3302 44.8645L51.9156 36.2791Z" fill="#B2B5B2"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,145 @@
|
||||||
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
|
import { format } from 'date-fns'
|
||||||
|
import { useSelector, useDispatch } from 'react-redux'
|
||||||
|
import { CSVDownloader, jsonToCSV } from 'react-papaparse'
|
||||||
|
import { Button, Loader, Text } from '@gnosis.pm/safe-react-components'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
import { enhanceSnackbarForAction, getNotificationsFromTxType } from 'src/logic/notifications'
|
||||||
|
import enqueueSnackbar from 'src/logic/notifications/store/actions/enqueueSnackbar'
|
||||||
|
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
||||||
|
|
||||||
|
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
|
||||||
|
import { AddressBookState } from 'src/logic/addressBook/model/addressBook'
|
||||||
|
|
||||||
|
import { lg, md, background } from 'src/theme/variables'
|
||||||
|
|
||||||
|
import { Modal } from 'src/components/Modal'
|
||||||
|
import Img from 'src/components/layout/Img'
|
||||||
|
import Row from 'src/components/layout/Row'
|
||||||
|
|
||||||
|
import SuccessSvg from './assets/success.svg'
|
||||||
|
import ErrorSvg from './assets/error.svg'
|
||||||
|
import LoadingSvg from './assets/wait.svg'
|
||||||
|
|
||||||
|
type ExportEntriesModalProps = {
|
||||||
|
isOpen: boolean
|
||||||
|
onClose: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImageContainer = styled(Row)`
|
||||||
|
padding: ${md} ${lg};
|
||||||
|
justify-content: center;
|
||||||
|
`
|
||||||
|
|
||||||
|
const InfoContainer = styled(Row)`
|
||||||
|
background-color: ${background};
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
padding: ${lg};
|
||||||
|
text-align: center;
|
||||||
|
`
|
||||||
|
|
||||||
|
const BodyImage = styled.div`
|
||||||
|
grid-row: 1;
|
||||||
|
`
|
||||||
|
const StyledLoader = styled(Loader)`
|
||||||
|
margin-right: 5px;
|
||||||
|
`
|
||||||
|
const StyledCSVLink = styled(CSVDownloader)`
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
justify-content: center;
|
||||||
|
`
|
||||||
|
|
||||||
|
export const ExportEntriesModal = ({ isOpen, onClose }: ExportEntriesModalProps): ReactElement => {
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
const addressBook: AddressBookState = useSelector(addressBookSelector)
|
||||||
|
const [loading, setLoading] = useState<boolean>(true)
|
||||||
|
const [error, setError] = useState<string | undefined>('')
|
||||||
|
const [csvData, setCsvData] = useState<string>('')
|
||||||
|
const [doRetry, setDoRetry] = useState<boolean>(false)
|
||||||
|
|
||||||
|
const date = format(new Date(), 'yyyy-MM-dd')
|
||||||
|
|
||||||
|
const handleClose = () =>
|
||||||
|
//This timeout prevents modal to be closed abruptly
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!loading) {
|
||||||
|
const notification = getNotificationsFromTxType(TX_NOTIFICATION_TYPES.ADDRESSBOOK_EXPORT_ENTRIES)
|
||||||
|
const action = error
|
||||||
|
? notification.afterExecution.afterExecutionError
|
||||||
|
: notification.afterExecution.noMoreConfirmationsNeeded
|
||||||
|
dispatch(enqueueSnackbar(enhanceSnackbarForAction(action)))
|
||||||
|
}
|
||||||
|
onClose()
|
||||||
|
}, 600)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleCsvData = () => {
|
||||||
|
if (!isOpen && !doRetry) return
|
||||||
|
setLoading(true)
|
||||||
|
setError('')
|
||||||
|
try {
|
||||||
|
setCsvData(jsonToCSV(addressBook))
|
||||||
|
} catch (e) {
|
||||||
|
setLoading(false)
|
||||||
|
setError(e.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setLoading(false)
|
||||||
|
setDoRetry(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCsvData()
|
||||||
|
}, [addressBook, isOpen, doRetry, csvData])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal description="Export address book" handleClose={onClose} open={isOpen} title="Export address book">
|
||||||
|
<Modal.Header onClose={onClose}>
|
||||||
|
<Modal.Header.Title withoutMargin>Export address book</Modal.Header.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body withoutPadding>
|
||||||
|
<ImageContainer>
|
||||||
|
<BodyImage>
|
||||||
|
<Img alt="Export" height={92} src={error ? ErrorSvg : loading ? LoadingSvg : SuccessSvg} />
|
||||||
|
</BodyImage>
|
||||||
|
</ImageContainer>
|
||||||
|
<InfoContainer>
|
||||||
|
<Text color="primary" as="p" size="xl">
|
||||||
|
{!error ? (
|
||||||
|
<Text size="xl" as="span">
|
||||||
|
You're about to export a CSV file with{' '}
|
||||||
|
<Text size="xl" strong as="span">
|
||||||
|
{addressBook.length} address book entries
|
||||||
|
</Text>
|
||||||
|
.
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
<Text size="xl" as="span">
|
||||||
|
An error occurred while generating the address book CSV.
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
</InfoContainer>
|
||||||
|
</Modal.Body>
|
||||||
|
<Modal.Footer withoutBorder>
|
||||||
|
<Row>
|
||||||
|
<Button size="md" variant="outlined" onClick={onClose}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button color="primary" size="md" disabled={loading} onClick={error ? () => setDoRetry(true) : handleClose}>
|
||||||
|
{!error ? (
|
||||||
|
<StyledCSVLink data={csvData} bom={true} filename={`gnosis-safe-address-book-${date}`} type="link">
|
||||||
|
{loading && <StyledLoader color="secondaryLight" size="xs" />}
|
||||||
|
Download
|
||||||
|
</StyledCSVLink>
|
||||||
|
) : (
|
||||||
|
'Retry'
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</Row>
|
||||||
|
</Modal.Footer>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import { updateAddressBookEntry } from 'src/logic/addressBook/store/actions/upda
|
||||||
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
|
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
|
||||||
import { isUserAnOwnerOfAnySafe, sameAddress } from 'src/logic/wallets/ethAddresses'
|
import { isUserAnOwnerOfAnySafe, sameAddress } from 'src/logic/wallets/ethAddresses'
|
||||||
import { CreateEditEntryModal } from 'src/routes/safe/components/AddressBook/CreateEditEntryModal'
|
import { CreateEditEntryModal } from 'src/routes/safe/components/AddressBook/CreateEditEntryModal'
|
||||||
|
import { ExportEntriesModal } from 'src/routes/safe/components/AddressBook/ExportEntriesModal'
|
||||||
import DeleteEntryModal from 'src/routes/safe/components/AddressBook/DeleteEntryModal'
|
import DeleteEntryModal from 'src/routes/safe/components/AddressBook/DeleteEntryModal'
|
||||||
import {
|
import {
|
||||||
AB_ADDRESS_ID,
|
AB_ADDRESS_ID,
|
||||||
|
@ -77,6 +78,7 @@ const AddressBookTable = (): ReactElement => {
|
||||||
const [selectedEntry, setSelectedEntry] = useState<Entry>(initialEntryState)
|
const [selectedEntry, setSelectedEntry] = useState<Entry>(initialEntryState)
|
||||||
const [editCreateEntryModalOpen, setEditCreateEntryModalOpen] = useState(false)
|
const [editCreateEntryModalOpen, setEditCreateEntryModalOpen] = useState(false)
|
||||||
const [deleteEntryModalOpen, setDeleteEntryModalOpen] = useState(false)
|
const [deleteEntryModalOpen, setDeleteEntryModalOpen] = useState(false)
|
||||||
|
const [exportEntriesModalOpen, setExportEntriesModalOpen] = useState(false)
|
||||||
const [sendFundsModalOpen, setSendFundsModalOpen] = useState(false)
|
const [sendFundsModalOpen, setSendFundsModalOpen] = useState(false)
|
||||||
const { trackEvent } = useAnalytics()
|
const { trackEvent } = useAnalytics()
|
||||||
|
|
||||||
|
@ -144,7 +146,7 @@ const AddressBookTable = (): ReactElement => {
|
||||||
<ButtonLink
|
<ButtonLink
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedEntry(initialEntryState)
|
setSelectedEntry(initialEntryState)
|
||||||
setEditCreateEntryModalOpen(true)
|
setExportEntriesModalOpen(true)
|
||||||
}}
|
}}
|
||||||
color="primary"
|
color="primary"
|
||||||
iconType="exportImg"
|
iconType="exportImg"
|
||||||
|
@ -283,6 +285,7 @@ const AddressBookTable = (): ReactElement => {
|
||||||
isOpen={deleteEntryModalOpen}
|
isOpen={deleteEntryModalOpen}
|
||||||
onClose={() => setDeleteEntryModalOpen(false)}
|
onClose={() => setDeleteEntryModalOpen(false)}
|
||||||
/>
|
/>
|
||||||
|
<ExportEntriesModal isOpen={exportEntriesModalOpen} onClose={() => setExportEntriesModalOpen(false)} />
|
||||||
<SendModal
|
<SendModal
|
||||||
activeScreenType="chooseTxType"
|
activeScreenType="chooseTxType"
|
||||||
isOpen={sendFundsModalOpen}
|
isOpen={sendFundsModalOpen}
|
||||||
|
|
20
yarn.lock
20
yarn.lock
|
@ -3567,6 +3567,13 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/npmlog/-/npmlog-4.1.2.tgz#d070fe6a6b78755d1092a3dc492d34c3d8f871c4"
|
resolved "https://registry.yarnpkg.com/@types/npmlog/-/npmlog-4.1.2.tgz#d070fe6a6b78755d1092a3dc492d34c3d8f871c4"
|
||||||
integrity sha512-4QQmOF5KlwfxJ5IGXFIudkeLCdMABz03RcUXu+LCb24zmln8QW6aDjuGl4d4XPVLf2j+FnjelHTP7dvceAFbhA==
|
integrity sha512-4QQmOF5KlwfxJ5IGXFIudkeLCdMABz03RcUXu+LCb24zmln8QW6aDjuGl4d4XPVLf2j+FnjelHTP7dvceAFbhA==
|
||||||
|
|
||||||
|
"@types/papaparse@^5.0.4":
|
||||||
|
version "5.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/papaparse/-/papaparse-5.2.5.tgz#9d3cd9d932eb0dccda9e3f73f39996c4da3fa628"
|
||||||
|
integrity sha512-TlqGskBad6skAgx2ifQmkO/FwiwObuWltBvX2bDceQhXh9IyZ7jYCK7qwhjB67kxw+0LJDXXM4jN3lcGqm1g5w==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/parse-json@^4.0.0":
|
"@types/parse-json@^4.0.0":
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||||
|
@ -15494,6 +15501,11 @@ pako@^1.0.4, pako@~1.0.5:
|
||||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
||||||
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
|
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
|
||||||
|
|
||||||
|
papaparse@^5.2.0:
|
||||||
|
version "5.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.3.0.tgz#ab1702feb96e79ab4309652f36db9536563ad05a"
|
||||||
|
integrity sha512-Lb7jN/4bTpiuGPrYy4tkKoUS8sTki8zacB5ke1p5zolhcSE4TlWgrlsxjrDTbG/dFVh07ck7X36hUf/b5V68pg==
|
||||||
|
|
||||||
parallel-transform@^1.1.0:
|
parallel-transform@^1.1.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc"
|
resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc"
|
||||||
|
@ -17436,6 +17448,14 @@ react-modal@^3.12.1:
|
||||||
react-lifecycles-compat "^3.0.0"
|
react-lifecycles-compat "^3.0.0"
|
||||||
warning "^4.0.3"
|
warning "^4.0.3"
|
||||||
|
|
||||||
|
react-papaparse@^3.14.0:
|
||||||
|
version "3.14.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-papaparse/-/react-papaparse-3.14.0.tgz#4b4e9981656d8e72889ae312d2781ca001b40c78"
|
||||||
|
integrity sha512-EfVJdyy9J4Ee4gS2qB9g5kPWLxlAnsguG6cpC+SHpVS2iVPoDqeUi9OTu8YwgFCoj5x+FF1o1S/lF/j5bfEmRA==
|
||||||
|
dependencies:
|
||||||
|
"@types/papaparse" "^5.0.4"
|
||||||
|
papaparse "^5.2.0"
|
||||||
|
|
||||||
react-popper-tooltip@^2.8.3:
|
react-popper-tooltip@^2.8.3:
|
||||||
version "2.11.1"
|
version "2.11.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-popper-tooltip/-/react-popper-tooltip-2.11.1.tgz#3c4bdfd8bc10d1c2b9a162e859bab8958f5b2644"
|
resolved "https://registry.yarnpkg.com/react-popper-tooltip/-/react-popper-tooltip-2.11.1.tgz#3c4bdfd8bc10d1c2b9a162e859bab8958f5b2644"
|
||||||
|
|
Loading…
Reference in New Issue