diff --git a/_redirects b/_redirects
index 3b85ef2de..db198413a 100644
--- a/_redirects
+++ b/_redirects
@@ -1 +1,2 @@
-/asset/* /asset/index.html 200
\ No newline at end of file
+/asset/* /asset/index.html 200
+/profile/* /profile/index.html 200
\ No newline at end of file
diff --git a/content/pages/history.json b/content/pages/history.json
index 57df3eb16..8dfbf2a8a 100644
--- a/content/pages/history.json
+++ b/content/pages/history.json
@@ -1,6 +1,4 @@
{
- "title": "History",
- "description": "Find the data sets and jobs that you previously accessed.",
"compute": {
"storage": "Results are stored for 30 days."
}
diff --git a/content/pages/profile.json b/content/pages/profile.json
new file mode 100644
index 000000000..b1d2c8362
--- /dev/null
+++ b/content/pages/profile.json
@@ -0,0 +1,4 @@
+{
+ "title": "Account",
+ "description": "Find the data sets and jobs that you previously accessed."
+}
diff --git a/content/site.json b/content/site.json
index 6b6556de2..7cef28e78 100644
--- a/content/site.json
+++ b/content/site.json
@@ -12,8 +12,8 @@
"link": "/publish"
},
{
- "name": "History",
- "link": "/history"
+ "name": "Profile",
+ "link": "/profile"
}
],
"warning": {
diff --git a/gatsby-node.js b/gatsby-node.js
index 86b1e751d..cb611c715 100644
--- a/gatsby-node.js
+++ b/gatsby-node.js
@@ -33,13 +33,16 @@ exports.onCreatePage = async ({ page, actions }) => {
const { createPage } = actions
// page.matchPath is a special key that's used for matching pages
// only on the client.
- const handleClientSideOnly = page.path.match(/^\/asset/)
+ const handleClientSideOnlyAsset = page.path.match(/^\/asset/)
+ const handleClientSideOnlyAccount = page.path.match(/^\/profile/)
- if (handleClientSideOnly) {
+ if (handleClientSideOnlyAsset) {
page.matchPath = '/asset/*'
-
// Update the page.
createPage(page)
+ } else if (handleClientSideOnlyAccount) {
+ page.matchPath = '/profile/*'
+ createPage(page)
}
}
diff --git a/package-lock.json b/package-lock.json
index 4574eb4f3..153f41abd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -55,6 +55,7 @@
"query-string": "^7.0.0",
"react": "^17.0.2",
"react-chartjs-2": "^2.11.2",
+ "react-clipboard.js": "^2.0.16",
"react-data-table-component": "^6.11.7",
"react-dom": "^17.0.2",
"react-dotdotdot": "^1.3.1",
@@ -85,7 +86,6 @@
"@testing-library/jest-dom": "^5.12.0",
"@testing-library/react": "^11.2.7",
"@types/chart.js": "^2.9.32",
- "@types/classnames": "^2.3.1",
"@types/jest": "^26.0.23",
"@types/loadable__component": "^5.13.1",
"@types/lodash.debounce": "^4.0.3",
@@ -10514,14 +10514,13 @@
"moment": "^2.10.2"
}
},
- "node_modules/@types/classnames": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.3.1.tgz",
- "integrity": "sha512-zeOWb0JGBoVmlQoznvqXbE0tEC/HONsnoUNH19Hc96NFsTAwTXbTqb8FMYkru1F/iqp7a18Ws3nWJvtA1sHD1A==",
- "deprecated": "This is a stub types definition. classnames provides its own type definitions, so you do not need this installed.",
- "dev": true,
+ "node_modules/@types/clipboard": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/clipboard/-/clipboard-2.0.7.tgz",
+ "integrity": "sha512-VwVFUHlneOsWfv/GaaY7Kwk4XasDqkAlyFQtsHxnOw0yyBYWTrlEXtmb9RtC+VFBCdtuOeIXECmELNd5RrKp/g==",
+ "deprecated": "This is a stub types definition. clipboard provides its own type definitions, so you do not need this installed.",
"dependencies": {
- "classnames": "*"
+ "clipboard": "*"
}
},
"node_modules/@types/common-tags": {
@@ -16998,6 +16997,16 @@
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
"integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw=="
},
+ "node_modules/clipboard": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz",
+ "integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==",
+ "dependencies": {
+ "good-listener": "^1.2.2",
+ "select": "^1.1.2",
+ "tiny-emitter": "^2.0.0"
+ }
+ },
"node_modules/clipboardy": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
@@ -19004,6 +19013,11 @@
"node": ">=0.4.0"
}
},
+ "node_modules/delegate": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+ "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+ },
"node_modules/delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -28778,6 +28792,14 @@
"node": ">= 0.10"
}
},
+ "node_modules/good-listener": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+ "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
+ "dependencies": {
+ "delegate": "^3.1.2"
+ }
+ },
"node_modules/google-protobuf": {
"version": "3.17.3",
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.17.3.tgz",
@@ -44904,6 +44926,20 @@
"react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
+ "node_modules/react-clipboard.js": {
+ "version": "2.0.16",
+ "resolved": "https://registry.npmjs.org/react-clipboard.js/-/react-clipboard.js-2.0.16.tgz",
+ "integrity": "sha512-COwmnbrRbl8y4f/SjtonnJTeBRD03YzsHBL5on8iL/uyjERsMkKC7djtfmns7iRAbzadn/84MdpaqaQ3ITP47g==",
+ "dependencies": {
+ "@types/clipboard": "^2.0.1",
+ "clipboard": "^2.0.0",
+ "prop-types": "^15.5.0"
+ },
+ "peerDependencies": {
+ "react": ">=15.5.0",
+ "react-dom": ">=15.0.0"
+ }
+ },
"node_modules/react-data-table-component": {
"version": "6.11.7",
"resolved": "https://registry.npmjs.org/react-data-table-component/-/react-data-table-component-6.11.7.tgz",
@@ -47656,6 +47692,11 @@
"seek-table": "bin/seek-bzip-table"
}
},
+ "node_modules/select": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+ "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
+ },
"node_modules/select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -51465,6 +51506,11 @@
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
},
+ "node_modules/tiny-emitter": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+ },
"node_modules/tiny-queue": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/tiny-queue/-/tiny-queue-0.2.1.tgz",
@@ -66963,13 +67009,12 @@
"moment": "^2.10.2"
}
},
- "@types/classnames": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.3.1.tgz",
- "integrity": "sha512-zeOWb0JGBoVmlQoznvqXbE0tEC/HONsnoUNH19Hc96NFsTAwTXbTqb8FMYkru1F/iqp7a18Ws3nWJvtA1sHD1A==",
- "dev": true,
+ "@types/clipboard": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/clipboard/-/clipboard-2.0.7.tgz",
+ "integrity": "sha512-VwVFUHlneOsWfv/GaaY7Kwk4XasDqkAlyFQtsHxnOw0yyBYWTrlEXtmb9RtC+VFBCdtuOeIXECmELNd5RrKp/g==",
"requires": {
- "classnames": "*"
+ "clipboard": "*"
}
},
"@types/common-tags": {
@@ -72373,6 +72418,16 @@
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
"integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw=="
},
+ "clipboard": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz",
+ "integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==",
+ "requires": {
+ "good-listener": "^1.2.2",
+ "select": "^1.1.2",
+ "tiny-emitter": "^2.0.0"
+ }
+ },
"clipboardy": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
@@ -73977,6 +74032,11 @@
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
+ "delegate": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+ "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+ },
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -81674,6 +81734,14 @@
"minimatch": "~3.0.2"
}
},
+ "good-listener": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+ "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
+ "requires": {
+ "delegate": "^3.1.2"
+ }
+ },
"google-protobuf": {
"version": "3.17.3",
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.17.3.tgz",
@@ -94695,6 +94763,16 @@
"prop-types": "^15.7.2"
}
},
+ "react-clipboard.js": {
+ "version": "2.0.16",
+ "resolved": "https://registry.npmjs.org/react-clipboard.js/-/react-clipboard.js-2.0.16.tgz",
+ "integrity": "sha512-COwmnbrRbl8y4f/SjtonnJTeBRD03YzsHBL5on8iL/uyjERsMkKC7djtfmns7iRAbzadn/84MdpaqaQ3ITP47g==",
+ "requires": {
+ "@types/clipboard": "^2.0.1",
+ "clipboard": "^2.0.0",
+ "prop-types": "^15.5.0"
+ }
+ },
"react-data-table-component": {
"version": "6.11.7",
"resolved": "https://registry.npmjs.org/react-data-table-component/-/react-data-table-component-6.11.7.tgz",
@@ -96908,6 +96986,11 @@
"commander": "^2.8.1"
}
},
+ "select": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+ "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
+ },
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -100004,6 +100087,11 @@
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
},
+ "tiny-emitter": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+ },
"tiny-queue": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/tiny-queue/-/tiny-queue-0.2.1.tgz",
diff --git a/package.json b/package.json
index 00d7cac9c..318ef028c 100644
--- a/package.json
+++ b/package.json
@@ -70,6 +70,7 @@
"query-string": "^7.0.0",
"react": "^17.0.2",
"react-chartjs-2": "^2.11.2",
+ "react-clipboard.js": "^2.0.16",
"react-data-table-component": "^6.11.7",
"react-dom": "^17.0.2",
"react-dotdotdot": "^1.3.1",
@@ -100,7 +101,6 @@
"@testing-library/jest-dom": "^5.12.0",
"@testing-library/react": "^11.2.7",
"@types/chart.js": "^2.9.32",
- "@types/classnames": "^2.3.1",
"@types/jest": "^26.0.23",
"@types/loadable__component": "^5.13.1",
"@types/lodash.debounce": "^4.0.3",
diff --git a/src/components/atoms/Blockies.module.css b/src/components/atoms/Blockies.module.css
new file mode 100644
index 000000000..928123b18
--- /dev/null
+++ b/src/components/atoms/Blockies.module.css
@@ -0,0 +1,8 @@
+.blockies {
+ width: var(--font-size-large);
+ height: var(--font-size-large);
+ border-radius: 50%;
+ overflow: hidden;
+ display: inline-block;
+ vertical-align: middle;
+}
diff --git a/src/components/atoms/Blockies.tsx b/src/components/atoms/Blockies.tsx
new file mode 100644
index 000000000..4a02becbe
--- /dev/null
+++ b/src/components/atoms/Blockies.tsx
@@ -0,0 +1,23 @@
+import { toDataUrl } from 'ethereum-blockies'
+import React, { ReactElement } from 'react'
+import styles from './Blockies.module.css'
+
+export default function Blockies({
+ accountId,
+ className
+}: {
+ accountId: string
+ className?: string
+}): ReactElement {
+ if (!accountId) return null
+ const blockies = toDataUrl(accountId)
+
+ return (
+
+ )
+}
diff --git a/src/components/atoms/Copy.module.css b/src/components/atoms/Copy.module.css
new file mode 100644
index 000000000..4ba29b771
--- /dev/null
+++ b/src/components/atoms/Copy.module.css
@@ -0,0 +1,35 @@
+.button {
+ display: inline-block;
+ margin: 0;
+ border: 0;
+ box-shadow: none;
+ background: none;
+ padding: 0;
+ cursor: pointer;
+ position: relative;
+}
+
+.icon {
+ fill: var(--color-secondary);
+ transition: 0.15s ease-out;
+ width: 10px;
+ height: 10px;
+}
+
+.button:hover .icon {
+ fill: var(--font-color-text);
+}
+
+.copied .icon,
+.button.copied:hover .icon {
+ fill: var(--brand-alert-green);
+}
+
+.copied::after {
+ content: 'Copied!';
+ position: absolute;
+ top: -150%;
+ left: -140%;
+ font-size: var(--font-size-mini);
+ color: var(--brand-alert-green);
+}
diff --git a/src/components/atoms/Copy.tsx b/src/components/atoms/Copy.tsx
new file mode 100644
index 000000000..d0693963f
--- /dev/null
+++ b/src/components/atoms/Copy.tsx
@@ -0,0 +1,33 @@
+import React, { ReactElement, useEffect, useState } from 'react'
+import loadable from '@loadable/component'
+import styles from './Copy.module.css'
+import { ReactComponent as IconCopy } from '../../images/copy.svg'
+
+// lazy load when needed only, as library is a bit big
+const Clipboard = loadable(() => import('react-clipboard.js'))
+
+export default function Copy({ text }: { text: string }): ReactElement {
+ const [isCopied, setIsCopied] = useState(false)
+
+ // Clear copy success style after 5 sec.
+ useEffect(() => {
+ if (!isCopied) return
+
+ const timeout = setTimeout(() => {
+ setIsCopied(false)
+ }, 5000)
+
+ return () => clearTimeout(timeout)
+ }, [isCopied])
+
+ return (
+ setIsCopied(true)}
+ className={`${styles.button} ${isCopied ? styles.copied : ''}`}
+ >
+
+
+ )
+}
diff --git a/src/components/atoms/Publisher/ProfileDetails.module.css b/src/components/atoms/Publisher/ProfileDetails.module.css
deleted file mode 100644
index a0e85bea5..000000000
--- a/src/components/atoms/Publisher/ProfileDetails.module.css
+++ /dev/null
@@ -1,72 +0,0 @@
-.profile {
- background: var(--background-highlight);
- border-radius: var(--border-radius);
- padding: calc(var(--spacer) / 2);
- margin-bottom: calc(var(--spacer) / 4);
-}
-
-@media (min-width: 40rem) {
- .profile {
- margin: calc(var(--spacer) / 8);
- margin-bottom: calc(var(--spacer) / 4);
- }
-}
-
-.profile p {
- margin-bottom: calc(var(--spacer) / 4);
-}
-
-.profile code {
- padding: 0;
- color: var(--color-secondary);
- font-size: var(--font-size-mini);
- overflow-wrap: break-word;
- word-wrap: break-word;
- word-break: break-word;
-}
-
-.header {
- margin-bottom: calc(var(--spacer) / 4);
- text-align: center;
-}
-
-.header::after {
- content: '';
- display: block;
- margin: calc(var(--spacer) / 2) auto;
- width: 20%;
- height: 2px;
- background: var(--border-color);
-}
-
-.image {
- width: 48px;
- height: 48px;
- border-radius: 50%;
- overflow: hidden;
- display: inline-block;
- margin-bottom: calc(var(--spacer) / 4);
- border: 1px solid var(--border-color);
- box-shadow: 0 6px 17px 0 var(--box-shadow-color);
-}
-
-.title {
- font-size: var(--font-size-base);
- margin-bottom: 0;
-}
-
-.description {
- font-size: var(--font-size-small);
-}
-
-.profile p:last-child {
- margin-bottom: 0;
-}
-
-.meta {
- color: var(--color-secondary);
- font-size: var(--font-size-mini);
- text-align: right;
- margin-left: calc(var(--spacer) / 4);
- margin-right: calc(var(--spacer) / 4);
-}
diff --git a/src/components/atoms/Publisher/ProfileDetails.tsx b/src/components/atoms/Publisher/ProfileDetails.tsx
deleted file mode 100644
index 7f4be381b..000000000
--- a/src/components/atoms/Publisher/ProfileDetails.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import React, { ReactElement } from 'react'
-import styles from './ProfileDetails.module.css'
-import { Profile } from '../../../models/Profile'
-import ExplorerLink from '../ExplorerLink'
-import PublisherLinks from './PublisherLinks'
-
-export default function ProfileDetails({
- profile,
- networkId,
- account
-}: {
- profile: Profile
- networkId: number
- account: string
-}): ReactElement {
- return (
- <>
-
-
- {profile?.image && (
-
-
-
- )}
-
- {profile?.emoji} {profile?.name}
-
-
-
- {account}
-
-
-
- {profile?.description && (
-
{profile?.description}
- )}
-
-
-
- >
- )
-}
diff --git a/src/components/atoms/Publisher/index.module.css b/src/components/atoms/Publisher/index.module.css
index 71a0287b8..93592f599 100644
--- a/src/components/atoms/Publisher/index.module.css
+++ b/src/components/atoms/Publisher/index.module.css
@@ -8,40 +8,9 @@
}
}
-.links {
- display: inline;
-}
-
-.links a,
-.links span {
- margin-left: calc(var(--spacer) / 3);
- font-size: var(--font-size-mini);
-}
-
-.links a:first-child,
-.links span:first-child {
- margin-left: 0;
-}
-
-.links a:hover,
-.links a:focus {
- color: var(--brand-pink);
-}
-
.linksExternal {
width: 6px;
height: 6px;
display: inline-block;
fill: var(--color-secondary);
}
-
-.detailsTrigger {
- cursor: help;
-}
-
-.detailsTrigger svg {
- width: 10px;
- height: 10px;
- position: relative;
- bottom: -1px;
-}
diff --git a/src/components/atoms/Publisher/index.tsx b/src/components/atoms/Publisher/index.tsx
index 8fac6942c..734fd1e02 100644
--- a/src/components/atoms/Publisher/index.tsx
+++ b/src/components/atoms/Publisher/index.tsx
@@ -1,17 +1,14 @@
import React, { ReactElement, useEffect, useState } from 'react'
import styles from './index.module.css'
import classNames from 'classnames/bind'
-import Tooltip from '../Tooltip'
import { Profile } from '../../../models/Profile'
import { Link } from 'gatsby'
import get3BoxProfile from '../../../utils/profile'
-import ExplorerLink from '../ExplorerLink'
import { accountTruncate } from '../../../utils/web3'
import axios from 'axios'
-import { ReactComponent as Info } from '../../../images/info.svg'
-import ProfileDetails from './ProfileDetails'
import Add from './Add'
import { useWeb3 } from '../../../providers/Web3'
+import { getEnsName } from '../../../utils/ens'
const cx = classNames.bind(styles)
@@ -24,27 +21,34 @@ export default function Publisher({
minimal?: boolean
className?: string
}): ReactElement {
- const { networkId, accountId } = useWeb3()
+ const { accountId } = useWeb3()
const [profile, setProfile] = useState()
- const [name, setName] = useState()
+ const [name, setName] = useState(accountTruncate(account))
+ const [accountEns, setAccountEns] = useState()
const showAdd = account === accountId && !profile
useEffect(() => {
if (!account) return
- setName(accountTruncate(account))
const source = axios.CancelToken.source()
- async function get3Box() {
+ async function getExternalName() {
+ // ENS
+ const accountEns = await getEnsName(account)
+ if (accountEns) {
+ setAccountEns(accountEns)
+ setName(accountEns)
+ }
+
+ // 3box
const profile = await get3BoxProfile(account, source.token)
if (!profile) return
-
setProfile(profile)
const { name, emoji } = profile
name && setName(`${emoji || ''} ${name}`)
}
- get3Box()
+ getExternalName()
return () => {
source.cancel()
@@ -63,34 +67,12 @@ export default function Publisher({
) : (
<>
{name}
-
- {' — '}
- {profile && (
-
- }
- >
-
- Profile
-
-
- )}
- {showAdd &&
}
-
- Explorer
-
-
+ {showAdd && }
>
)}
diff --git a/src/components/atoms/Tabs.module.css b/src/components/atoms/Tabs.module.css
index 3d76fc0d6..f138f9458 100644
--- a/src/components/atoms/Tabs.module.css
+++ b/src/components/atoms/Tabs.module.css
@@ -1,8 +1,7 @@
.tabList {
text-align: center;
border-bottom: 1px solid var(--border-color);
- padding-top: calc(var(--spacer) / 2);
- padding-bottom: calc(var(--spacer) / 2);
+ padding: calc(var(--spacer) / 2);
}
.tab {
@@ -36,5 +35,11 @@
}
.tabContent {
- padding: var(--spacer);
+ padding: calc(var(--spacer) / 2);
+}
+
+@media (min-width: 40rem) {
+ .tabContent {
+ padding: var(--spacer);
+ }
}
diff --git a/src/components/atoms/Tooltip.module.css b/src/components/atoms/Tooltip.module.css
index 97bcbda11..131a7cea5 100644
--- a/src/components/atoms/Tooltip.module.css
+++ b/src/components/atoms/Tooltip.module.css
@@ -9,6 +9,10 @@
font-size: var(--font-size-small);
}
+.content p {
+ margin: 0;
+}
+
.icon {
width: 1em;
height: 1em;
diff --git a/src/components/atoms/Tooltip.tsx b/src/components/atoms/Tooltip.tsx
index 1719ff0a0..08a5039a2 100644
--- a/src/components/atoms/Tooltip.tsx
+++ b/src/components/atoms/Tooltip.tsx
@@ -5,6 +5,7 @@ import { useSpring, animated } from 'react-spring'
import styles from './Tooltip.module.css'
import { ReactComponent as Info } from '../../images/info.svg'
import { Placement } from 'tippy.js'
+import Markdown from './Markdown'
const cx = classNames.bind(styles)
diff --git a/src/components/molecules/AssetListTitle.tsx b/src/components/molecules/AssetListTitle.tsx
index 9fb6a7535..fe6c67bfe 100644
--- a/src/components/molecules/AssetListTitle.tsx
+++ b/src/components/molecules/AssetListTitle.tsx
@@ -1,5 +1,4 @@
import { DDO } from '@oceanprotocol/lib'
-import { useOcean } from '../../providers/Ocean'
import { Link } from 'gatsby'
import React, { ReactElement, useEffect, useState } from 'react'
import { getAssetsNames } from '../../utils/aquarius'
@@ -43,7 +42,7 @@ export default function AssetListTitle({
return (
- {assetTitle}
+ {assetTitle}
)
}
diff --git a/src/components/molecules/AssetTeaser.tsx b/src/components/molecules/AssetTeaser.tsx
index 2543e1ba1..b1b249642 100644
--- a/src/components/molecules/AssetTeaser.tsx
+++ b/src/components/molecules/AssetTeaser.tsx
@@ -13,11 +13,13 @@ import { BestPrice } from '../../models/BestPrice'
declare type AssetTeaserProps = {
ddo: DDO
price: BestPrice
+ noPublisher?: boolean
}
const AssetTeaser: React.FC = ({
ddo,
- price
+ price,
+ noPublisher
}: AssetTeaserProps) => {
const { attributes } = ddo.findServiceByType('metadata')
const { name, type } = attributes.main
@@ -34,7 +36,9 @@ const AssetTeaser: React.FC = ({
{name}
-
+ {!noPublisher && (
+
+ )}
{
- if (!appConfig.metadataCacheUri || bookmarks === []) return
+ if (!appConfig?.metadataCacheUri || bookmarks === []) return
const source = axios.CancelToken.source()
@@ -121,7 +95,7 @@ export default function Bookmarks(): ReactElement {
return () => {
source.cancel()
}
- }, [bookmarks, chainIds])
+ }, [bookmarks, chainIds, appConfig?.metadataCacheUri])
return (
+
+ {icon && icon}
+ {value}
+
+
+ {label}{' '}
+ {tooltip && (
+ }
+ className={styles.tooltip}
+ />
+ )}
+
+
+ )
+}
diff --git a/src/components/molecules/PoolTransactions/Title.tsx b/src/components/molecules/PoolTransactions/Title.tsx
index 5305c34f7..bd9fcf5a7 100644
--- a/src/components/molecules/PoolTransactions/Title.tsx
+++ b/src/components/molecules/PoolTransactions/Title.tsx
@@ -1,28 +1,23 @@
import React, { useState, useEffect, ReactElement } from 'react'
-import { Datatoken, PoolTransaction } from '.'
+import { PoolTransaction } from '.'
import { useUserPreferences } from '../../../providers/UserPreferences'
import ExplorerLink from '../../atoms/ExplorerLink'
import { formatPrice } from '../../atoms/Price/PriceUnit'
import styles from './Title.module.css'
-function getSymbol(tokenId: Datatoken) {
- const symbol = tokenId === null ? 'OCEAN' : tokenId.symbol
- return symbol
-}
-
async function getTitle(row: PoolTransaction, locale: string) {
let title = ''
switch (row.event) {
case 'swap': {
const inToken = row.tokens.filter((x) => x.type === 'in')[0]
- const inTokenSymbol = getSymbol(inToken.poolToken.tokenId)
+ const inTokenSymbol = inToken?.poolToken.symbol
const outToken = row.tokens.filter((x) => x.type === 'out')[0]
- const outTokenSymbol = getSymbol(outToken.poolToken.tokenId)
+ const outTokenSymbol = outToken?.poolToken.symbol
title += `Swap ${formatPrice(
- Math.abs(inToken.value).toString(),
+ Math.abs(inToken?.value).toString(),
locale
)}${inTokenSymbol} for ${formatPrice(
- Math.abs(outToken.value).toString(),
+ Math.abs(outToken?.value).toString(),
locale
)}${outTokenSymbol}`
@@ -34,18 +29,18 @@ async function getTitle(row: PoolTransaction, locale: string) {
x.tokenAddress.toLowerCase() !==
row.poolAddress.datatokenAddress.toLowerCase()
)[0]
- const firstTokenSymbol = await getSymbol(firstToken.poolToken.tokenId)
+ const firstTokenSymbol = firstToken?.poolToken.symbol
const secondToken = row.tokens.filter(
(x) =>
x.tokenAddress.toLowerCase() ===
row.poolAddress.datatokenAddress.toLowerCase()
)[0]
- const secondTokenSymbol = await getSymbol(secondToken.poolToken.tokenId)
+ const secondTokenSymbol = secondToken?.poolToken.symbol
title += `Create pool with ${formatPrice(
- Math.abs(firstToken.value).toString(),
+ Math.abs(firstToken?.value).toString(),
locale
)}${firstTokenSymbol} and ${formatPrice(
- Math.abs(secondToken.value).toString(),
+ Math.abs(secondToken?.value).toString(),
locale
)}${secondTokenSymbol}`
break
@@ -53,7 +48,7 @@ async function getTitle(row: PoolTransaction, locale: string) {
case 'join':
case 'exit': {
for (let i = 0; i < row.tokens.length; i++) {
- const tokenSymbol = await getSymbol(row.tokens[i].poolToken.tokenId)
+ const tokenSymbol = row.tokens[i].poolToken.symbol
if (i > 0) title += '\n'
title += `${row.event === 'join' ? 'Add' : 'Remove'} ${formatPrice(
Math.abs(row.tokens[i].value).toString(),
@@ -73,6 +68,7 @@ export default function Title({ row }: { row: PoolTransaction }): ReactElement {
useEffect(() => {
if (!locale || !row) return
+
async function init() {
const title = await getTitle(row, locale)
setTitle(title)
diff --git a/src/components/molecules/PoolTransactions/index.tsx b/src/components/molecules/PoolTransactions/index.tsx
index 24cc616d2..2ba87f53f 100644
--- a/src/components/molecules/PoolTransactions/index.tsx
+++ b/src/components/molecules/PoolTransactions/index.tsx
@@ -1,4 +1,4 @@
-import React, { ReactElement, useEffect, useState } from 'react'
+import React, { ReactElement, useCallback, useEffect, useState } from 'react'
import Time from '../../atoms/Time'
import Table from '../../atoms/Table'
import AssetTitle from '../AssetListTitle'
@@ -6,14 +6,14 @@ import { useUserPreferences } from '../../../providers/UserPreferences'
import { gql } from 'urql'
import { TransactionHistory_poolTransactions as TransactionHistoryPoolTransactions } from '../../../@types/apollo/TransactionHistory'
import web3 from 'web3'
-import { useWeb3 } from '../../../providers/Web3'
import { fetchDataForMultipleChains } from '../../../utils/subgraph'
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
import NetworkName from '../../atoms/NetworkName'
import { retrieveDDO } from '../../../utils/aquarius'
-import axios from 'axios'
+import axios, { CancelToken } from 'axios'
import Title from './Title'
import styles from './index.module.css'
+import { DDO, Logger } from '@oceanprotocol/lib'
const REFETCH_INTERVAL = 20000
@@ -27,10 +27,12 @@ const txHistoryQueryByPool = gql`
) {
tokens {
poolToken {
- tokenId {
- symbol
- }
+ id
+ symbol
}
+ value
+ type
+ tokenAddress
}
tx
event
@@ -38,11 +40,6 @@ const txHistoryQueryByPool = gql`
poolAddress {
datatokenAddress
}
- tokens {
- value
- type
- tokenAddress
- }
}
}
`
@@ -56,10 +53,12 @@ const txHistoryQuery = gql`
) {
tokens {
poolToken {
- tokenId {
- symbol
- }
+ id
+ symbol
}
+ value
+ type
+ tokenAddress
}
tx
event
@@ -67,11 +66,6 @@ const txHistoryQuery = gql`
poolAddress {
datatokenAddress
}
- tokens {
- value
- type
- tokenAddress
- }
}
}
`
@@ -82,6 +76,7 @@ export interface Datatoken {
export interface PoolTransaction extends TransactionHistoryPoolTransactions {
networkId: number
+ ddo: DDO
}
const columns = [
@@ -94,11 +89,7 @@ const columns = [
{
name: 'Data Set',
selector: function getAssetRow(row: PoolTransaction) {
- const did = web3.utils
- .toChecksumAddress(row.poolAddress.datatokenAddress)
- .replace('0x', 'did:op:')
-
- return
+ return
}
},
{
@@ -130,21 +121,22 @@ const columnsMinimal = [columns[0], columns[3]]
export default function PoolTransactions({
poolAddress,
poolChainId,
- minimal
+ minimal,
+ accountId
}: {
poolAddress?: string
poolChainId?: number[]
minimal?: boolean
+ accountId: string
}): ReactElement {
- const { accountId } = useWeb3()
- const [logs, setLogs] = useState()
- const [isLoading, setIsLoading] = useState(false)
+ const [transactions, setTransactions] = useState()
+ const [isLoading, setIsLoading] = useState(true)
const { chainIds } = useUserPreferences()
const { appConfig } = useSiteMetadata()
const [dataFetchInterval, setDataFetchInterval] = useState()
const [data, setData] = useState()
- async function fetchPoolTransactionData() {
+ const getPoolTransactionData = useCallback(async () => {
const variables = {
user: accountId?.toLowerCase(),
pool: poolAddress?.toLowerCase()
@@ -165,71 +157,94 @@ export default function PoolTransactions({
if (JSON.stringify(data) !== JSON.stringify(transactions)) {
setData(transactions)
}
- }
+ }, [accountId, chainIds, data, poolAddress, poolChainId])
- function refetchPoolTransactions() {
- if (!dataFetchInterval) {
- setDataFetchInterval(
- setInterval(function () {
- fetchPoolTransactionData()
- }, REFETCH_INTERVAL)
+ const getPoolTransactions = useCallback(
+ async (cancelToken: CancelToken) => {
+ if (!data) return
+
+ const poolTransactions: PoolTransaction[] = []
+
+ for (let i = 0; i < data.length; i++) {
+ const { datatokenAddress } = data[i].poolAddress
+ const did = web3.utils
+ .toChecksumAddress(datatokenAddress)
+ .replace('0x', 'did:op:')
+ const ddo = await retrieveDDO(did, cancelToken)
+ ddo &&
+ poolTransactions.push({
+ ...data[i],
+ networkId: ddo?.chainId,
+ ddo
+ })
+ }
+ const sortedTransactions = poolTransactions.sort(
+ (a, b) => b.timestamp - a.timestamp
)
- }
- }
+ setTransactions(sortedTransactions)
+ },
+ [data]
+ )
+ //
+ // Get data, periodically
+ //
useEffect(() => {
+ if (!appConfig?.metadataCacheUri) return
+
+ async function getTransactions() {
+ try {
+ await getPoolTransactionData()
+
+ if (dataFetchInterval) return
+ const interval = setInterval(async () => {
+ await getPoolTransactionData()
+ }, REFETCH_INTERVAL)
+ setDataFetchInterval(interval)
+ } catch (error) {
+ Logger.error('Error fetching pool transactions: ', error.message)
+ }
+ }
+ getTransactions()
+
return () => {
clearInterval(dataFetchInterval)
}
- }, [dataFetchInterval])
+ }, [getPoolTransactionData, dataFetchInterval, appConfig.metadataCacheUri])
+ //
+ // Transform to final transactions
+ //
useEffect(() => {
- if (!appConfig.metadataCacheUri) return
+ const cancelTokenSource = axios.CancelToken.source()
- async function getTransactions() {
- const poolTransactions: PoolTransaction[] = []
- const source = axios.CancelToken.source()
+ async function transformData() {
try {
setIsLoading(true)
-
- if (!data) {
- await fetchPoolTransactionData()
- return
- }
- const poolTransactionsData = data.map((obj) => ({ ...obj }))
-
- for (let i = 0; i < poolTransactionsData.length; i++) {
- const did = web3.utils
- .toChecksumAddress(
- poolTransactionsData[i].poolAddress.datatokenAddress
- )
- .replace('0x', 'did:op:')
- const ddo = await retrieveDDO(did, source.token)
- poolTransactionsData[i].networkId = ddo.chainId
- poolTransactions.push(poolTransactionsData[i])
- }
- const sortedTransactions = poolTransactions.sort(
- (a, b) => b.timestamp - a.timestamp
- )
- setLogs(sortedTransactions)
- refetchPoolTransactions()
+ await getPoolTransactions(cancelTokenSource.token)
} catch (error) {
- console.error('Error fetching pool transactions: ', error.message)
+ Logger.error('Error fetching pool transactions: ', error.message)
} finally {
setIsLoading(false)
}
}
- getTransactions()
- }, [accountId, chainIds, appConfig.metadataCacheUri, poolAddress, data])
+ transformData()
+
+ return () => {
+ cancelTokenSource.cancel()
+ }
+ }, [getPoolTransactions])
return accountId ? (
= 4 : logs?.length >= 9}
+ pagination={
+ minimal ? transactions?.length >= 4 : transactions?.length >= 9
+ }
paginationPerPage={minimal ? 5 : 10}
/>
) : (
diff --git a/src/components/molecules/Wallet/Account.module.css b/src/components/molecules/Wallet/Account.module.css
index 0a4d0d6be..1dbce49f8 100644
--- a/src/components/molecules/Wallet/Account.module.css
+++ b/src/components/molecules/Wallet/Account.module.css
@@ -41,20 +41,12 @@
}
}
-.blockies {
- width: var(--font-size-large);
- height: var(--font-size-large);
- border-radius: 50%;
- overflow: hidden;
- display: inline-block;
- vertical-align: middle;
-}
-
.address {
display: none;
text-transform: none;
border-right: 1px solid var(--border-color);
padding-right: calc(var(--spacer) / 3);
+ padding-left: calc(var(--spacer) / 8);
}
@media screen and (min-width: 60rem) {
diff --git a/src/components/molecules/Wallet/Account.tsx b/src/components/molecules/Wallet/Account.tsx
index 7b3be33fc..e95430306 100644
--- a/src/components/molecules/Wallet/Account.tsx
+++ b/src/components/molecules/Wallet/Account.tsx
@@ -1,29 +1,15 @@
-import { toDataUrl } from 'ethereum-blockies'
import React, { FormEvent } from 'react'
import { ReactComponent as Caret } from '../../../images/caret.svg'
import { accountTruncate } from '../../../utils/web3'
import Loader from '../../atoms/Loader'
import styles from './Account.module.css'
import { useWeb3 } from '../../../providers/Web3'
-
-const Blockies = ({ account }: { account: string | undefined }) => {
- if (!account) return null
- const blockies = toDataUrl(account)
-
- return (
-
- )
-}
+import Blockies from '../../atoms/Blockies'
// Forward ref for Tippy.js
// eslint-disable-next-line
const Account = React.forwardRef((props, ref: any) => {
- const { accountId, web3Modal, connect } = useWeb3()
+ const { accountId, accountEns, web3Modal, connect } = useWeb3()
async function handleActivation(e: FormEvent) {
// prevent accidentially submitting a form the button might be in
@@ -44,9 +30,9 @@ const Account = React.forwardRef((props, ref: any) => {
ref={ref}
onClick={(e) => e.preventDefault()}
>
-
+
- {accountTruncate(accountId)}
+ {accountTruncate(accountEns || accountId)}
diff --git a/src/components/organisms/AssetActions/Compute/index.tsx b/src/components/organisms/AssetActions/Compute/index.tsx
index b3ef7b75e..168127855 100644
--- a/src/components/organisms/AssetActions/Compute/index.tsx
+++ b/src/components/organisms/AssetActions/Compute/index.tsx
@@ -38,11 +38,11 @@ import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelectio
import AlgorithmDatasetsListForCompute from '../../AssetContent/AlgorithmDatasetsListForCompute'
import { getPreviousOrders, getPrice } from '../../../../utils/subgraph'
import AssetActionHistoryTable from '../../AssetActionHistoryTable'
-import ComputeJobs from '../../../pages/History/ComputeJobs'
+import ComputeJobs from '../../../pages/Profile/History/ComputeJobs'
import { BestPrice } from '../../../../models/BestPrice'
const SuccessAction = () => (
-