Fetch apps from external resource (#2129)

* Configure Safe Apps list with URL parameter

* Fix load in AppFrame to allow deleting only manually added apps
This commit is contained in:
Daniel Sanchez 2021-04-21 09:24:16 +02:00 committed by GitHub
parent 69cc936835
commit 072d9d9980
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 8 deletions

View File

@ -0,0 +1,21 @@
import axios from 'axios'
import { SAFE_APPS_LIST_URL } from 'src/utils/constants'
export type TokenListResult = {
name: string
timestamp: string
apps: AppData[]
}
export type AppData = {
url: string
name?: string
disabled?: boolean
description?: string
networks: number[]
}
export const fetchSafeAppsList = async (): Promise<TokenListResult> => {
return axios.get(SAFE_APPS_LIST_URL).then(({ data }) => data)
}

View File

@ -27,7 +27,7 @@ import { SAFELIST_ADDRESS } from 'src/routes/routes'
import { isSameURL } from 'src/utils/url'
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
import { loadFromStorage, saveToStorage } from 'src/utils/storage'
import { staticAppsList } from 'src/routes/safe/components/Apps/utils'
import { useAppList } from '../hooks/useAppList'
import { LoadingContainer } from 'src/components/LoaderContainer/index'
import { TIMEOUT } from 'src/utils/constants'
import { web3ReadOnly } from 'src/logic/wallets/getWeb3'
@ -103,6 +103,7 @@ const AppFrame = ({ appUrl }: Props): React.ReactElement => {
const { trackEvent } = useAnalytics()
const history = useHistory()
const { consentReceived, onConsentReceipt } = useLegalConsent()
const { staticAppsList } = useAppList()
const matchSafeWithAddress = useRouteMatch<{ safeAddress: string }>({ path: `${SAFELIST_ADDRESS}/:safeAddress` })
@ -267,9 +268,10 @@ const AppFrame = ({ appUrl }: Props): React.ReactElement => {
setIsAppDeletable(!existsStaticApp)
setSafeApp(app)
}
loadApp()
}, [appUrl])
if (staticAppsList.length) {
loadApp()
}
}, [appUrl, staticAppsList])
//track GA
useEffect(() => {

View File

@ -1,15 +1,29 @@
import { useState, useEffect } from 'react'
import { loadFromStorage } from 'src/utils/storage'
import { APPS_STORAGE_KEY, getAppInfoFromUrl, getEmptySafeApp, staticAppsList } from '../utils'
import { APPS_STORAGE_KEY, getAppInfoFromUrl, getAppsList, getEmptySafeApp } from '../utils'
import { AppData } from '../api/fetchSafeAppsList'
import { SafeApp, StoredSafeApp, SAFE_APP_FETCH_STATUS } from '../types.d'
import { getNetworkId } from 'src/config'
type UseAppListReturnType = {
appList: SafeApp[]
staticAppsList: AppData[]
}
const useAppList = (): UseAppListReturnType => {
const [appList, setAppList] = useState<SafeApp[]>([])
const [staticAppsList, setStaticAppsList] = useState<AppData[]>([])
useEffect(() => {
const loadAppsList = async () => {
const remoteAppsList = await getAppsList()
setStaticAppsList(remoteAppsList)
}
if (!staticAppsList.length) {
loadAppsList()
}
}, [staticAppsList])
// Load apps list
// for each URL we return a mocked safe-app with a loading status
@ -42,21 +56,31 @@ const useAppList = (): UseAppListReturnType => {
.filter((app) => (!app.networks ? true : app.networks.includes(getNetworkId())))
.map((app) => ({
...getEmptySafeApp(),
...app,
url: app.url.trim(),
}))
setAppList(apps)
apps.forEach((app) => getAppInfoFromUrl(app.url).then(fetchAppCallback))
apps.forEach((app) => {
if (!app.name || app.name === 'unknown') {
// We are using legacy mode, we have to fetch info from manifest
getAppInfoFromUrl(app.url).then(fetchAppCallback)
} else {
// We already have manifest information so we directly add the app
fetchAppCallback(app)
}
})
}
if (!appList.length) {
if (staticAppsList.length) {
loadApps()
}
}, [appList])
}, [staticAppsList])
return {
appList,
staticAppsList,
}
}

View File

@ -6,6 +6,7 @@ import { SafeApp, SAFE_APP_FETCH_STATUS } from './types.d'
import { getContentFromENS } from 'src/logic/wallets/getWeb3'
import appsIconSvg from 'src/assets/icons/apps.svg'
import { ETHEREUM_NETWORK } from 'src/config/networks/network.d'
import { AppData, fetchSafeAppsList } from './api/fetchSafeAppsList'
export const APPS_STORAGE_KEY = 'APPS_STORAGE_KEY'
@ -175,6 +176,17 @@ export const staticAppsList: Array<StaticAppInfo> = [
},
]
export const getAppsList = async (): Promise<AppData[]> => {
let result
try {
result = await fetchSafeAppsList()
} catch (error) {
console.error('Could not fetch remote apps list', error)
}
return result?.apps && result?.apps.length ? result.apps : staticAppsList
}
export const getAppInfoFromOrigin = (origin: string): { url: string; name: string } | null => {
try {
return JSON.parse(origin)

View File

@ -25,6 +25,9 @@ export const TIMEOUT = process.env.NODE_ENV === 'test' ? 1500 : 5000
export const ETHERSCAN_API_KEY = process.env.REACT_APP_ETHERSCAN_API_KEY
export const EXCHANGE_RATE_URL = 'https://api.exchangeratesapi.io/latest'
export const EXCHANGE_RATE_URL_FALLBACK = 'https://api.coinbase.com/v2/exchange-rates'
export const SAFE_APPS_LIST_URL =
process.env.REACT_APP_SAFE_APPS_LIST_URL ||
'https://raw.githubusercontent.com/gnosis/safe-apps-list/main/public/gnosis-default.applist.json'
export const IPFS_GATEWAY = process.env.REACT_APP_IPFS_GATEWAY
export const SPENDING_LIMIT_MODULE_ADDRESS =
process.env.REACT_APP_SPENDING_LIMIT_MODULE_ADDRESS || '0xCFbFaC74C26F8647cBDb8c5caf80BB5b32E43134'