mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Account metadata header (#776)
* get all neded data for the header from 3box, aqua and subgraph * fix tvl display error * WIP metadata header styling * added more styling for the header * make page title optional so we can remove it on account page * stroke change for svg images and default values * more styling added to the header * fixed linter * added ocean balance to tvl * update styling for statistcs * fixed eror for go to my account from another account page * updated styling for mobile use * wip show more on explorer links and description * properly display read more for explorer links and description * replaced show more with 3box redirect on description * change accounts default picture and check links length before display element * use optional on links * grid cleanup, new number unit, split up stats * rename all the things, more profile header styling * visual hierarchy, improve image loading experience * layout flow & visual tweaks * more description * replaced account route with profile when accesing a profile by the eth address * use account id from url if exists when fetching data * bump @oceanprotocol/art to v3.2.0 * styling, fallbacks, edge case fixes * clean up Publisher atom, link to profile page * fixed issue when switching to my profile from another profile * output accountId, make it copyable, remove stats icons * render tweaks, markup cleanup * add 3box reference * mobile tabs spacing tweaks * text flow and spacing tweaks Co-authored-by: Matthias Kretschmann <m@kretschmann.io>
This commit is contained in:
parent
3112a10930
commit
f8ffcbac75
@ -1,2 +1,2 @@
|
|||||||
/asset/* /asset/index.html 200
|
/asset/* /asset/index.html 200
|
||||||
/account/* /account/index.html 200
|
/profile/* /profile/index.html 200
|
@ -12,8 +12,8 @@
|
|||||||
"link": "/publish"
|
"link": "/publish"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "My Account",
|
"name": "Profile",
|
||||||
"link": "/account"
|
"link": "/profile"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"warning": {
|
"warning": {
|
||||||
|
@ -34,14 +34,14 @@ exports.onCreatePage = async ({ page, actions }) => {
|
|||||||
// page.matchPath is a special key that's used for matching pages
|
// page.matchPath is a special key that's used for matching pages
|
||||||
// only on the client.
|
// only on the client.
|
||||||
const handleClientSideOnlyAsset = page.path.match(/^\/asset/)
|
const handleClientSideOnlyAsset = page.path.match(/^\/asset/)
|
||||||
const handleClientSideOnlyAccount = page.path.match(/^\/account/)
|
const handleClientSideOnlyAccount = page.path.match(/^\/profile/)
|
||||||
|
|
||||||
if (handleClientSideOnlyAsset) {
|
if (handleClientSideOnlyAsset) {
|
||||||
page.matchPath = '/asset/*'
|
page.matchPath = '/asset/*'
|
||||||
// Update the page.
|
// Update the page.
|
||||||
createPage(page)
|
createPage(page)
|
||||||
} else if (handleClientSideOnlyAccount) {
|
} else if (handleClientSideOnlyAccount) {
|
||||||
page.matchPath = '/account/*'
|
page.matchPath = '/profile/*'
|
||||||
createPage(page)
|
createPage(page)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
108
package-lock.json
generated
108
package-lock.json
generated
@ -55,6 +55,7 @@
|
|||||||
"query-string": "^7.0.0",
|
"query-string": "^7.0.0",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-chartjs-2": "^2.11.2",
|
"react-chartjs-2": "^2.11.2",
|
||||||
|
"react-clipboard.js": "^2.0.16",
|
||||||
"react-data-table-component": "^6.11.7",
|
"react-data-table-component": "^6.11.7",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-dotdotdot": "^1.3.1",
|
"react-dotdotdot": "^1.3.1",
|
||||||
@ -10524,6 +10525,15 @@
|
|||||||
"classnames": "*"
|
"classnames": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"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": {
|
||||||
|
"clipboard": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/common-tags": {
|
"node_modules/@types/common-tags": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.0.tgz",
|
||||||
@ -16998,6 +17008,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
|
||||||
"integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw=="
|
"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": {
|
"node_modules/clipboardy": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
|
||||||
@ -19004,6 +19024,11 @@
|
|||||||
"node": ">=0.4.0"
|
"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": {
|
"node_modules/delegates": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||||
@ -28778,6 +28803,14 @@
|
|||||||
"node": ">= 0.10"
|
"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": {
|
"node_modules/google-protobuf": {
|
||||||
"version": "3.17.3",
|
"version": "3.17.3",
|
||||||
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.17.3.tgz",
|
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.17.3.tgz",
|
||||||
@ -44904,6 +44937,20 @@
|
|||||||
"react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
|
"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": {
|
"node_modules/react-data-table-component": {
|
||||||
"version": "6.11.7",
|
"version": "6.11.7",
|
||||||
"resolved": "https://registry.npmjs.org/react-data-table-component/-/react-data-table-component-6.11.7.tgz",
|
"resolved": "https://registry.npmjs.org/react-data-table-component/-/react-data-table-component-6.11.7.tgz",
|
||||||
@ -47656,6 +47703,11 @@
|
|||||||
"seek-table": "bin/seek-bzip-table"
|
"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": {
|
"node_modules/select-hose": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
|
||||||
@ -51465,6 +51517,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
||||||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
|
"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": {
|
"node_modules/tiny-queue": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/tiny-queue/-/tiny-queue-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-queue/-/tiny-queue-0.2.1.tgz",
|
||||||
@ -66972,6 +67029,14 @@
|
|||||||
"classnames": "*"
|
"classnames": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@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": {
|
||||||
|
"clipboard": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/common-tags": {
|
"@types/common-tags": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.0.tgz",
|
||||||
@ -72373,6 +72438,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
|
||||||
"integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw=="
|
"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": {
|
"clipboardy": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
|
||||||
@ -73977,6 +74052,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
|
"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": {
|
"delegates": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||||
@ -81674,6 +81754,14 @@
|
|||||||
"minimatch": "~3.0.2"
|
"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": {
|
"google-protobuf": {
|
||||||
"version": "3.17.3",
|
"version": "3.17.3",
|
||||||
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.17.3.tgz",
|
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.17.3.tgz",
|
||||||
@ -94695,6 +94783,16 @@
|
|||||||
"prop-types": "^15.7.2"
|
"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": {
|
"react-data-table-component": {
|
||||||
"version": "6.11.7",
|
"version": "6.11.7",
|
||||||
"resolved": "https://registry.npmjs.org/react-data-table-component/-/react-data-table-component-6.11.7.tgz",
|
"resolved": "https://registry.npmjs.org/react-data-table-component/-/react-data-table-component-6.11.7.tgz",
|
||||||
@ -96908,6 +97006,11 @@
|
|||||||
"commander": "^2.8.1"
|
"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": {
|
"select-hose": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
|
||||||
@ -100004,6 +100107,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
||||||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
|
"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": {
|
"tiny-queue": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/tiny-queue/-/tiny-queue-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-queue/-/tiny-queue-0.2.1.tgz",
|
||||||
|
@ -70,6 +70,7 @@
|
|||||||
"query-string": "^7.0.0",
|
"query-string": "^7.0.0",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-chartjs-2": "^2.11.2",
|
"react-chartjs-2": "^2.11.2",
|
||||||
|
"react-clipboard.js": "^2.0.16",
|
||||||
"react-data-table-component": "^6.11.7",
|
"react-data-table-component": "^6.11.7",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-dotdotdot": "^1.3.1",
|
"react-dotdotdot": "^1.3.1",
|
||||||
|
35
src/components/atoms/Copy.module.css
Normal file
35
src/components/atoms/Copy.module.css
Normal file
@ -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: -100%;
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
color: var(--brand-alert-green);
|
||||||
|
}
|
33
src/components/atoms/Copy.tsx
Normal file
33
src/components/atoms/Copy.tsx
Normal file
@ -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 (
|
||||||
|
<Clipboard
|
||||||
|
data-clipboard-text={text}
|
||||||
|
button-title="Copy to clipboard"
|
||||||
|
onSuccess={() => setIsCopied(true)}
|
||||||
|
className={`${styles.button} ${isCopied ? styles.copied : ''}`}
|
||||||
|
>
|
||||||
|
<IconCopy className={styles.icon} />
|
||||||
|
</Clipboard>
|
||||||
|
)
|
||||||
|
}
|
@ -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);
|
|
||||||
}
|
|
@ -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 (
|
|
||||||
<>
|
|
||||||
<div className={styles.profile}>
|
|
||||||
<header className={styles.header}>
|
|
||||||
{profile?.image && (
|
|
||||||
<div className={styles.image}>
|
|
||||||
<img src={profile.image} width="48" height="48" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<h3 className={styles.title}>
|
|
||||||
{profile?.emoji} {profile?.name}
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<ExplorerLink networkId={networkId} path={`address/${account}`}>
|
|
||||||
<code>{account}</code>
|
|
||||||
</ExplorerLink>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
{profile?.description && (
|
|
||||||
<p className={styles.description}>{profile?.description}</p>
|
|
||||||
)}
|
|
||||||
<PublisherLinks links={profile?.links} />
|
|
||||||
</div>
|
|
||||||
<div className={styles.meta}>
|
|
||||||
Profile data from{' '}
|
|
||||||
<a
|
|
||||||
href={`https://www.3box.io/${account}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
3Box Hub
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
@ -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 {
|
.linksExternal {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
height: 6px;
|
height: 6px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
fill: var(--color-secondary);
|
fill: var(--color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.detailsTrigger {
|
|
||||||
cursor: help;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detailsTrigger svg {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
position: relative;
|
|
||||||
bottom: -1px;
|
|
||||||
}
|
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import classNames from 'classnames/bind'
|
import classNames from 'classnames/bind'
|
||||||
import Tooltip from '../Tooltip'
|
|
||||||
import { Profile } from '../../../models/Profile'
|
import { Profile } from '../../../models/Profile'
|
||||||
import { Link } from 'gatsby'
|
import { Link } from 'gatsby'
|
||||||
import get3BoxProfile from '../../../utils/profile'
|
import get3BoxProfile from '../../../utils/profile'
|
||||||
import ExplorerLink from '../ExplorerLink'
|
|
||||||
import { accountTruncate } from '../../../utils/web3'
|
import { accountTruncate } from '../../../utils/web3'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { ReactComponent as Info } from '../../../images/info.svg'
|
|
||||||
import ProfileDetails from './ProfileDetails'
|
|
||||||
import Add from './Add'
|
import Add from './Add'
|
||||||
import { useWeb3 } from '../../../providers/Web3'
|
import { useWeb3 } from '../../../providers/Web3'
|
||||||
|
|
||||||
@ -62,35 +58,10 @@ export default function Publisher({
|
|||||||
name
|
name
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Link
|
<Link to={`/profile/${account}`} title="Show profile page.">
|
||||||
to={`/search?sort=created&sortOrder=desc&text=${account}`}
|
|
||||||
title="Show all data sets created by this account."
|
|
||||||
>
|
|
||||||
{name}
|
{name}
|
||||||
</Link>
|
</Link>
|
||||||
<div className={styles.links}>
|
{showAdd && <Add />}
|
||||||
{' — '}
|
|
||||||
{profile && (
|
|
||||||
<Tooltip
|
|
||||||
placement="bottom"
|
|
||||||
content={
|
|
||||||
<ProfileDetails
|
|
||||||
profile={profile}
|
|
||||||
networkId={networkId}
|
|
||||||
account={account}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span className={styles.detailsTrigger}>
|
|
||||||
Profile <Info className={styles.linksExternal} />
|
|
||||||
</span>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
{showAdd && <Add />}
|
|
||||||
<ExplorerLink networkId={networkId} path={`address/${account}`}>
|
|
||||||
Explorer
|
|
||||||
</ExplorerLink>
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
.tabList {
|
.tabList {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--border-color);
|
||||||
padding-top: calc(var(--spacer) / 2);
|
padding: calc(var(--spacer) / 2);
|
||||||
padding-bottom: calc(var(--spacer) / 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab {
|
.tab {
|
||||||
@ -36,5 +35,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tabContent {
|
.tabContent {
|
||||||
padding: var(--spacer);
|
padding: calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 40rem) {
|
||||||
|
.tabContent {
|
||||||
|
padding: var(--spacer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
48
src/components/molecules/NumberUnit.module.css
Normal file
48
src/components/molecules/NumberUnit.module.css
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
.number,
|
||||||
|
.number * {
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
font-size: var(--font-size-h4);
|
||||||
|
color: var(--font-color-heading);
|
||||||
|
}
|
||||||
|
|
||||||
|
.number {
|
||||||
|
white-space: nowrap;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.number.small,
|
||||||
|
.number.small * {
|
||||||
|
font-size: var(--font-size-h5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.number svg {
|
||||||
|
width: var(--font-size-large);
|
||||||
|
height: var(--font-size-large);
|
||||||
|
margin-right: calc(var(--spacer) / 4);
|
||||||
|
stroke: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit {
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
color: var(--color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit a {
|
||||||
|
color: var(--color-secondary);
|
||||||
|
display: block;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border: 0.1rem solid transparent;
|
||||||
|
border-radius: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit a:hover,
|
||||||
|
.unit a:focus {
|
||||||
|
background: var(--brand-white);
|
||||||
|
border: 0.1rem solid var(--brand-pink);
|
||||||
|
}
|
45
src/components/molecules/NumberUnit.tsx
Normal file
45
src/components/molecules/NumberUnit.tsx
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import styles from './NumberUnit.module.css'
|
||||||
|
|
||||||
|
interface NumberInnerProps {
|
||||||
|
label: string
|
||||||
|
value: number | string | Element | ReactElement
|
||||||
|
small?: boolean
|
||||||
|
icon?: Element | ReactElement
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NumberUnitProps extends NumberInnerProps {
|
||||||
|
link?: string
|
||||||
|
linkTooltip?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const NumberInner = ({ small, label, value, icon }: NumberInnerProps) => (
|
||||||
|
<>
|
||||||
|
<div className={`${styles.number} ${small && styles.small}`}>
|
||||||
|
{icon && icon}
|
||||||
|
{value}
|
||||||
|
</div>
|
||||||
|
<span className={styles.label}>{label}</span>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function NumberUnit({
|
||||||
|
link,
|
||||||
|
linkTooltip,
|
||||||
|
small,
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
icon
|
||||||
|
}: NumberUnitProps): ReactElement {
|
||||||
|
return (
|
||||||
|
<div className={styles.unit}>
|
||||||
|
{link ? (
|
||||||
|
<a href={link} title={linkTooltip}>
|
||||||
|
<NumberInner small={small} label={label} value={value} icon={icon} />
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<NumberInner small={small} label={label} value={value} icon={icon} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -39,10 +39,10 @@ import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelectio
|
|||||||
import AlgorithmDatasetsListForCompute from '../../AssetContent/AlgorithmDatasetsListForCompute'
|
import AlgorithmDatasetsListForCompute from '../../AssetContent/AlgorithmDatasetsListForCompute'
|
||||||
import { getPreviousOrders, getPrice } from '../../../../utils/subgraph'
|
import { getPreviousOrders, getPrice } from '../../../../utils/subgraph'
|
||||||
import AssetActionHistoryTable from '../../AssetActionHistoryTable'
|
import AssetActionHistoryTable from '../../AssetActionHistoryTable'
|
||||||
import ComputeJobs from '../../../pages/Account/History/ComputeJobs'
|
import ComputeJobs from '../../../pages/Profile/History/ComputeJobs'
|
||||||
|
|
||||||
const SuccessAction = () => (
|
const SuccessAction = () => (
|
||||||
<Button style="text" to="/account?defaultTab=ComputeJobs" size="small">
|
<Button style="text" to="/profile?defaultTab=ComputeJobs" size="small">
|
||||||
Go to history →
|
Go to history →
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
import React, { ReactElement } from 'react'
|
|
||||||
import HistoryPage from './History'
|
|
||||||
import { useWeb3 } from '../../../providers/Web3'
|
|
||||||
|
|
||||||
export default function AccountPage({
|
|
||||||
accountIdentifier
|
|
||||||
}: {
|
|
||||||
accountIdentifier: string
|
|
||||||
}): ReactElement {
|
|
||||||
const { accountId } = useWeb3()
|
|
||||||
if (!accountIdentifier) accountIdentifier = accountId
|
|
||||||
|
|
||||||
return (
|
|
||||||
<article>
|
|
||||||
{accountIdentifier ? (
|
|
||||||
<p>WIP Account metadata header for user: {accountIdentifier}</p>
|
|
||||||
) : (
|
|
||||||
<p>Please connect your Web3 wallet.</p>
|
|
||||||
)}
|
|
||||||
<HistoryPage accountIdentifier={accountIdentifier} />
|
|
||||||
</article>
|
|
||||||
)
|
|
||||||
}
|
|
62
src/components/pages/Profile/Account.module.css
Normal file
62
src/components/pages/Profile/Account.module.css
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
.account {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 40rem) {
|
||||||
|
.account {
|
||||||
|
text-align: left;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 0.2fr 1.8fr;
|
||||||
|
gap: calc(var(--spacer) / 2);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.imageWrap {
|
||||||
|
width: 96px;
|
||||||
|
height: 96px;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: calc(var(--spacer) / 4);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
box-shadow: 0 6px 17px 0 var(--box-shadow-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
max-width: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-size: var(--font-size-h3);
|
||||||
|
margin-bottom: calc(var(--spacer) / 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.accountId {
|
||||||
|
display: block;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
color: var(--color-secondary);
|
||||||
|
word-wrap: break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.explorer {
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
margin-right: calc(var(--spacer) / 1.5);
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.explorer svg:first-child {
|
||||||
|
width: var(--font-size-mini);
|
||||||
|
height: var(--font-size-mini);
|
||||||
|
}
|
||||||
|
|
||||||
|
.explorer svg:last-child {
|
||||||
|
margin-left: calc(var(--spacer) / 12);
|
||||||
|
}
|
75
src/components/pages/Profile/Account.tsx
Normal file
75
src/components/pages/Profile/Account.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { toDataUrl } from 'ethereum-blockies'
|
||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import { useUserPreferences } from '../../../providers/UserPreferences'
|
||||||
|
import { accountTruncate } from '../../../utils/web3'
|
||||||
|
import ExplorerLink from '../../atoms/ExplorerLink'
|
||||||
|
import NetworkName from '../../atoms/NetworkName'
|
||||||
|
import jellyfish from '@oceanprotocol/art/creatures/jellyfish/jellyfish-grid.svg'
|
||||||
|
import styles from './Account.module.css'
|
||||||
|
import Copy from '../../atoms/Copy'
|
||||||
|
|
||||||
|
const Blockies = ({ account }: { account: string | undefined }) => {
|
||||||
|
if (!account) return null
|
||||||
|
|
||||||
|
const blockies = toDataUrl(account)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
className={styles.image}
|
||||||
|
src={blockies}
|
||||||
|
alt="Blockies"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Account({
|
||||||
|
name,
|
||||||
|
image,
|
||||||
|
accountId
|
||||||
|
}: {
|
||||||
|
name: string
|
||||||
|
image: string
|
||||||
|
accountId: string
|
||||||
|
}): ReactElement {
|
||||||
|
const { chainIds } = useUserPreferences()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.account}>
|
||||||
|
<figure className={styles.imageWrap}>
|
||||||
|
{image ? (
|
||||||
|
<img src={image} className={styles.image} width="96" height="96" />
|
||||||
|
) : accountId ? (
|
||||||
|
<Blockies account={accountId} />
|
||||||
|
) : (
|
||||||
|
<img
|
||||||
|
src={jellyfish}
|
||||||
|
className={styles.image}
|
||||||
|
width="96"
|
||||||
|
height="96"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className={styles.name}>{name || accountTruncate(accountId)}</h3>
|
||||||
|
<code className={styles.accountId}>
|
||||||
|
{accountId} <Copy text={accountId} />
|
||||||
|
</code>
|
||||||
|
<p>
|
||||||
|
{accountId &&
|
||||||
|
chainIds.map((value) => (
|
||||||
|
<ExplorerLink
|
||||||
|
className={styles.explorer}
|
||||||
|
networkId={value}
|
||||||
|
path={`address/${accountId}`}
|
||||||
|
key={value}
|
||||||
|
>
|
||||||
|
<NetworkName networkId={value} />
|
||||||
|
</ExplorerLink>
|
||||||
|
))}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
54
src/components/pages/Profile/Header.module.css
Normal file
54
src/components/pages/Profile/Header.module.css
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
.grid {
|
||||||
|
composes: box from '../../atoms/Box.module.css';
|
||||||
|
background: var(--background-body-transparent);
|
||||||
|
backdrop-filter: blur(3px);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
color: var(--color-secondary);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
margin-top: var(--spacer);
|
||||||
|
}
|
||||||
|
|
||||||
|
.description p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 50rem) {
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--spacer);
|
||||||
|
/* lazy golden ratio */
|
||||||
|
grid-template-columns: 1.618fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
margin-top: calc(var(--spacer) / 2);
|
||||||
|
-webkit-line-clamp: 7 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.publisherLinks {
|
||||||
|
margin-top: calc(var(--spacer) / 2);
|
||||||
|
margin-bottom: calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.more {
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
margin-left: calc(var(--spacer) / 8);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta {
|
||||||
|
color: var(--color-secondary);
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
position: absolute;
|
||||||
|
right: calc(var(--spacer) / 3);
|
||||||
|
bottom: calc(var(--spacer) / 6);
|
||||||
|
}
|
109
src/components/pages/Profile/Header.tsx
Normal file
109
src/components/pages/Profile/Header.tsx
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
|
import get3BoxProfile from '../../../utils/profile'
|
||||||
|
import { ProfileLink } from '../../../models/Profile'
|
||||||
|
import { accountTruncate } from '../../../utils/web3'
|
||||||
|
import axios from 'axios'
|
||||||
|
import PublisherLinks from './PublisherLinks'
|
||||||
|
import Markdown from '../../atoms/Markdown'
|
||||||
|
import Stats from './Stats'
|
||||||
|
import Account from './Account'
|
||||||
|
import styles from './Header.module.css'
|
||||||
|
|
||||||
|
const isDescriptionTextClamped = () => {
|
||||||
|
const el = document.getElementById('description')
|
||||||
|
if (el) return el.scrollHeight > el.clientHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AccountHeader({
|
||||||
|
accountId
|
||||||
|
}: {
|
||||||
|
accountId: string
|
||||||
|
}): ReactElement {
|
||||||
|
const [image, setImage] = useState<string>()
|
||||||
|
const [name, setName] = useState(accountTruncate(accountId))
|
||||||
|
const [description, setDescription] = useState<string>()
|
||||||
|
const [links, setLinks] = useState<ProfileLink[]>()
|
||||||
|
const [isShowMore, setIsShowMore] = useState(false)
|
||||||
|
|
||||||
|
const toogleShowMore = () => {
|
||||||
|
setIsShowMore(!isShowMore)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!accountId) {
|
||||||
|
setName(null)
|
||||||
|
setDescription(null)
|
||||||
|
setImage(null)
|
||||||
|
setLinks([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const source = axios.CancelToken.source()
|
||||||
|
|
||||||
|
async function getInfoFrom3Box() {
|
||||||
|
const profile = await get3BoxProfile(accountId, source.token)
|
||||||
|
if (profile) {
|
||||||
|
const { name, emoji, description, image, links } = profile
|
||||||
|
setName(`${emoji || ''} ${name || accountTruncate(accountId)}`)
|
||||||
|
setDescription(description || null)
|
||||||
|
setImage(image || null)
|
||||||
|
setLinks(links || [])
|
||||||
|
} else {
|
||||||
|
setName(null)
|
||||||
|
setDescription(null)
|
||||||
|
setImage(null)
|
||||||
|
setLinks([])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getInfoFrom3Box()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
source.cancel()
|
||||||
|
}
|
||||||
|
}, [accountId])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.grid}>
|
||||||
|
<div>
|
||||||
|
<Account accountId={accountId} image={image} name={name} />
|
||||||
|
<Stats accountId={accountId} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Markdown
|
||||||
|
text={
|
||||||
|
description ||
|
||||||
|
'No description found on [3box](https://3box.io/login).'
|
||||||
|
}
|
||||||
|
className={styles.description}
|
||||||
|
/>
|
||||||
|
{isDescriptionTextClamped() ? (
|
||||||
|
<span className={styles.more} onClick={toogleShowMore}>
|
||||||
|
<a
|
||||||
|
href={`https://www.3box.io/${accountId}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
Read more on 3box
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
{links?.length > 0 && (
|
||||||
|
<PublisherLinks links={links} className={styles.publisherLinks} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={styles.meta}>
|
||||||
|
Profile data from{' '}
|
||||||
|
<a
|
||||||
|
href={`https://www.3box.io/${accountId}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
3Box Hub
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -7,7 +7,6 @@ import Modal from '../../../../atoms/Modal'
|
|||||||
import MetaItem from '../../../../organisms/AssetContent/MetaItem'
|
import MetaItem from '../../../../organisms/AssetContent/MetaItem'
|
||||||
import { ReactComponent as External } from '../../../../../images/external.svg'
|
import { ReactComponent as External } from '../../../../../images/external.svg'
|
||||||
import { retrieveDDO } from '../../../../../utils/aquarius'
|
import { retrieveDDO } from '../../../../../utils/aquarius'
|
||||||
import { useOcean } from '../../../../../providers/Ocean'
|
|
||||||
import Results from './Results'
|
import Results from './Results'
|
||||||
import styles from './Details.module.css'
|
import styles from './Details.module.css'
|
||||||
import { useSiteMetadata } from '../../../../../hooks/useSiteMetadata'
|
import { useSiteMetadata } from '../../../../../hooks/useSiteMetadata'
|
@ -22,10 +22,7 @@ import styles from './index.module.css'
|
|||||||
import { useUserPreferences } from '../../../../../providers/UserPreferences'
|
import { useUserPreferences } from '../../../../../providers/UserPreferences'
|
||||||
import { getOceanConfig } from '../../../../../utils/ocean'
|
import { getOceanConfig } from '../../../../../utils/ocean'
|
||||||
import { fetchDataForMultipleChains } from '../../../../../utils/subgraph'
|
import { fetchDataForMultipleChains } from '../../../../../utils/subgraph'
|
||||||
import {
|
import { OrdersData_tokenOrders_datatokenId as OrdersDatatoken } from '../../../../../@types/apollo/OrdersData'
|
||||||
OrdersData_tokenOrders as OrdersData,
|
|
||||||
OrdersData_tokenOrders_datatokenId as OrdersDatatoken
|
|
||||||
} from '../../../../../@types/apollo/OrdersData'
|
|
||||||
import NetworkName from '../../../../atoms/NetworkName'
|
import NetworkName from '../../../../atoms/NetworkName'
|
||||||
|
|
||||||
const getComputeOrders = gql`
|
const getComputeOrders = gql`
|
@ -4,7 +4,6 @@ import { gql } from 'urql'
|
|||||||
import Time from '../../../atoms/Time'
|
import Time from '../../../atoms/Time'
|
||||||
import web3 from 'web3'
|
import web3 from 'web3'
|
||||||
import AssetTitle from '../../../molecules/AssetListTitle'
|
import AssetTitle from '../../../molecules/AssetListTitle'
|
||||||
import { useWeb3 } from '../../../../providers/Web3'
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { retrieveDDO } from '../../../../utils/aquarius'
|
import { retrieveDDO } from '../../../../utils/aquarius'
|
||||||
import { Logger } from '@oceanprotocol/lib'
|
import { Logger } from '@oceanprotocol/lib'
|
@ -5,15 +5,16 @@ import styles from './PoolShares.module.css'
|
|||||||
import AssetTitle from '../../../molecules/AssetListTitle'
|
import AssetTitle from '../../../molecules/AssetListTitle'
|
||||||
import { gql } from 'urql'
|
import { gql } from 'urql'
|
||||||
import {
|
import {
|
||||||
PoolShares as PoolSharesList,
|
|
||||||
PoolShares_poolShares as PoolShare,
|
PoolShares_poolShares as PoolShare,
|
||||||
PoolShares_poolShares_poolId_tokens as PoolSharePoolIdTokens
|
PoolShares_poolShares_poolId_tokens as PoolSharePoolIdTokens
|
||||||
} from '../../../../@types/apollo/PoolShares'
|
} from '../../../../@types/apollo/PoolShares'
|
||||||
import web3 from 'web3'
|
import web3 from 'web3'
|
||||||
import Token from '../../../organisms/AssetActions/Pool/Token'
|
import Token from '../../../organisms/AssetActions/Pool/Token'
|
||||||
import { useWeb3 } from '../../../../providers/Web3'
|
|
||||||
import { useUserPreferences } from '../../../../providers/UserPreferences'
|
import { useUserPreferences } from '../../../../providers/UserPreferences'
|
||||||
import { fetchDataForMultipleChains } from '../../../../utils/subgraph'
|
import {
|
||||||
|
fetchDataForMultipleChains,
|
||||||
|
calculateUserLiquidity
|
||||||
|
} from '../../../../utils/subgraph'
|
||||||
import NetworkName from '../../../atoms/NetworkName'
|
import NetworkName from '../../../atoms/NetworkName'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { retrieveDDO } from '../../../../utils/aquarius'
|
import { retrieveDDO } from '../../../../utils/aquarius'
|
||||||
@ -59,17 +60,6 @@ interface Asset {
|
|||||||
createTime: number
|
createTime: number
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateUserLiquidity(poolShare: PoolShare) {
|
|
||||||
const ocean =
|
|
||||||
(poolShare.balance / poolShare.poolId.totalShares) *
|
|
||||||
poolShare.poolId.oceanReserve
|
|
||||||
const datatokens =
|
|
||||||
(poolShare.balance / poolShare.poolId.totalShares) *
|
|
||||||
poolShare.poolId.datatokenReserve
|
|
||||||
const totalLiquidity = ocean + datatokens * poolShare.poolId.consumePrice
|
|
||||||
return totalLiquidity
|
|
||||||
}
|
|
||||||
|
|
||||||
function findValidToken(tokens: PoolSharePoolIdTokens[]) {
|
function findValidToken(tokens: PoolSharePoolIdTokens[]) {
|
||||||
const symbol = tokens.find((token) => token.tokenId !== null)
|
const symbol = tokens.find((token) => token.tokenId !== null)
|
||||||
return symbol.tokenId.symbol
|
return symbol.tokenId.symbol
|
@ -1,6 +1,6 @@
|
|||||||
import { Logger } from '@oceanprotocol/lib'
|
import { Logger } from '@oceanprotocol/lib'
|
||||||
import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
||||||
import React, { ReactElement, useEffect, useState, useReducer } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import AssetList from '../../../organisms/AssetList'
|
import AssetList from '../../../organisms/AssetList'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import {
|
import {
|
@ -16,7 +16,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.tabs {
|
||||||
margin-top: var(--spacer);
|
margin-top: var(--spacer);
|
||||||
background-color: var(--background-body);
|
background-color: var(--background-body);
|
||||||
}
|
}
|
@ -5,8 +5,8 @@ import PoolTransactions from '../../../molecules/PoolTransactions'
|
|||||||
import PublishedList from './PublishedList'
|
import PublishedList from './PublishedList'
|
||||||
import Downloads from './Downloads'
|
import Downloads from './Downloads'
|
||||||
import ComputeJobs from './ComputeJobs'
|
import ComputeJobs from './ComputeJobs'
|
||||||
|
import { useLocation } from '@reach/router'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import { useUserPreferences } from '../../../../providers/UserPreferences'
|
|
||||||
import OceanProvider from '../../../../providers/Ocean'
|
import OceanProvider from '../../../../providers/Ocean'
|
||||||
import { useWeb3 } from '../../../../providers/Web3'
|
import { useWeb3 } from '../../../../providers/Web3'
|
||||||
|
|
||||||
@ -53,20 +53,17 @@ export default function HistoryPage({
|
|||||||
}: {
|
}: {
|
||||||
accountIdentifier: string
|
accountIdentifier: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { chainIds } = useUserPreferences()
|
|
||||||
const { accountId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const url = new URL(window.location.href)
|
const location = useLocation()
|
||||||
|
|
||||||
|
const url = new URL(location.href)
|
||||||
const defaultTab = url.searchParams.get('defaultTab')
|
const defaultTab = url.searchParams.get('defaultTab')
|
||||||
const tabs = getTabs(accountIdentifier, accountId)
|
const tabs = getTabs(accountIdentifier, accountId)
|
||||||
|
|
||||||
let defaultTabIndex = 0
|
let defaultTabIndex = 0
|
||||||
defaultTab === 'ComputeJobs' ? (defaultTabIndex = 4) : (defaultTabIndex = 0)
|
defaultTab === 'ComputeJobs' ? (defaultTabIndex = 4) : (defaultTabIndex = 0)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article className={styles.content}>
|
<Tabs items={tabs} className={styles.tabs} defaultIndex={defaultTabIndex} />
|
||||||
<Tabs
|
|
||||||
items={tabs}
|
|
||||||
className={styles.tabs}
|
|
||||||
defaultIndex={defaultTabIndex}
|
|
||||||
/>
|
|
||||||
</article>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -1,3 +1,7 @@
|
|||||||
|
.links {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.links,
|
.links,
|
||||||
.links a {
|
.links a {
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
@ -7,6 +11,7 @@
|
|||||||
.links a {
|
.links a {
|
||||||
margin-left: calc(var(--spacer) / 3);
|
margin-left: calc(var(--spacer) / 3);
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.links a:first-child {
|
.links a:first-child {
|
||||||
@ -19,5 +24,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.linksExternal {
|
.linksExternal {
|
||||||
composes: linksExternal from './index.module.css';
|
composes: linksExternal from '../../atoms/Publisher/index.module.css';
|
||||||
}
|
}
|
@ -1,15 +1,25 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styles from './PublisherLinks.module.css'
|
import classNames from 'classnames/bind'
|
||||||
import { ProfileLink } from '../../../models/Profile'
|
import { ProfileLink } from '../../../models/Profile'
|
||||||
import { ReactComponent as External } from '../../../images/external.svg'
|
import { ReactComponent as External } from '../../../images/external.svg'
|
||||||
|
import styles from './PublisherLinks.module.css'
|
||||||
|
|
||||||
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
export default function PublisherLinks({
|
export default function PublisherLinks({
|
||||||
links
|
links,
|
||||||
|
className
|
||||||
}: {
|
}: {
|
||||||
links: ProfileLink[]
|
links: ProfileLink[]
|
||||||
|
className: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
|
const styleClasses = cx({
|
||||||
|
links: true,
|
||||||
|
[className]: className
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.links}>
|
<div className={styleClasses}>
|
||||||
{' — '}
|
{' — '}
|
||||||
{links?.map((link: ProfileLink) => {
|
{links?.map((link: ProfileLink) => {
|
||||||
const href =
|
const href =
|
6
src/components/pages/Profile/Stats.module.css
Normal file
6
src/components/pages/Profile/Stats.module.css
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.stats {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--spacer);
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
|
||||||
|
margin-top: calc(var(--spacer) / 2);
|
||||||
|
}
|
103
src/components/pages/Profile/Stats.tsx
Normal file
103
src/components/pages/Profile/Stats.tsx
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import { DDO, Logger } from '@oceanprotocol/lib'
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { ReactElement } from 'react-markdown'
|
||||||
|
import { useUserPreferences } from '../../../providers/UserPreferences'
|
||||||
|
import {
|
||||||
|
getAccountLiquidityInOwnAssets,
|
||||||
|
getAccountNumberOfOrders,
|
||||||
|
getAssetsBestPrices,
|
||||||
|
UserTVL
|
||||||
|
} from '../../../utils/subgraph'
|
||||||
|
import Conversion from '../../atoms/Price/Conversion'
|
||||||
|
import NumberUnit from '../../molecules/NumberUnit'
|
||||||
|
import styles from './Stats.module.css'
|
||||||
|
import {
|
||||||
|
queryMetadata,
|
||||||
|
transformChainIdsListToQuery
|
||||||
|
} from '../../../utils/aquarius'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
export default function Stats({
|
||||||
|
accountId
|
||||||
|
}: {
|
||||||
|
accountId: string
|
||||||
|
}): ReactElement {
|
||||||
|
const { chainIds } = useUserPreferences()
|
||||||
|
|
||||||
|
const [publishedAssets, setPublishedAssets] = useState<DDO[]>()
|
||||||
|
const [numberOfAssets, setNumberOfAssets] = useState(0)
|
||||||
|
const [sold, setSold] = useState(0)
|
||||||
|
const [tvl, setTvl] = useState<UserTVL>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!accountId) {
|
||||||
|
setNumberOfAssets(0)
|
||||||
|
setSold(0)
|
||||||
|
setTvl({ price: '0', oceanBalance: '0' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getPublished() {
|
||||||
|
const queryPublishedAssets = {
|
||||||
|
query: {
|
||||||
|
query_string: {
|
||||||
|
query: `(publicKey.owner:${accountId}) AND (${transformChainIdsListToQuery(
|
||||||
|
chainIds
|
||||||
|
)})`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const source = axios.CancelToken.source()
|
||||||
|
const result = await queryMetadata(queryPublishedAssets, source.token)
|
||||||
|
setPublishedAssets(result.results)
|
||||||
|
setNumberOfAssets(result.totalResults)
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getPublished()
|
||||||
|
|
||||||
|
async function getAccountSoldValue() {
|
||||||
|
const nrOrders = await getAccountNumberOfOrders(accountId, chainIds)
|
||||||
|
setSold(nrOrders)
|
||||||
|
}
|
||||||
|
getAccountSoldValue()
|
||||||
|
}, [accountId, chainIds])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!publishedAssets) return
|
||||||
|
|
||||||
|
async function getAccountTVL() {
|
||||||
|
try {
|
||||||
|
const accountPoolAdresses: string[] = []
|
||||||
|
const assetsPrices = await getAssetsBestPrices(publishedAssets)
|
||||||
|
for (const priceInfo of assetsPrices) {
|
||||||
|
if (priceInfo.price.type === 'pool') {
|
||||||
|
accountPoolAdresses.push(priceInfo.price.address.toLowerCase())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const userTvl: UserTVL = await getAccountLiquidityInOwnAssets(
|
||||||
|
accountId,
|
||||||
|
chainIds,
|
||||||
|
accountPoolAdresses
|
||||||
|
)
|
||||||
|
setTvl(userTvl)
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getAccountTVL()
|
||||||
|
}, [publishedAssets])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.stats}>
|
||||||
|
<NumberUnit label="Published" value={numberOfAssets} />
|
||||||
|
<NumberUnit label="Sold" value={sold} />
|
||||||
|
<NumberUnit
|
||||||
|
label="Total Value Locked"
|
||||||
|
value={<Conversion price={tvl?.price} hideApproximateSymbol />}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
16
src/components/pages/Profile/index.tsx
Normal file
16
src/components/pages/Profile/index.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import HistoryPage from './History'
|
||||||
|
import AccountHeader from './Header'
|
||||||
|
|
||||||
|
export default function AccountPage({
|
||||||
|
accountId
|
||||||
|
}: {
|
||||||
|
accountId: string
|
||||||
|
}): ReactElement {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AccountHeader accountId={accountId} />
|
||||||
|
<HistoryPage accountIdentifier={accountId} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -5,7 +5,7 @@ import Container from '../atoms/Container'
|
|||||||
|
|
||||||
export interface PageProps {
|
export interface PageProps {
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
title: string
|
title?: string
|
||||||
uri: string
|
uri: string
|
||||||
description?: string
|
description?: string
|
||||||
noPageHeader?: boolean
|
noPageHeader?: boolean
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
import React, { ReactElement, useState, useEffect } from 'react'
|
|
||||||
import Page from '../../components/templates/Page'
|
|
||||||
import { graphql, PageProps } from 'gatsby'
|
|
||||||
import AccountPage from '../../components/pages/Account'
|
|
||||||
|
|
||||||
export default function PageGatsbyAccount(props: PageProps): ReactElement {
|
|
||||||
const content = (props.data as any).content.edges[0].node.childPagesJson
|
|
||||||
const { title } = content
|
|
||||||
const [accountId, setAccountId] = useState<string>()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setAccountId(props.location.pathname.split('/')[2])
|
|
||||||
}, [props.location.pathname])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Page title={title} uri={props.uri}>
|
|
||||||
<AccountPage accountIdentifier={accountId} />
|
|
||||||
</Page>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const contentQuery = graphql`
|
|
||||||
query AccountPageQuery {
|
|
||||||
content: allFile(filter: { relativePath: { eq: "pages/account.json" } }) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
childPagesJson {
|
|
||||||
title
|
|
||||||
description
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
39
src/pages/profile/index.tsx
Normal file
39
src/pages/profile/index.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
|
import Page from '../../components/templates/Page'
|
||||||
|
import { graphql, PageProps } from 'gatsby'
|
||||||
|
import ProfilePage from '../../components/pages/Profile'
|
||||||
|
import { accountTruncate } from '../../utils/web3'
|
||||||
|
import { useWeb3 } from '../../providers/Web3'
|
||||||
|
|
||||||
|
export default function PageGatsbyProfile(props: PageProps): ReactElement {
|
||||||
|
const { accountId } = useWeb3()
|
||||||
|
const [finalAccountId, setFinalAccountId] = useState<string>()
|
||||||
|
|
||||||
|
// Have accountId in path take over, if not present fall back to web3
|
||||||
|
useEffect(() => {
|
||||||
|
const pathAccountId = props.location.pathname.split('/')[2]
|
||||||
|
const finalAccountId = pathAccountId || accountId
|
||||||
|
setFinalAccountId(finalAccountId)
|
||||||
|
}, [props.location.pathname, accountId])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page uri={props.uri} title={accountTruncate(finalAccountId)} noPageHeader>
|
||||||
|
<ProfilePage accountId={finalAccountId} />
|
||||||
|
</Page>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const contentQuery = graphql`
|
||||||
|
query ProfilePageQuery {
|
||||||
|
content: allFile(filter: { relativePath: { eq: "pages/profile.json" } }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
childPagesJson {
|
||||||
|
title
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
@ -20,6 +20,15 @@ import {
|
|||||||
HighestLiquidityAssets_pools as HighestLiquidityAssetsPools,
|
HighestLiquidityAssets_pools as HighestLiquidityAssetsPools,
|
||||||
HighestLiquidityAssets as HighestLiquidityGraphAssets
|
HighestLiquidityAssets as HighestLiquidityGraphAssets
|
||||||
} from '../@types/apollo/HighestLiquidityAssets'
|
} from '../@types/apollo/HighestLiquidityAssets'
|
||||||
|
import {
|
||||||
|
PoolShares as PoolSharesList,
|
||||||
|
PoolShares_poolShares as PoolShare
|
||||||
|
} from '../@types/apollo/PoolShares'
|
||||||
|
|
||||||
|
export interface UserTVL {
|
||||||
|
price: string
|
||||||
|
oceanBalance: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface PriceList {
|
export interface PriceList {
|
||||||
[key: string]: string
|
[key: string]: string
|
||||||
@ -141,6 +150,43 @@ const HighestLiquidityAssets = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const TotalAccountOrders = gql`
|
||||||
|
query TotalAccountOrders($payer: String) {
|
||||||
|
tokenOrders(orderBy: id, where: { payer: $payer }) {
|
||||||
|
id
|
||||||
|
payer {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const UserSharesQuery = gql`
|
||||||
|
query UserSharesQuery($user: String, $pools: [String!]) {
|
||||||
|
poolShares(where: { userAddress: $user, poolId_in: $pools }) {
|
||||||
|
id
|
||||||
|
balance
|
||||||
|
userAddress {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
poolId {
|
||||||
|
id
|
||||||
|
datatokenAddress
|
||||||
|
valueLocked
|
||||||
|
tokens {
|
||||||
|
tokenId {
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oceanReserve
|
||||||
|
datatokenReserve
|
||||||
|
totalShares
|
||||||
|
consumePrice
|
||||||
|
spotPrice
|
||||||
|
createTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
export function getSubgraphUri(chainId: number): string {
|
export function getSubgraphUri(chainId: number): string {
|
||||||
const config = getOceanConfig(chainId)
|
const config = getOceanConfig(chainId)
|
||||||
return config.subgraphUri
|
return config.subgraphUri
|
||||||
@ -497,3 +543,64 @@ export async function getHighestLiquidityDIDs(
|
|||||||
.replace(/(did:op:)/g, '0x')
|
.replace(/(did:op:)/g, '0x')
|
||||||
return [searchDids, didList.length]
|
return [searchDids, didList.length]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getAccountNumberOfOrders(
|
||||||
|
accountId: string,
|
||||||
|
chainIds: number[]
|
||||||
|
): Promise<number> {
|
||||||
|
const queryVariables = {
|
||||||
|
payer: accountId.toLowerCase()
|
||||||
|
}
|
||||||
|
const results = await fetchDataForMultipleChains(
|
||||||
|
TotalAccountOrders,
|
||||||
|
queryVariables,
|
||||||
|
chainIds
|
||||||
|
)
|
||||||
|
let numberOfOrders = 0
|
||||||
|
for (const result of results) {
|
||||||
|
numberOfOrders += result.tokenOrders.length
|
||||||
|
}
|
||||||
|
return numberOfOrders
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateUserLiquidity(poolShare: PoolShare) {
|
||||||
|
const ocean =
|
||||||
|
(poolShare.balance / poolShare.poolId.totalShares) *
|
||||||
|
poolShare.poolId.oceanReserve
|
||||||
|
const datatokens =
|
||||||
|
(poolShare.balance / poolShare.poolId.totalShares) *
|
||||||
|
poolShare.poolId.datatokenReserve
|
||||||
|
const totalLiquidity = ocean + datatokens * poolShare.poolId.consumePrice
|
||||||
|
return totalLiquidity
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAccountLiquidityInOwnAssets(
|
||||||
|
accountId: string,
|
||||||
|
chainIds: number[],
|
||||||
|
pools: string[]
|
||||||
|
): Promise<UserTVL> {
|
||||||
|
const queryVariables = {
|
||||||
|
user: accountId.toLowerCase(),
|
||||||
|
pools: pools
|
||||||
|
}
|
||||||
|
const results: PoolSharesList[] = await fetchDataForMultipleChains(
|
||||||
|
UserSharesQuery,
|
||||||
|
queryVariables,
|
||||||
|
chainIds
|
||||||
|
)
|
||||||
|
let totalLiquidity = 0
|
||||||
|
let totalOceanLiquidity = 0
|
||||||
|
for (const result of results) {
|
||||||
|
for (const poolShare of result.poolShares) {
|
||||||
|
const userShare = poolShare.balance / poolShare.poolId.totalShares
|
||||||
|
const userBalance = userShare * poolShare.poolId.oceanReserve
|
||||||
|
totalOceanLiquidity += userBalance
|
||||||
|
const poolLiquidity = calculateUserLiquidity(poolShare)
|
||||||
|
totalLiquidity += poolLiquidity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
price: totalLiquidity.toString(),
|
||||||
|
oceanBalance: totalOceanLiquidity.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -23,8 +23,8 @@
|
|||||||
"link": "/publish"
|
"link": "/publish"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Account",
|
"name": "Profile",
|
||||||
"link": "/account"
|
"link": "/profile"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user