Adding Apps info to toast notification (#621)

* Adding APP_Name for APPs TXs in toast messages

* refactor: save appId instead of appName in origin field

* adding fallback icon and message to TX apps table

* review changes

* review fixes

* force build
This commit is contained in:
nicolas 2020-03-02 12:51:37 -03:00 committed by GitHub
parent 0037522505
commit 63252566dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 125 additions and 48 deletions

View File

@ -7,6 +7,7 @@ import { NOTIFICATIONS, type Notification } from './notificationTypes'
import closeSnackbarAction from '~/logic/notifications/store/actions/closeSnackbar' import closeSnackbarAction from '~/logic/notifications/store/actions/closeSnackbar'
import { TX_NOTIFICATION_TYPES } from '~/logic/safe/transactions' import { TX_NOTIFICATION_TYPES } from '~/logic/safe/transactions'
import { getAppInfo } from '~/routes/safe/components/Apps/appsList'
import { store } from '~/store' import { store } from '~/store'
export type NotificationsQueue = { export type NotificationsQueue = {
@ -21,15 +22,26 @@ export type NotificationsQueue = {
afterRejection: Notification | null, afterRejection: Notification | null,
} }
const standardTxNotificationsQueue: NotificationsQueue = { const setNotificationOrigin = (notification: Notification, origin: string): Notification => {
beforeExecution: NOTIFICATIONS.SIGN_TX_MSG, if (!origin) {
pendingExecution: NOTIFICATIONS.TX_PENDING_MSG, return notification
afterRejection: NOTIFICATIONS.TX_REJECTED_MSG, }
afterExecution: {
noMoreConfirmationsNeeded: NOTIFICATIONS.TX_EXECUTED_MSG, const appInfo = getAppInfo(origin)
moreConfirmationsNeeded: NOTIFICATIONS.TX_EXECUTED_MORE_CONFIRMATIONS_MSG, return { ...notification, message: `${appInfo.name}: ${notification.message}` }
}, }
afterExecutionError: NOTIFICATIONS.TX_FAILED_MSG,
const getStandardTxNotificationsQueue = (origin?: string): NotificationsQueue => {
return {
beforeExecution: setNotificationOrigin(NOTIFICATIONS.SIGN_TX_MSG, origin),
pendingExecution: setNotificationOrigin(NOTIFICATIONS.TX_PENDING_MSG, origin),
afterRejection: setNotificationOrigin(NOTIFICATIONS.TX_REJECTED_MSG, origin),
afterExecution: {
noMoreConfirmationsNeeded: setNotificationOrigin(NOTIFICATIONS.TX_EXECUTED_MSG, origin),
moreConfirmationsNeeded: setNotificationOrigin(NOTIFICATIONS.TX_EXECUTED_MORE_CONFIRMATIONS_MSG, origin),
},
afterExecutionError: setNotificationOrigin(NOTIFICATIONS.TX_FAILED_MSG, origin),
}
} }
const waitingTransactionNotificationsQueue: NotificationsQueue = { const waitingTransactionNotificationsQueue: NotificationsQueue = {
@ -41,26 +53,30 @@ const waitingTransactionNotificationsQueue: NotificationsQueue = {
afterExecutionError: null, afterExecutionError: null,
} }
const confirmationTxNotificationsQueue: NotificationsQueue = { const getConfirmationTxNotificationsQueue = (origin?: string): NotificationsQueue => {
beforeExecution: NOTIFICATIONS.SIGN_TX_MSG, return {
pendingExecution: NOTIFICATIONS.TX_CONFIRMATION_PENDING_MSG, beforeExecution: setNotificationOrigin(NOTIFICATIONS.SIGN_TX_MSG, origin),
afterRejection: NOTIFICATIONS.TX_REJECTED_MSG, pendingExecution: setNotificationOrigin(NOTIFICATIONS.TX_CONFIRMATION_PENDING_MSG, origin),
afterExecution: { afterRejection: setNotificationOrigin(NOTIFICATIONS.TX_REJECTED_MSG, origin),
noMoreConfirmationsNeeded: NOTIFICATIONS.TX_EXECUTED_MSG, afterExecution: {
moreConfirmationsNeeded: NOTIFICATIONS.TX_CONFIRMATION_EXECUTED_MSG, noMoreConfirmationsNeeded: setNotificationOrigin(NOTIFICATIONS.TX_EXECUTED_MSG, origin),
}, moreConfirmationsNeeded: setNotificationOrigin(NOTIFICATIONS.TX_CONFIRMATION_EXECUTED_MSG, origin),
afterExecutionError: NOTIFICATIONS.TX_CONFIRMATION_FAILED_MSG, },
afterExecutionError: setNotificationOrigin(NOTIFICATIONS.TX_CONFIRMATION_FAILED_MSG, origin),
}
} }
const cancellationTxNotificationsQueue: NotificationsQueue = { const getCancellationTxNotificationsQueue = (origin?: string): NotificationsQueue => {
beforeExecution: NOTIFICATIONS.SIGN_TX_MSG, return {
pendingExecution: NOTIFICATIONS.TX_PENDING_MSG, beforeExecution: setNotificationOrigin(NOTIFICATIONS.SIGN_TX_MSG, origin),
afterRejection: NOTIFICATIONS.TX_REJECTED_MSG, pendingExecution: setNotificationOrigin(NOTIFICATIONS.TX_PENDING_MSG, origin),
afterExecution: { afterRejection: setNotificationOrigin(NOTIFICATIONS.TX_REJECTED_MSG, origin),
noMoreConfirmationsNeeded: NOTIFICATIONS.TX_EXECUTED_MSG, afterExecution: {
moreConfirmationsNeeded: NOTIFICATIONS.TX_CANCELLATION_EXECUTED_MSG, noMoreConfirmationsNeeded: setNotificationOrigin(NOTIFICATIONS.TX_EXECUTED_MSG, origin),
}, moreConfirmationsNeeded: setNotificationOrigin(NOTIFICATIONS.TX_CANCELLATION_EXECUTED_MSG, origin),
afterExecutionError: NOTIFICATIONS.TX_FAILED_MSG, },
afterExecutionError: setNotificationOrigin(NOTIFICATIONS.TX_FAILED_MSG, origin),
}
} }
const safeNameChangeNotificationsQueue: NotificationsQueue = { const safeNameChangeNotificationsQueue: NotificationsQueue = {
@ -143,20 +159,20 @@ const addressBookDeleteEntry: NotificationsQueue = {
afterExecutionError: null, afterExecutionError: null,
} }
export const getNotificationsFromTxType = (txType: string) => { export const getNotificationsFromTxType = (txType: string, origin?: string) => {
let notificationsQueue: NotificationsQueue let notificationsQueue: NotificationsQueue
switch (txType) { switch (txType) {
case TX_NOTIFICATION_TYPES.STANDARD_TX: { case TX_NOTIFICATION_TYPES.STANDARD_TX: {
notificationsQueue = standardTxNotificationsQueue notificationsQueue = getStandardTxNotificationsQueue(origin)
break break
} }
case TX_NOTIFICATION_TYPES.CONFIRMATION_TX: { case TX_NOTIFICATION_TYPES.CONFIRMATION_TX: {
notificationsQueue = confirmationTxNotificationsQueue notificationsQueue = getConfirmationTxNotificationsQueue(origin)
break break
} }
case TX_NOTIFICATION_TYPES.CANCELLATION_TX: { case TX_NOTIFICATION_TYPES.CANCELLATION_TX: {
notificationsQueue = cancellationTxNotificationsQueue notificationsQueue = getCancellationTxNotificationsQueue(origin)
break break
} }
case TX_NOTIFICATION_TYPES.SETTINGS_CHANGE_TX: { case TX_NOTIFICATION_TYPES.SETTINGS_CHANGE_TX: {

View File

@ -1,7 +1,9 @@
// @flow // @flow
import appsIconSvg from '../Transactions/TxsTable/TxType/assets/appsIcon.svg'
const appList = [ const appList = [
{ {
id: 1, id: '1',
name: 'Compound', name: 'Compound',
url: process.env.REACT_APP_GNOSIS_APPS_URL || 'https://gnosis-apps.netlify.com', url: process.env.REACT_APP_GNOSIS_APPS_URL || 'https://gnosis-apps.netlify.com',
iconUrl: 'https://compound.finance/images/compound-mark.svg', iconUrl: 'https://compound.finance/images/compound-mark.svg',
@ -9,7 +11,7 @@ const appList = [
providedBy: { name: 'Gnosis', url: '' }, providedBy: { name: 'Gnosis', url: '' },
}, },
{ {
id: 2, id: '2',
name: 'ENS Manager', name: 'ENS Manager',
url: '', url: '',
iconUrl: 'https://app.ens.domains/static/media/ensIconLogo.4d995d23.svg', iconUrl: 'https://app.ens.domains/static/media/ensIconLogo.4d995d23.svg',
@ -17,7 +19,7 @@ const appList = [
providedBy: { name: 'Gnosis', url: '' }, providedBy: { name: 'Gnosis', url: '' },
}, },
{ {
id: 3, id: '3',
name: 'Uniswap', name: 'Uniswap',
url: '', url: '',
iconUrl: iconUrl:
@ -26,7 +28,7 @@ const appList = [
providedBy: { name: 'Gnosis', url: '' }, providedBy: { name: 'Gnosis', url: '' },
}, },
{ {
id: 4, id: '4',
name: 'Nexus Mutual', name: 'Nexus Mutual',
url: '', url: '',
iconUrl: iconUrl:
@ -41,4 +43,20 @@ const appList = [
export default appList export default appList
export const getAppInfo = (appName: string) => appList.find(app => app.name === appName) export const getAppInfo = (appId: string) => {
const res = appList.find(app => app.id === appId.toString())
if (!res) {
return {
id: 0,
name: 'External App',
url: null,
iconUrl: appsIconSvg,
description: null,
providedBy: {
name: null,
url: null,
},
}
}
return res
}

View File

@ -1,14 +1,14 @@
// @flow // @flow
import { withSnackbar } from 'notistack'
import React, { useCallback, useEffect, useState } from 'react' import React, { useCallback, useEffect, useState } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import ButtonLink from '../../../../components/layout/ButtonLink'
import appsList from './appsList' import appsList from './appsList'
import confirmTransactions from './confirmTransactions' import confirmTransactions from './confirmTransactions'
import sendTransactions from './sendTransactions' import sendTransactions from './sendTransactions'
import { ListContentLayout as LCL, Loader } from '~/components-v2' import { ListContentLayout as LCL, Loader } from '~/components-v2'
import ButtonLink from '~/components/layout/ButtonLink'
const StyledIframe = styled.iframe` const StyledIframe = styled.iframe`
width: 100%; width: 100%;
@ -29,12 +29,25 @@ type Props = {
ethBalance: String, ethBalance: String,
network: String, network: String,
createTransaction: any, createTransaction: any,
enqueueSnackbar: Function,
closeSnackbar: Function,
openModal: () => {}, openModal: () => {},
closeModal: () => {}, closeModal: () => {},
} }
function Apps({ closeModal, createTransaction, ethBalance, network, openModal, safeAddress, safeName, web3 }: Props) { function Apps({
const [selectedApp, setSelectedApp] = useState(1) closeModal,
closeSnackbar,
createTransaction,
enqueueSnackbar,
ethBalance,
network,
openModal,
safeAddress,
safeName,
web3,
}: Props) {
const [selectedApp, setSelectedApp] = useState('1')
const [appIsLoading, setAppIsLoading] = useState(true) const [appIsLoading, setAppIsLoading] = useState(true)
const [iframeEl, setframeEl] = useState(null) const [iframeEl, setframeEl] = useState(null)
@ -55,7 +68,15 @@ function Apps({ closeModal, createTransaction, ethBalance, network, openModal, s
const onConfirm = async () => { const onConfirm = async () => {
closeModal() closeModal()
const txHash = await sendTransactions(web3, createTransaction, safeAddress, data.data, getSelectedApp().name) const txHash = await sendTransactions(
web3,
createTransaction,
safeAddress,
data.data,
enqueueSnackbar,
closeSnackbar,
getSelectedApp().id,
)
if (txHash) { if (txHash) {
sendMessageToIframe(operations.ON_TX_UPDATE, { sendMessageToIframe(operations.ON_TX_UPDATE, {
@ -184,4 +205,4 @@ function Apps({ closeModal, createTransaction, ethBalance, network, openModal, s
) )
} }
export default Apps export default withSnackbar(Apps)

View File

@ -14,7 +14,15 @@ const multiSendAbi = [
}, },
] ]
const sendTransactions = (web3: any, createTransaction: any, safeAddress: String, txs: Array<any>, origin: string) => { const sendTransactions = (
web3: any,
createTransaction: any,
safeAddress: String,
txs: Array<any>,
enqueueSnackbar: Function,
closeSnackbar: Function,
origin: string,
) => {
const multiSend = new web3.eth.Contract(multiSendAbi, multiSendAddress) const multiSend = new web3.eth.Contract(multiSendAbi, multiSendAddress)
const encodeMultiSendCalldata = multiSend.methods const encodeMultiSendCalldata = multiSend.methods
@ -39,8 +47,8 @@ const sendTransactions = (web3: any, createTransaction: any, safeAddress: String
valueInWei: 0, valueInWei: 0,
txData: encodeMultiSendCalldata, txData: encodeMultiSendCalldata,
notifiedTransaction: 'STANDARD_TX', notifiedTransaction: 'STANDARD_TX',
enqueueSnackbar: () => {}, enqueueSnackbar,
closeSnackbar: () => {}, closeSnackbar,
operation: DELEGATE_CALL, operation: DELEGATE_CALL,
navigateToTransactionsTab: false, navigateToTransactionsTab: false,
origin, origin,

View File

@ -74,6 +74,7 @@ const RejectTxModal = ({
enqueueSnackbar, enqueueSnackbar,
closeSnackbar, closeSnackbar,
txNonce: tx.nonce, txNonce: tx.nonce,
origin: tx.origin,
}) })
onClose() onClose()
} }

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path
className="fill"
fillRule="nonzero"
d="M2 1h4a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zm1 2v2h2V3H3zM10 9h4a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1zm1 2v2h2v-2h-2zM12 7a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-1.9a1.1 1.1 0 1 1 0-2.2 1.1 1.1 0 0 1 0 2.2z"
/>
<path
className="fill"
d="M2.641 11.999l-1.36-1.361A.96.96 0 0 1 2.637 9.28l1.36 1.36 1.362-1.36a.959.959 0 1 1 1.357 1.357l-1.36 1.36 1.36 1.362a.96.96 0 0 1-1.357 1.358L4 13.356l-1.361 1.362a.962.962 0 0 1-1.358 0 .964.964 0 0 1 0-1.358l1.361-1.361z"
/>
</svg>

After

Width:  |  Height:  |  Size: 690 B

View File

@ -31,7 +31,7 @@ const typeToLabel = {
const TxType = ({ txType, origin }: { txType: TransactionType, origin: string | null }) => { const TxType = ({ txType, origin }: { txType: TransactionType, origin: string | null }) => {
const iconUrl = txType === 'third-party-app' ? getAppInfo(origin).iconUrl : typeToIcon[txType] const iconUrl = txType === 'third-party-app' ? getAppInfo(origin).iconUrl : typeToIcon[txType]
const text = txType === 'third-party-app' ? origin : typeToLabel[txType] const text = txType === 'third-party-app' ? getAppInfo(origin).name : typeToLabel[txType]
return <IconText iconUrl={iconUrl} text={text} /> return <IconText iconUrl={iconUrl} text={text} />
} }

View File

@ -67,7 +67,7 @@ const createTransaction = ({
'', '',
)}000000000000000000000000000000000000000000000000000000000000000001` )}000000000000000000000000000000000000000000000000000000000000000001`
const notificationsQueue: NotificationsQueue = getNotificationsFromTxType(notifiedTransaction) const notificationsQueue: NotificationsQueue = getNotificationsFromTxType(notifiedTransaction, origin)
const beforeExecutionKey = showSnackbar(notificationsQueue.beforeExecution, enqueueSnackbar, closeSnackbar) const beforeExecutionKey = showSnackbar(notificationsQueue.beforeExecution, enqueueSnackbar, closeSnackbar)
let pendingExecutionKey let pendingExecutionKey

View File

@ -56,7 +56,7 @@ const processTransaction = ({
)}000000000000000000000000000000000000000000000000000000000000000001` )}000000000000000000000000000000000000000000000000000000000000000001`
} }
const notificationsQueue: NotificationsQueue = getNotificationsFromTxType(notifiedTransaction) const notificationsQueue: NotificationsQueue = getNotificationsFromTxType(notifiedTransaction, tx.origin)
const beforeExecutionKey = showSnackbar(notificationsQueue.beforeExecution, enqueueSnackbar, closeSnackbar) const beforeExecutionKey = showSnackbar(notificationsQueue.beforeExecution, enqueueSnackbar, closeSnackbar)
let pendingExecutionKey let pendingExecutionKey