1
0
mirror of https://github.com/oceanprotocol/market.git synced 2024-11-15 01:34:57 +01:00

Merge branch 'main' into fix/issue583-start-second-job

This commit is contained in:
Bogdan Fazakas 2021-05-17 17:24:07 +03:00
commit 7cc6b9b95b
11 changed files with 235 additions and 61 deletions

View File

@ -0,0 +1,71 @@
.button {
display: inline-block;
position: relative;
min-width: auto;
}
.button:hover,
.button:focus {
transform: none;
}
.logoWrap {
position: relative;
display: inline-block;
z-index: 1;
}
.logoWrap::before {
content: '+';
color: var(--color-secondary);
font-family: var(--font-family-base);
font-weight: var(--font-weight-base);
font-size: 1.25em;
position: absolute;
right: 0.05em;
top: 0.05em;
line-height: 0;
}
.logo {
width: 1.6em;
height: 1.6em;
display: inline-block;
margin-bottom: -0.35em;
border-radius: 50%;
border: 0.065rem solid var(--color-secondary);
margin-right: calc(var(--spacer) / 10);
transition: 0.2s ease-out;
}
.button:hover .logo,
.button:focus .logo {
border-color: var(--color-primary);
}
.button:hover .logoWrap::before,
.button:focus .logoWrap::before {
color: var(--color-primary);
}
.text {
display: inline-block;
position: relative;
}
.minimal .text {
opacity: 0;
transform: translate3d(-1rem, 0, 0);
transition: 0.2s ease-out;
z-index: 0;
white-space: pre;
position: absolute;
left: 100%;
top: 0.15rem;
}
.minimal:hover .text,
.minimal:focus .text {
opacity: 1;
transform: translate3d(0, 0, 0);
}

View File

@ -0,0 +1,53 @@
import React, { ReactElement } from 'react'
import classNames from 'classnames/bind'
import { addTokenToWallet } from '../../utils/web3'
import { useWeb3 } from '../../providers/Web3'
import Button from './Button'
import styles from './AddToken.module.css'
const cx = classNames.bind(styles)
export default function AddToken({
address,
symbol,
logo,
text,
className,
minimal
}: {
address: string
symbol: string
logo: string // needs to be a remote image
text?: string
className?: string
minimal?: boolean
}): ReactElement {
const { web3Provider } = useWeb3()
const styleClasses = cx({
button: true,
minimal: minimal,
[className]: className
})
async function handleAddToken() {
if (!web3Provider) return
await addTokenToWallet(web3Provider, address, symbol, logo)
}
return (
<Button
className={styleClasses}
style="text"
size="small"
onClick={handleAddToken}
>
<span className={styles.logoWrap}>
<img src={logo} className={styles.logo} width="16" height="16" />
</span>
<span className={styles.text}>{text || `Add ${symbol}`}</span>
</Button>
)
}

View File

@ -1,20 +1,30 @@
import React, { ReactElement, ReactNode, useEffect, useState } from 'react' import React, { ReactElement, ReactNode, useEffect, useState } from 'react'
import { ReactComponent as External } from '../../images/external.svg' import { ReactComponent as External } from '../../images/external.svg'
import styles from './ExplorerLink.module.css' import classNames from 'classnames/bind'
import { ConfigHelperConfig } from '@oceanprotocol/lib' import { ConfigHelperConfig } from '@oceanprotocol/lib'
import { useOcean } from '../../providers/Ocean' import { useOcean } from '../../providers/Ocean'
import styles from './ExplorerLink.module.css'
const cx = classNames.bind(styles)
export default function ExplorerLink({ export default function ExplorerLink({
path, path,
children children,
className
}: { }: {
networkId: number networkId: number
path: string path: string
children: ReactNode children: ReactNode
className?: string
}): ReactElement { }): ReactElement {
const { config } = useOcean() const { config } = useOcean()
const [url, setUrl] = useState<string>() const [url, setUrl] = useState<string>()
const styleClasses = cx({
link: true,
[className]: className
})
useEffect(() => { useEffect(() => {
setUrl((config as ConfigHelperConfig).explorerUri) setUrl((config as ConfigHelperConfig).explorerUri)
}, [config]) }, [config])
@ -25,7 +35,7 @@ export default function ExplorerLink({
title={`View on ${(config as ConfigHelperConfig).explorerUri}`} title={`View on ${(config as ConfigHelperConfig).explorerUri}`}
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className={styles.link} className={styleClasses}
> >
{children} <External /> {children} <External />
</a> </a>

View File

@ -68,7 +68,6 @@ export default function Publisher({
> >
{name} {name}
</Link> </Link>
<div className={styles.links}> <div className={styles.links}>
{' — '} {' — '}
{profile && ( {profile && (

View File

@ -2,7 +2,6 @@ import React, { ReactElement, useEffect, useState } from 'react'
import { useWeb3 } from '../../providers/Web3' import { useWeb3 } from '../../providers/Web3'
import { addCustomNetwork, NetworkObject } from '../../utils/web3' import { addCustomNetwork, NetworkObject } from '../../utils/web3'
import { getOceanConfig } from '../../utils/ocean' import { getOceanConfig } from '../../utils/ocean'
import { getProviderInfo } from 'web3modal'
import { useOcean } from '../../providers/Ocean' import { useOcean } from '../../providers/Ocean'
import { useSiteMetadata } from '../../hooks/useSiteMetadata' import { useSiteMetadata } from '../../hooks/useSiteMetadata'
import AnnouncementBanner, { import AnnouncementBanner, {
@ -19,7 +18,7 @@ const networkMatic: NetworkObject = {
} }
export default function NetworkBanner(): ReactElement { export default function NetworkBanner(): ReactElement {
const { web3Provider } = useWeb3() const { web3Provider, web3ProviderInfo } = useWeb3()
const { config, connect } = useOcean() const { config, connect } = useOcean()
const { announcement } = useSiteMetadata() const { announcement } = useSiteMetadata()
@ -51,10 +50,9 @@ export default function NetworkBanner(): ReactElement {
} }
useEffect(() => { useEffect(() => {
if (!web3Provider && !config) return if (!web3ProviderInfo || (!web3Provider && !config)) return
const providerInfo = getProviderInfo(web3Provider) switch (web3ProviderInfo.name) {
switch (providerInfo?.name) {
case 'Web3': case 'Web3':
if (config.networkId !== 137) { if (config.networkId !== 137) {
setText(announcement.main) setText(announcement.main)
@ -80,7 +78,7 @@ export default function NetworkBanner(): ReactElement {
setAction(undefined) setAction(undefined)
} }
} }
}, [web3Provider, config, announcement]) }, [web3Provider, web3ProviderInfo, config, announcement])
return <AnnouncementBanner text={text} action={action} /> return <AnnouncementBanner text={text} action={action} />
} }

View File

@ -42,7 +42,7 @@
justify-content: space-between; justify-content: space-between;
} }
.actions span { .walletLogoWrap {
display: block; display: block;
} }
@ -84,3 +84,7 @@
.walletInfo button { .walletInfo button {
margin-top: calc(var(--spacer) / 5) !important; margin-top: calc(var(--spacer) / 5) !important;
} }
.addToken {
margin-left: 0.3rem;
}

View File

@ -1,33 +1,29 @@
import React, { ReactElement, useEffect, useState } from 'react' import React, { ReactElement, useEffect, useState } from 'react'
import Button from '../../atoms/Button'
import styles from './Details.module.css'
import { useOcean } from '../../../providers/Ocean'
import Web3Feedback from './Feedback'
import { getProviderInfo, IProviderInfo } from 'web3modal'
import Conversion from '../../atoms/Price/Conversion'
import { formatCurrency } from '@coingecko/cryptoformat' import { formatCurrency } from '@coingecko/cryptoformat'
import { useOcean } from '../../../providers/Ocean'
import { useUserPreferences } from '../../../providers/UserPreferences' import { useUserPreferences } from '../../../providers/UserPreferences'
import Button from '../../atoms/Button'
import AddToken from '../../atoms/AddToken'
import Conversion from '../../atoms/Price/Conversion'
import { useWeb3 } from '../../../providers/Web3' import { useWeb3 } from '../../../providers/Web3'
import { addOceanToWallet } from '../../../utils/web3'
import { Logger } from '@oceanprotocol/lib' import Web3Feedback from './Feedback'
import styles from './Details.module.css'
export default function Details(): ReactElement { export default function Details(): ReactElement {
const { web3Provider, connect, logout, networkData } = useWeb3() const {
web3Provider,
web3ProviderInfo,
connect,
logout,
networkData
} = useWeb3()
const { balance, config } = useOcean() const { balance, config } = useOcean()
const { locale } = useUserPreferences() const { locale } = useUserPreferences()
const [providerInfo, setProviderInfo] = useState<IProviderInfo>()
const [mainCurrency, setMainCurrency] = useState<string>() const [mainCurrency, setMainCurrency] = useState<string>()
// const [portisNetwork, setPortisNetwork] = useState<string>() // const [portisNetwork, setPortisNetwork] = useState<string>()
// Workaround cause getInjectedProviderName() always returns `MetaMask`
// https://github.com/oceanprotocol/market/issues/332
useEffect(() => {
if (!web3Provider) return
const providerInfo = getProviderInfo(web3Provider)
setProviderInfo(providerInfo)
}, [web3Provider])
useEffect(() => { useEffect(() => {
if (!networkData) return if (!networkData) return
@ -61,11 +57,11 @@ export default function Details(): ReactElement {
<li className={styles.actions}> <li className={styles.actions}>
<div title="Connected provider" className={styles.walletInfo}> <div title="Connected provider" className={styles.walletInfo}>
<span> <span className={styles.walletLogoWrap}>
<img className={styles.walletLogo} src={providerInfo?.logo} /> <img className={styles.walletLogo} src={web3ProviderInfo?.logo} />
{providerInfo?.name} {web3ProviderInfo?.name}
</span> </span>
{/* {providerInfo?.name === 'Portis' && ( {/* {web3ProviderInfo?.name === 'Portis' && (
<InputElement <InputElement
name="network" name="network"
type="select" type="select"
@ -75,20 +71,17 @@ export default function Details(): ReactElement {
onChange={handlePortisNetworkChange} onChange={handlePortisNetworkChange}
/> />
)} */} )} */}
{providerInfo?.name === 'MetaMask' && ( {web3ProviderInfo?.name === 'MetaMask' && (
<Button <AddToken
style="text" address={config.oceanTokenAddress}
size="small" symbol={config.oceanTokenSymbol}
onClick={() => { logo="https://raw.githubusercontent.com/oceanprotocol/art/main/logo/token.png"
addOceanToWallet(config, web3Provider) className={styles.addToken}
}} />
>
{`Add ${config.oceanTokenSymbol}`}
</Button>
)} )}
</div> </div>
<p> <p>
{providerInfo?.name === 'Portis' && ( {web3ProviderInfo?.name === 'Portis' && (
<Button <Button
style="text" style="text"
size="small" size="small"

View File

@ -27,6 +27,11 @@
margin-right: calc(var(--spacer) / 4); margin-right: calc(var(--spacer) / 4);
} }
.datatoken {
white-space: pre;
margin-right: calc(var(--spacer) / 3);
}
.byline { .byline {
font-size: var(--font-size-small); font-size: var(--font-size-small);
} }
@ -34,3 +39,13 @@
.updated { .updated {
font-size: var(--font-size-mini); font-size: var(--font-size-mini);
} }
.addWrap {
padding-left: calc(var(--spacer) / 5);
border-left: 1px solid var(--border-color);
display: inline-block;
}
.add {
font-size: var(--font-size-mini);
}

View File

@ -3,13 +3,14 @@ import { useAsset } from '../../../providers/Asset'
import { useWeb3 } from '../../../providers/Web3' import { useWeb3 } from '../../../providers/Web3'
import ExplorerLink from '../../atoms/ExplorerLink' import ExplorerLink from '../../atoms/ExplorerLink'
import Publisher from '../../atoms/Publisher' import Publisher from '../../atoms/Publisher'
import AddToken from '../../atoms/AddToken'
import Time from '../../atoms/Time' import Time from '../../atoms/Time'
import styles from './MetaMain.module.css'
import AssetType from '../../atoms/AssetType' import AssetType from '../../atoms/AssetType'
import styles from './MetaMain.module.css'
export default function MetaMain(): ReactElement { export default function MetaMain(): ReactElement {
const { ddo, owner, type } = useAsset() const { ddo, owner, type } = useAsset()
const { networkId } = useWeb3() const { networkId, web3ProviderInfo } = useWeb3()
const isCompute = Boolean(ddo?.findServiceByType('compute')) const isCompute = Boolean(ddo?.findServiceByType('compute'))
const accessType = isCompute ? 'compute' : 'access' const accessType = isCompute ? 'compute' : 'access'
@ -22,6 +23,7 @@ export default function MetaMain(): ReactElement {
className={styles.assetType} className={styles.assetType}
/> />
<ExplorerLink <ExplorerLink
className={styles.datatoken}
networkId={networkId} networkId={networkId}
path={ path={
networkId === 137 || networkId === 1287 networkId === 137 || networkId === 1287
@ -31,6 +33,19 @@ export default function MetaMain(): ReactElement {
> >
{`${ddo?.dataTokenInfo.name}${ddo?.dataTokenInfo.symbol}`} {`${ddo?.dataTokenInfo.name}${ddo?.dataTokenInfo.symbol}`}
</ExplorerLink> </ExplorerLink>
{web3ProviderInfo?.name === 'MetaMask' && (
<span className={styles.addWrap}>
<AddToken
address={ddo?.dataTokenInfo.address}
symbol={ddo?.dataTokenInfo.symbol}
logo="https://raw.githubusercontent.com/oceanprotocol/art/main/logo/datatoken.png"
text={`Add ${ddo?.dataTokenInfo.symbol} to wallet`}
className={styles.add}
minimal
/>
</span>
)}
</header> </header>
<div className={styles.byline}> <div className={styles.byline}>

View File

@ -8,7 +8,7 @@ import React, {
useCallback useCallback
} from 'react' } from 'react'
import Web3 from 'web3' import Web3 from 'web3'
import Web3Modal from 'web3modal' import Web3Modal, { getProviderInfo, IProviderInfo } from 'web3modal'
import { infuraProjectId as infuraId, portisId } from '../../app.config' import { infuraProjectId as infuraId, portisId } from '../../app.config'
import WalletConnectProvider from '@walletconnect/web3-provider' import WalletConnectProvider from '@walletconnect/web3-provider'
import { Logger } from '@oceanprotocol/lib' import { Logger } from '@oceanprotocol/lib'
@ -24,6 +24,7 @@ interface Web3ProviderValue {
web3: Web3 web3: Web3
web3Provider: any web3Provider: any
web3Modal: Web3Modal web3Modal: Web3Modal
web3ProviderInfo: IProviderInfo
accountId: string accountId: string
networkId: number networkId: number
networkDisplayName: string networkDisplayName: string
@ -106,6 +107,7 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
const [web3, setWeb3] = useState<Web3>() const [web3, setWeb3] = useState<Web3>()
const [web3Provider, setWeb3Provider] = useState<any>() const [web3Provider, setWeb3Provider] = useState<any>()
const [web3Modal, setWeb3Modal] = useState<Web3Modal>() const [web3Modal, setWeb3Modal] = useState<Web3Modal>()
const [web3ProviderInfo, setWeb3ProviderInfo] = useState<IProviderInfo>()
const [networkId, setNetworkId] = useState<number>() const [networkId, setNetworkId] = useState<number>()
const [networkDisplayName, setNetworkDisplayName] = useState<string>() const [networkDisplayName, setNetworkDisplayName] = useState<string>()
const [networkData, setNetworkData] = useState<EthereumListsChain>() const [networkData, setNetworkData] = useState<EthereumListsChain>()
@ -209,6 +211,18 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
getBlock() getBlock()
}, [web3, networkId]) }, [web3, networkId])
// -----------------------------------
// Get and set web3 provider info
// -----------------------------------
// Workaround cause getInjectedProviderName() always returns `MetaMask`
// https://github.com/oceanprotocol/market/issues/332
useEffect(() => {
if (!web3Provider) return
const providerInfo = getProviderInfo(web3Provider)
setWeb3ProviderInfo(providerInfo)
}, [web3Provider])
// ----------------------------------- // -----------------------------------
// Logout helper // Logout helper
// ----------------------------------- // -----------------------------------
@ -255,6 +269,7 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
web3, web3,
web3Provider, web3Provider,
web3Modal, web3Modal,
web3ProviderInfo,
accountId, accountId,
networkId, networkId,
networkDisplayName, networkDisplayName,

View File

@ -1,4 +1,4 @@
import { Logger, ConfigHelperConfig } from '@oceanprotocol/lib' import { Logger } from '@oceanprotocol/lib'
export interface EthereumListsChain { export interface EthereumListsChain {
name: string name: string
@ -79,32 +79,33 @@ export function addCustomNetwork(
) )
} }
export function addOceanToWallet( export async function addTokenToWallet(
config: ConfigHelperConfig, web3Provider: any,
web3Provider: any address: string,
): void { symbol: string,
logo?: string
): Promise<void> {
const image =
logo ||
'https://raw.githubusercontent.com/oceanprotocol/art/main/logo/token.png'
const tokenMetadata = { const tokenMetadata = {
type: 'ERC20', type: 'ERC20',
options: { options: { address, symbol, image, decimals: 18 }
address: config.oceanTokenAddress,
symbol: config.oceanTokenSymbol,
decimals: 18,
image:
'https://raw.githubusercontent.com/oceanprotocol/art/main/logo/token.png'
}
} }
web3Provider.sendAsync( web3Provider.sendAsync(
{ {
method: 'wallet_watchAsset', method: 'wallet_watchAsset',
params: tokenMetadata, params: tokenMetadata,
id: Math.round(Math.random() * 100000) id: Math.round(Math.random() * 100000)
}, },
(err: string, added: any) => { (err: { code: number; message: string }, added: any) => {
if (err || 'error' in added) { if (err || 'error' in added) {
Logger.error( Logger.error(
`Couldn't add ${tokenMetadata.options.symbol} (${ `Couldn't add ${tokenMetadata.options.symbol} (${
tokenMetadata.options.address tokenMetadata.options.address
}) to MetaMask, error: ${err || added.error}` }) to MetaMask, error: ${err.message || added.error}`
) )
} else { } else {
Logger.log( Logger.log(