diff --git a/src/routes/safe/components/Apps/index.jsx b/src/routes/safe/components/Apps/index.jsx
index 258c4f5f..241fca86 100644
--- a/src/routes/safe/components/Apps/index.jsx
+++ b/src/routes/safe/components/Apps/index.jsx
@@ -21,6 +21,7 @@ import {
safeParamAddressFromStateSelector,
} from '~/routes/safe/store/selectors'
import { loadFromStorage, saveToStorage } from '~/utils/storage'
+import { isSameHref } from '~/utils/url'
const APPS_STORAGE_KEY = 'APPS_STORAGE_KEY'
const APPS_LEGAL_DISCLAIMER_STORAGE_KEY = 'APPS_LEGAL_DISCLAIMER_STORAGE_KEY'
@@ -30,7 +31,6 @@ const StyledIframe = styled.iframe`
box-sizing: border-box;
width: 100%;
height: 100%;
- display: ${(props) => (props.shouldDisplay ? 'block' : 'none')};
`
const Centered = styled.div`
display: flex;
@@ -68,7 +68,8 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
const getSelectedApp = () => appList.find((e) => e.id === selectedApp)
const sendMessageToIframe = (messageId, data) => {
- iframeEl.contentWindow.postMessage({ messageId, data }, getSelectedApp().url)
+ const app = getSelectedApp()
+ iframeEl.contentWindow.postMessage({ messageId, data }, app.url)
}
const handleIframeMessage = async (data) => {
@@ -89,6 +90,7 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
safeAddress,
safeName,
ethBalance,
+ getSelectedApp().name,
getSelectedApp().iconUrl,
data.data,
openModal,
@@ -172,16 +174,18 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
)
}
+ const app = getSelectedApp()
+
return (
<>
{appIsLoading &&
}
>
)
@@ -250,8 +254,9 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
return
}
- if (!getSelectedApp().url.includes(origin)) {
- console.error(`ThirdPartyApp: A message from was received from an unknown origin ${origin}`)
+ const app = getSelectedApp()
+ if (!app.url.includes(origin)) {
+ console.error(`ThirdPartyApp: A message was received from an unknown origin ${origin}`)
return
}
@@ -263,7 +268,7 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
return () => {
window.removeEventListener('message', onIframeMessage)
}
- })
+ }, [selectedApp])
// load legalDisclaimer
useEffect(() => {
@@ -276,7 +281,7 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
}
checkLegalDisclaimer()
- })
+ }, [])
// Load apps list
useEffect(() => {
@@ -333,7 +338,8 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
})
}
- if (!iframeEl) {
+ const app = getSelectedApp()
+ if (!iframeEl || !selectedApp || !isSameHref(iframeEl.src, app.url)) {
return
}
@@ -342,7 +348,7 @@ function Apps({ closeModal, closeSnackbar, enqueueSnackbar, openModal }: Props)
return () => {
iframeEl.removeEventListener('load', onIframeLoaded)
}
- }, [iframeEl])
+ }, [iframeEl, selectedApp])
if (loading) {
return
diff --git a/src/routes/safe/components/Apps/utils.js b/src/routes/safe/components/Apps/utils.js
index 70965b29..a5689e83 100644
--- a/src/routes/safe/components/Apps/utils.js
+++ b/src/routes/safe/components/Apps/utils.js
@@ -3,7 +3,14 @@ import axios from 'axios'
import appsIconSvg from '~/routes/safe/components/Transactions/TxsTable/TxType/assets/appsIcon.svg'
-const gnosisAppsUrl = process.env.REACT_APP_GNOSIS_APPS_URL
+const removeLastTrailingSlash = (url: string) => {
+ if (url.substr(-1) === '/') {
+ return url.substr(0, url.length - 1)
+ }
+ return url
+}
+
+const gnosisAppsUrl = removeLastTrailingSlash(process.env.REACT_APP_GNOSIS_APPS_URL)
export const staticAppsList = [
{ url: `${gnosisAppsUrl}/compound`, disabled: false },
{ url: `${gnosisAppsUrl}/aave`, disabled: false },
@@ -29,40 +36,42 @@ export const getAppInfoFromUrl = async (appUrl: string) => {
return res
}
- let cleanedUpAppUrl = appUrl.trim()
- if (cleanedUpAppUrl.substr(-1) === '/') {
- cleanedUpAppUrl = cleanedUpAppUrl.substr(0, cleanedUpAppUrl.length - 1)
- res.url = cleanedUpAppUrl
- }
+ res.url = appUrl.trim()
+ let noTrailingSlashUrl = removeLastTrailingSlash(res.url)
try {
- const appInfo = await axios.get(`${cleanedUpAppUrl}/manifest.json`)
+ const appInfo = await axios.get(`${noTrailingSlashUrl}/manifest.json`)
// verify imported app fulfil safe requirements
if (!appInfo || !appInfo.data || !appInfo.data.name || !appInfo.data.description) {
throw Error('The app does not fulfil the structure required.')
}
+ // the DB origin field has a limit of 100 characters
+ const originFieldSize = 100
+ const jsonDataLength = 20
+ const remainingSpace = originFieldSize - res.url.length - jsonDataLength
+
res = {
...res,
...appInfo.data,
- id: JSON.stringify({ url: cleanedUpAppUrl, name: appInfo.data.name }),
+ id: JSON.stringify({ url: res.url, name: appInfo.data.name.substring(0, remainingSpace) }),
error: false,
}
+
if (appInfo.data.iconPath) {
try {
- const iconInfo = await axios.get(`${cleanedUpAppUrl}/${appInfo.data.iconPath}`)
+ const iconInfo = await axios.get(`${noTrailingSlashUrl}/${appInfo.data.iconPath}`, { timeout: 1000 * 10 })
if (/image\/\w/gm.test(iconInfo.headers['content-type'])) {
- res.iconUrl = `${cleanedUpAppUrl}/${appInfo.data.iconPath}`
+ res.iconUrl = `${noTrailingSlashUrl}/${appInfo.data.iconPath}`
}
} catch (error) {
- console.error(`It was not possible to fetch icon from app ${cleanedUpAppUrl}`)
+ console.error(`It was not possible to fetch icon from app ${res.url}`)
}
}
-
return res
} catch (error) {
- console.error(`It was not possible to fetch app from ${cleanedUpAppUrl}: ${error.message}`)
+ console.error(`It was not possible to fetch app from ${res.url}: ${error.message}`)
return res
}
}
diff --git a/src/routes/safe/components/Balances/Coins/index.jsx b/src/routes/safe/components/Balances/Coins/index.jsx
index 315b34db..98bb886b 100644
--- a/src/routes/safe/components/Balances/Coins/index.jsx
+++ b/src/routes/safe/components/Balances/Coins/index.jsx
@@ -19,8 +19,8 @@ import Button from '~/components/layout/Button'
import Row from '~/components/layout/Row'
import {
currencyRateSelector,
- currencyValuesListSelector,
currentCurrencySelector,
+ safeFiatBalancesListSelector,
} from '~/logic/currencyValues/store/selectors'
import { BALANCE_ROW_TEST_ID } from '~/routes/safe/components/Balances'
import AssetTableCell from '~/routes/safe/components/Balances/AssetTableCell'
@@ -46,16 +46,16 @@ const Coins = (props: Props) => {
const classes = useStyles()
const columns = generateColumns()
const autoColumns = columns.filter((c) => !c.custom)
- const currencySelected = useSelector(currentCurrencySelector)
+ const selectedCurrency = useSelector(currentCurrencySelector)
const currencyRate = useSelector(currencyRateSelector)
const activeTokens = useSelector(extendedSafeTokensSelector)
- const currencyValues = useSelector(currencyValuesListSelector)
+ const currencyValues = useSelector(safeFiatBalancesListSelector)
const granted = useSelector(grantedSelector)
const [filteredData, setFilteredData] = React.useState(List())
React.useMemo(() => {
- setFilteredData(getBalanceData(activeTokens, currencySelected, currencyValues, currencyRate))
- }, [currencySelected, currencyRate, activeTokens.hashCode(), currencyValues.hashCode()])
+ setFilteredData(getBalanceData(activeTokens, selectedCurrency, currencyValues, currencyRate))
+ }, [selectedCurrency, currencyRate, activeTokens.hashCode(), currencyValues])
return (
diff --git a/src/routes/safe/components/Balances/SendModal/screens/SendCollectible/index.jsx b/src/routes/safe/components/Balances/SendModal/screens/SendCollectible/index.jsx
index 80b07e40..92efe240 100644
--- a/src/routes/safe/components/Balances/SendModal/screens/SendCollectible/index.jsx
+++ b/src/routes/safe/components/Balances/SendModal/screens/SendCollectible/index.jsx
@@ -9,20 +9,21 @@ import ArrowDown from '../assets/arrow-down.svg'
import { styles } from './style'
-import QRIcon from '~/assets/icons/qrcode.svg'
import CopyBtn from '~/components/CopyBtn'
import EtherscanBtn from '~/components/EtherscanBtn'
import Identicon from '~/components/Identicon'
-import ScanQRModal from '~/components/ScanQRModal'
+import { ScanQRWrapper } from '~/components/ScanQRModal/ScanQRWrapper'
import WhenFieldChanges from '~/components/WhenFieldChanges'
import GnoForm from '~/components/forms/GnoForm'
import Block from '~/components/layout/Block'
import Button from '~/components/layout/Button'
import Col from '~/components/layout/Col'
import Hairline from '~/components/layout/Hairline'
-import Img from '~/components/layout/Img'
import Paragraph from '~/components/layout/Paragraph'
import Row from '~/components/layout/Row'
+import type { AddressBook } from '~/logic/addressBook/model/addressBook'
+import { getAddressBook } from '~/logic/addressBook/store/selectors'
+import { getNameFromAdbk } from '~/logic/addressBook/utils'
import type { NFTAssetsState, NFTTokensState } from '~/logic/collectibles/store/reducer/collectibles'
import { nftTokensSelector, safeActiveSelectorMap } from '~/logic/collectibles/store/selectors'
import type { NFTToken } from '~/routes/safe/components/Balances/Collectibles/types'
@@ -60,7 +61,7 @@ const SendCollectible = ({ initialValues, onClose, onNext, recipientAddress, sel
const { address: safeAddress, ethBalance, name: safeName } = useSelector(safeSelector)
const nftAssets: NFTAssetsState = useSelector(safeActiveSelectorMap)
const nftTokens: NFTTokensState = useSelector(nftTokensSelector)
- const [qrModalOpen, setQrModalOpen] = useState(false)
+ const addressBook: AddressBook = useSelector(getAddressBook)
const [selectedEntry, setSelectedEntry] = useState