mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Merge pull request #774 from oceanprotocol/feature/account_page
This commit is contained in:
commit
db5b892e23
@ -1 +1,2 @@
|
|||||||
/asset/* /asset/index.html 200
|
/asset/* /asset/index.html 200
|
||||||
|
/profile/* /profile/index.html 200
|
@ -1,6 +1,4 @@
|
|||||||
{
|
{
|
||||||
"title": "History",
|
|
||||||
"description": "Find the data sets and jobs that you previously accessed.",
|
|
||||||
"compute": {
|
"compute": {
|
||||||
"storage": "Results are stored for 30 days."
|
"storage": "Results are stored for 30 days."
|
||||||
}
|
}
|
||||||
|
4
content/pages/profile.json
Normal file
4
content/pages/profile.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"title": "Account",
|
||||||
|
"description": "Find the data sets and jobs that you previously accessed."
|
||||||
|
}
|
@ -12,8 +12,8 @@
|
|||||||
"link": "/publish"
|
"link": "/publish"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "History",
|
"name": "Profile",
|
||||||
"link": "/history"
|
"link": "/profile"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"warning": {
|
"warning": {
|
||||||
|
@ -33,13 +33,16 @@ exports.onCreatePage = async ({ page, actions }) => {
|
|||||||
const { createPage } = actions
|
const { createPage } = 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 handleClientSideOnly = page.path.match(/^\/asset/)
|
const handleClientSideOnlyAsset = page.path.match(/^\/asset/)
|
||||||
|
const handleClientSideOnlyAccount = page.path.match(/^\/profile/)
|
||||||
|
|
||||||
if (handleClientSideOnly) {
|
if (handleClientSideOnlyAsset) {
|
||||||
page.matchPath = '/asset/*'
|
page.matchPath = '/asset/*'
|
||||||
|
|
||||||
// Update the page.
|
// Update the page.
|
||||||
createPage(page)
|
createPage(page)
|
||||||
|
} else if (handleClientSideOnlyAccount) {
|
||||||
|
page.matchPath = '/profile/*'
|
||||||
|
createPage(page)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
116
package-lock.json
generated
116
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",
|
||||||
@ -85,7 +86,6 @@
|
|||||||
"@testing-library/jest-dom": "^5.12.0",
|
"@testing-library/jest-dom": "^5.12.0",
|
||||||
"@testing-library/react": "^11.2.7",
|
"@testing-library/react": "^11.2.7",
|
||||||
"@types/chart.js": "^2.9.32",
|
"@types/chart.js": "^2.9.32",
|
||||||
"@types/classnames": "^2.3.1",
|
|
||||||
"@types/jest": "^26.0.23",
|
"@types/jest": "^26.0.23",
|
||||||
"@types/loadable__component": "^5.13.1",
|
"@types/loadable__component": "^5.13.1",
|
||||||
"@types/lodash.debounce": "^4.0.3",
|
"@types/lodash.debounce": "^4.0.3",
|
||||||
@ -10514,14 +10514,13 @@
|
|||||||
"moment": "^2.10.2"
|
"moment": "^2.10.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/classnames": {
|
"node_modules/@types/clipboard": {
|
||||||
"version": "2.3.1",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/clipboard/-/clipboard-2.0.7.tgz",
|
||||||
"integrity": "sha512-zeOWb0JGBoVmlQoznvqXbE0tEC/HONsnoUNH19Hc96NFsTAwTXbTqb8FMYkru1F/iqp7a18Ws3nWJvtA1sHD1A==",
|
"integrity": "sha512-VwVFUHlneOsWfv/GaaY7Kwk4XasDqkAlyFQtsHxnOw0yyBYWTrlEXtmb9RtC+VFBCdtuOeIXECmELNd5RrKp/g==",
|
||||||
"deprecated": "This is a stub types definition. classnames provides its own type definitions, so you do not need this installed.",
|
"deprecated": "This is a stub types definition. clipboard provides its own type definitions, so you do not need this installed.",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"classnames": "*"
|
"clipboard": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/common-tags": {
|
"node_modules/@types/common-tags": {
|
||||||
@ -16998,6 +16997,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 +19013,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 +28792,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 +44926,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 +47692,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 +51506,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",
|
||||||
@ -66963,13 +67009,12 @@
|
|||||||
"moment": "^2.10.2"
|
"moment": "^2.10.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/classnames": {
|
"@types/clipboard": {
|
||||||
"version": "2.3.1",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/clipboard/-/clipboard-2.0.7.tgz",
|
||||||
"integrity": "sha512-zeOWb0JGBoVmlQoznvqXbE0tEC/HONsnoUNH19Hc96NFsTAwTXbTqb8FMYkru1F/iqp7a18Ws3nWJvtA1sHD1A==",
|
"integrity": "sha512-VwVFUHlneOsWfv/GaaY7Kwk4XasDqkAlyFQtsHxnOw0yyBYWTrlEXtmb9RtC+VFBCdtuOeIXECmELNd5RrKp/g==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"classnames": "*"
|
"clipboard": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/common-tags": {
|
"@types/common-tags": {
|
||||||
@ -72373,6 +72418,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 +74032,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 +81734,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 +94763,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 +96986,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 +100087,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",
|
||||||
@ -100,7 +101,6 @@
|
|||||||
"@testing-library/jest-dom": "^5.12.0",
|
"@testing-library/jest-dom": "^5.12.0",
|
||||||
"@testing-library/react": "^11.2.7",
|
"@testing-library/react": "^11.2.7",
|
||||||
"@types/chart.js": "^2.9.32",
|
"@types/chart.js": "^2.9.32",
|
||||||
"@types/classnames": "^2.3.1",
|
|
||||||
"@types/jest": "^26.0.23",
|
"@types/jest": "^26.0.23",
|
||||||
"@types/loadable__component": "^5.13.1",
|
"@types/loadable__component": "^5.13.1",
|
||||||
"@types/lodash.debounce": "^4.0.3",
|
"@types/lodash.debounce": "^4.0.3",
|
||||||
|
8
src/components/atoms/Blockies.module.css
Normal file
8
src/components/atoms/Blockies.module.css
Normal file
@ -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;
|
||||||
|
}
|
23
src/components/atoms/Blockies.tsx
Normal file
23
src/components/atoms/Blockies.tsx
Normal file
@ -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 (
|
||||||
|
<img
|
||||||
|
className={`${styles.blockies} ${className || ''}`}
|
||||||
|
src={blockies}
|
||||||
|
alt="Blockies"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
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: -140%;
|
||||||
|
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,17 +1,14 @@
|
|||||||
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'
|
||||||
|
import { getEnsName } from '../../../utils/ens'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
@ -24,27 +21,34 @@ export default function Publisher({
|
|||||||
minimal?: boolean
|
minimal?: boolean
|
||||||
className?: string
|
className?: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { networkId, accountId } = useWeb3()
|
const { accountId } = useWeb3()
|
||||||
const [profile, setProfile] = useState<Profile>()
|
const [profile, setProfile] = useState<Profile>()
|
||||||
const [name, setName] = useState<string>()
|
const [name, setName] = useState(accountTruncate(account))
|
||||||
|
const [accountEns, setAccountEns] = useState<string>()
|
||||||
|
|
||||||
const showAdd = account === accountId && !profile
|
const showAdd = account === accountId && !profile
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!account) return
|
if (!account) return
|
||||||
|
|
||||||
setName(accountTruncate(account))
|
|
||||||
const source = axios.CancelToken.source()
|
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)
|
const profile = await get3BoxProfile(account, source.token)
|
||||||
if (!profile) return
|
if (!profile) return
|
||||||
|
|
||||||
setProfile(profile)
|
setProfile(profile)
|
||||||
const { name, emoji } = profile
|
const { name, emoji } = profile
|
||||||
name && setName(`${emoji || ''} ${name}`)
|
name && setName(`${emoji || ''} ${name}`)
|
||||||
}
|
}
|
||||||
get3Box()
|
getExternalName()
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
source.cancel()
|
source.cancel()
|
||||||
@ -63,34 +67,12 @@ export default function Publisher({
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Link
|
<Link
|
||||||
to={`/search?sort=created&sortOrder=desc&text=${account}`}
|
to={`/profile/${accountEns || account}`}
|
||||||
title="Show all data sets created by this account."
|
title="Show profile page."
|
||||||
>
|
>
|
||||||
{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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,10 @@
|
|||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
|
@ -5,6 +5,7 @@ import { useSpring, animated } from 'react-spring'
|
|||||||
import styles from './Tooltip.module.css'
|
import styles from './Tooltip.module.css'
|
||||||
import { ReactComponent as Info } from '../../images/info.svg'
|
import { ReactComponent as Info } from '../../images/info.svg'
|
||||||
import { Placement } from 'tippy.js'
|
import { Placement } from 'tippy.js'
|
||||||
|
import Markdown from './Markdown'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
import { useOcean } from '../../providers/Ocean'
|
|
||||||
import { Link } from 'gatsby'
|
import { Link } from 'gatsby'
|
||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import { getAssetsNames } from '../../utils/aquarius'
|
import { getAssetsNames } from '../../utils/aquarius'
|
||||||
@ -43,7 +42,7 @@ export default function AssetListTitle({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<h3 className={styles.title}>
|
<h3 className={styles.title}>
|
||||||
<Link to={`/asset/${did || ddo.id}`}>{assetTitle}</Link>
|
<Link to={`/asset/${did || ddo?.id}`}>{assetTitle}</Link>
|
||||||
</h3>
|
</h3>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,13 @@ import { BestPrice } from '../../models/BestPrice'
|
|||||||
declare type AssetTeaserProps = {
|
declare type AssetTeaserProps = {
|
||||||
ddo: DDO
|
ddo: DDO
|
||||||
price: BestPrice
|
price: BestPrice
|
||||||
|
noPublisher?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const AssetTeaser: React.FC<AssetTeaserProps> = ({
|
const AssetTeaser: React.FC<AssetTeaserProps> = ({
|
||||||
ddo,
|
ddo,
|
||||||
price
|
price,
|
||||||
|
noPublisher
|
||||||
}: AssetTeaserProps) => {
|
}: AssetTeaserProps) => {
|
||||||
const { attributes } = ddo.findServiceByType('metadata')
|
const { attributes } = ddo.findServiceByType('metadata')
|
||||||
const { name, type } = attributes.main
|
const { name, type } = attributes.main
|
||||||
@ -34,7 +36,9 @@ const AssetTeaser: React.FC<AssetTeaserProps> = ({
|
|||||||
<Dotdotdot clamp={3}>
|
<Dotdotdot clamp={3}>
|
||||||
<h1 className={styles.title}>{name}</h1>
|
<h1 className={styles.title}>{name}</h1>
|
||||||
</Dotdotdot>
|
</Dotdotdot>
|
||||||
<Publisher account={owner} minimal className={styles.publisher} />
|
{!noPublisher && (
|
||||||
|
<Publisher account={owner} minimal className={styles.publisher} />
|
||||||
|
)}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<AssetType
|
<AssetType
|
||||||
|
@ -5,10 +5,7 @@ import { Logger } from '@oceanprotocol/lib'
|
|||||||
import Price from '../atoms/Price'
|
import Price from '../atoms/Price'
|
||||||
import Tooltip from '../atoms/Tooltip'
|
import Tooltip from '../atoms/Tooltip'
|
||||||
import AssetTitle from './AssetListTitle'
|
import AssetTitle from './AssetListTitle'
|
||||||
import {
|
import { getAssetsFromDidList } from '../../utils/aquarius'
|
||||||
queryMetadata,
|
|
||||||
transformChainIdsListToQuery
|
|
||||||
} from '../../utils/aquarius'
|
|
||||||
import { getAssetsBestPrices, AssetListPrices } from '../../utils/subgraph'
|
import { getAssetsBestPrices, AssetListPrices } from '../../utils/subgraph'
|
||||||
import axios, { CancelToken } from 'axios'
|
import axios, { CancelToken } from 'axios'
|
||||||
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
|
import { useSiteMetadata } from '../../hooks/useSiteMetadata'
|
||||||
@ -18,31 +15,8 @@ async function getAssetsBookmarked(
|
|||||||
chainIds: number[],
|
chainIds: number[],
|
||||||
cancelToken: CancelToken
|
cancelToken: CancelToken
|
||||||
) {
|
) {
|
||||||
const searchDids = JSON.stringify(bookmarks)
|
|
||||||
.replace(/,/g, ' ')
|
|
||||||
.replace(/"/g, '')
|
|
||||||
.replace(/(\[|\])/g, '')
|
|
||||||
// for whatever reason ddo.id is not searchable, so use ddo.dataToken instead
|
|
||||||
.replace(/(did:op:)/g, '0x')
|
|
||||||
|
|
||||||
const queryBookmarks = {
|
|
||||||
page: 1,
|
|
||||||
offset: 100,
|
|
||||||
query: {
|
|
||||||
query_string: {
|
|
||||||
query: `(${searchDids}) AND (${transformChainIdsListToQuery(
|
|
||||||
chainIds
|
|
||||||
)})`,
|
|
||||||
fields: ['dataToken'],
|
|
||||||
default_operator: 'OR'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sort: { created: -1 }
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await queryMetadata(queryBookmarks, cancelToken)
|
const result = await getAssetsFromDidList(bookmarks, chainIds, cancelToken)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error(error.message)
|
Logger.error(error.message)
|
||||||
@ -88,7 +62,7 @@ export default function Bookmarks(): ReactElement {
|
|||||||
const { chainIds } = useUserPreferences()
|
const { chainIds } = useUserPreferences()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!appConfig.metadataCacheUri || bookmarks === []) return
|
if (!appConfig?.metadataCacheUri || bookmarks === []) return
|
||||||
|
|
||||||
const source = axios.CancelToken.source()
|
const source = axios.CancelToken.source()
|
||||||
|
|
||||||
@ -121,7 +95,7 @@ export default function Bookmarks(): ReactElement {
|
|||||||
return () => {
|
return () => {
|
||||||
source.cancel()
|
source.cancel()
|
||||||
}
|
}
|
||||||
}, [bookmarks, chainIds])
|
}, [bookmarks, chainIds, appConfig?.metadataCacheUri])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table
|
<Table
|
||||||
|
55
src/components/molecules/NumberUnit.module.css
Normal file
55
src/components/molecules/NumberUnit.module.css
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
.number,
|
||||||
|
.number * {
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
font-size: var(--font-size-h4);
|
||||||
|
color: var(--font-color-heading);
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip svg {
|
||||||
|
width: 0.8em !important;
|
||||||
|
height: 0.8em !important;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
38
src/components/molecules/NumberUnit.tsx
Normal file
38
src/components/molecules/NumberUnit.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import Markdown from '../atoms/Markdown'
|
||||||
|
import Tooltip from '../atoms/Tooltip'
|
||||||
|
import styles from './NumberUnit.module.css'
|
||||||
|
|
||||||
|
interface NumberUnitProps {
|
||||||
|
label: string
|
||||||
|
value: number | string | Element | ReactElement
|
||||||
|
small?: boolean
|
||||||
|
icon?: Element | ReactElement
|
||||||
|
tooltip?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NumberUnit({
|
||||||
|
small,
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
icon,
|
||||||
|
tooltip
|
||||||
|
}: NumberUnitProps): ReactElement {
|
||||||
|
return (
|
||||||
|
<div className={styles.unit}>
|
||||||
|
<div className={`${styles.number} ${small && styles.small}`}>
|
||||||
|
{icon && icon}
|
||||||
|
{value}
|
||||||
|
</div>
|
||||||
|
<span className={styles.label}>
|
||||||
|
{label}{' '}
|
||||||
|
{tooltip && (
|
||||||
|
<Tooltip
|
||||||
|
content={<Markdown text={tooltip} />}
|
||||||
|
className={styles.tooltip}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -1,28 +1,23 @@
|
|||||||
import React, { useState, useEffect, ReactElement } from 'react'
|
import React, { useState, useEffect, ReactElement } from 'react'
|
||||||
import { Datatoken, PoolTransaction } from '.'
|
import { PoolTransaction } from '.'
|
||||||
import { useUserPreferences } from '../../../providers/UserPreferences'
|
import { useUserPreferences } from '../../../providers/UserPreferences'
|
||||||
import ExplorerLink from '../../atoms/ExplorerLink'
|
import ExplorerLink from '../../atoms/ExplorerLink'
|
||||||
import { formatPrice } from '../../atoms/Price/PriceUnit'
|
import { formatPrice } from '../../atoms/Price/PriceUnit'
|
||||||
import styles from './Title.module.css'
|
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) {
|
async function getTitle(row: PoolTransaction, locale: string) {
|
||||||
let title = ''
|
let title = ''
|
||||||
switch (row.event) {
|
switch (row.event) {
|
||||||
case 'swap': {
|
case 'swap': {
|
||||||
const inToken = row.tokens.filter((x) => x.type === 'in')[0]
|
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 outToken = row.tokens.filter((x) => x.type === 'out')[0]
|
||||||
const outTokenSymbol = getSymbol(outToken.poolToken.tokenId)
|
const outTokenSymbol = outToken?.poolToken.symbol
|
||||||
title += `Swap ${formatPrice(
|
title += `Swap ${formatPrice(
|
||||||
Math.abs(inToken.value).toString(),
|
Math.abs(inToken?.value).toString(),
|
||||||
locale
|
locale
|
||||||
)}${inTokenSymbol} for ${formatPrice(
|
)}${inTokenSymbol} for ${formatPrice(
|
||||||
Math.abs(outToken.value).toString(),
|
Math.abs(outToken?.value).toString(),
|
||||||
locale
|
locale
|
||||||
)}${outTokenSymbol}`
|
)}${outTokenSymbol}`
|
||||||
|
|
||||||
@ -34,18 +29,18 @@ async function getTitle(row: PoolTransaction, locale: string) {
|
|||||||
x.tokenAddress.toLowerCase() !==
|
x.tokenAddress.toLowerCase() !==
|
||||||
row.poolAddress.datatokenAddress.toLowerCase()
|
row.poolAddress.datatokenAddress.toLowerCase()
|
||||||
)[0]
|
)[0]
|
||||||
const firstTokenSymbol = await getSymbol(firstToken.poolToken.tokenId)
|
const firstTokenSymbol = firstToken?.poolToken.symbol
|
||||||
const secondToken = row.tokens.filter(
|
const secondToken = row.tokens.filter(
|
||||||
(x) =>
|
(x) =>
|
||||||
x.tokenAddress.toLowerCase() ===
|
x.tokenAddress.toLowerCase() ===
|
||||||
row.poolAddress.datatokenAddress.toLowerCase()
|
row.poolAddress.datatokenAddress.toLowerCase()
|
||||||
)[0]
|
)[0]
|
||||||
const secondTokenSymbol = await getSymbol(secondToken.poolToken.tokenId)
|
const secondTokenSymbol = secondToken?.poolToken.symbol
|
||||||
title += `Create pool with ${formatPrice(
|
title += `Create pool with ${formatPrice(
|
||||||
Math.abs(firstToken.value).toString(),
|
Math.abs(firstToken?.value).toString(),
|
||||||
locale
|
locale
|
||||||
)}${firstTokenSymbol} and ${formatPrice(
|
)}${firstTokenSymbol} and ${formatPrice(
|
||||||
Math.abs(secondToken.value).toString(),
|
Math.abs(secondToken?.value).toString(),
|
||||||
locale
|
locale
|
||||||
)}${secondTokenSymbol}`
|
)}${secondTokenSymbol}`
|
||||||
break
|
break
|
||||||
@ -53,7 +48,7 @@ async function getTitle(row: PoolTransaction, locale: string) {
|
|||||||
case 'join':
|
case 'join':
|
||||||
case 'exit': {
|
case 'exit': {
|
||||||
for (let i = 0; i < row.tokens.length; i++) {
|
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'
|
if (i > 0) title += '\n'
|
||||||
title += `${row.event === 'join' ? 'Add' : 'Remove'} ${formatPrice(
|
title += `${row.event === 'join' ? 'Add' : 'Remove'} ${formatPrice(
|
||||||
Math.abs(row.tokens[i].value).toString(),
|
Math.abs(row.tokens[i].value).toString(),
|
||||||
@ -73,6 +68,7 @@ export default function Title({ row }: { row: PoolTransaction }): ReactElement {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!locale || !row) return
|
if (!locale || !row) return
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const title = await getTitle(row, locale)
|
const title = await getTitle(row, locale)
|
||||||
setTitle(title)
|
setTitle(title)
|
||||||
|
@ -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 Time from '../../atoms/Time'
|
||||||
import Table from '../../atoms/Table'
|
import Table from '../../atoms/Table'
|
||||||
import AssetTitle from '../AssetListTitle'
|
import AssetTitle from '../AssetListTitle'
|
||||||
@ -6,14 +6,14 @@ import { useUserPreferences } from '../../../providers/UserPreferences'
|
|||||||
import { gql } from 'urql'
|
import { gql } from 'urql'
|
||||||
import { TransactionHistory_poolTransactions as TransactionHistoryPoolTransactions } from '../../../@types/apollo/TransactionHistory'
|
import { TransactionHistory_poolTransactions as TransactionHistoryPoolTransactions } from '../../../@types/apollo/TransactionHistory'
|
||||||
import web3 from 'web3'
|
import web3 from 'web3'
|
||||||
import { useWeb3 } from '../../../providers/Web3'
|
|
||||||
import { fetchDataForMultipleChains } from '../../../utils/subgraph'
|
import { fetchDataForMultipleChains } from '../../../utils/subgraph'
|
||||||
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
||||||
import NetworkName from '../../atoms/NetworkName'
|
import NetworkName from '../../atoms/NetworkName'
|
||||||
import { retrieveDDO } from '../../../utils/aquarius'
|
import { retrieveDDO } from '../../../utils/aquarius'
|
||||||
import axios from 'axios'
|
import axios, { CancelToken } from 'axios'
|
||||||
import Title from './Title'
|
import Title from './Title'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
|
import { DDO, Logger } from '@oceanprotocol/lib'
|
||||||
|
|
||||||
const REFETCH_INTERVAL = 20000
|
const REFETCH_INTERVAL = 20000
|
||||||
|
|
||||||
@ -27,10 +27,12 @@ const txHistoryQueryByPool = gql`
|
|||||||
) {
|
) {
|
||||||
tokens {
|
tokens {
|
||||||
poolToken {
|
poolToken {
|
||||||
tokenId {
|
id
|
||||||
symbol
|
symbol
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
value
|
||||||
|
type
|
||||||
|
tokenAddress
|
||||||
}
|
}
|
||||||
tx
|
tx
|
||||||
event
|
event
|
||||||
@ -38,11 +40,6 @@ const txHistoryQueryByPool = gql`
|
|||||||
poolAddress {
|
poolAddress {
|
||||||
datatokenAddress
|
datatokenAddress
|
||||||
}
|
}
|
||||||
tokens {
|
|
||||||
value
|
|
||||||
type
|
|
||||||
tokenAddress
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@ -56,10 +53,12 @@ const txHistoryQuery = gql`
|
|||||||
) {
|
) {
|
||||||
tokens {
|
tokens {
|
||||||
poolToken {
|
poolToken {
|
||||||
tokenId {
|
id
|
||||||
symbol
|
symbol
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
value
|
||||||
|
type
|
||||||
|
tokenAddress
|
||||||
}
|
}
|
||||||
tx
|
tx
|
||||||
event
|
event
|
||||||
@ -67,11 +66,6 @@ const txHistoryQuery = gql`
|
|||||||
poolAddress {
|
poolAddress {
|
||||||
datatokenAddress
|
datatokenAddress
|
||||||
}
|
}
|
||||||
tokens {
|
|
||||||
value
|
|
||||||
type
|
|
||||||
tokenAddress
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@ -82,6 +76,7 @@ export interface Datatoken {
|
|||||||
|
|
||||||
export interface PoolTransaction extends TransactionHistoryPoolTransactions {
|
export interface PoolTransaction extends TransactionHistoryPoolTransactions {
|
||||||
networkId: number
|
networkId: number
|
||||||
|
ddo: DDO
|
||||||
}
|
}
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
@ -94,11 +89,7 @@ const columns = [
|
|||||||
{
|
{
|
||||||
name: 'Data Set',
|
name: 'Data Set',
|
||||||
selector: function getAssetRow(row: PoolTransaction) {
|
selector: function getAssetRow(row: PoolTransaction) {
|
||||||
const did = web3.utils
|
return <AssetTitle ddo={row.ddo} />
|
||||||
.toChecksumAddress(row.poolAddress.datatokenAddress)
|
|
||||||
.replace('0x', 'did:op:')
|
|
||||||
|
|
||||||
return <AssetTitle did={did} />
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -130,21 +121,22 @@ const columnsMinimal = [columns[0], columns[3]]
|
|||||||
export default function PoolTransactions({
|
export default function PoolTransactions({
|
||||||
poolAddress,
|
poolAddress,
|
||||||
poolChainId,
|
poolChainId,
|
||||||
minimal
|
minimal,
|
||||||
|
accountId
|
||||||
}: {
|
}: {
|
||||||
poolAddress?: string
|
poolAddress?: string
|
||||||
poolChainId?: number[]
|
poolChainId?: number[]
|
||||||
minimal?: boolean
|
minimal?: boolean
|
||||||
|
accountId: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { accountId } = useWeb3()
|
const [transactions, setTransactions] = useState<PoolTransaction[]>()
|
||||||
const [logs, setLogs] = useState<PoolTransaction[]>()
|
const [isLoading, setIsLoading] = useState<boolean>(true)
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false)
|
|
||||||
const { chainIds } = useUserPreferences()
|
const { chainIds } = useUserPreferences()
|
||||||
const { appConfig } = useSiteMetadata()
|
const { appConfig } = useSiteMetadata()
|
||||||
const [dataFetchInterval, setDataFetchInterval] = useState<NodeJS.Timeout>()
|
const [dataFetchInterval, setDataFetchInterval] = useState<NodeJS.Timeout>()
|
||||||
const [data, setData] = useState<PoolTransaction[]>()
|
const [data, setData] = useState<PoolTransaction[]>()
|
||||||
|
|
||||||
async function fetchPoolTransactionData() {
|
const getPoolTransactionData = useCallback(async () => {
|
||||||
const variables = {
|
const variables = {
|
||||||
user: accountId?.toLowerCase(),
|
user: accountId?.toLowerCase(),
|
||||||
pool: poolAddress?.toLowerCase()
|
pool: poolAddress?.toLowerCase()
|
||||||
@ -165,71 +157,94 @@ export default function PoolTransactions({
|
|||||||
if (JSON.stringify(data) !== JSON.stringify(transactions)) {
|
if (JSON.stringify(data) !== JSON.stringify(transactions)) {
|
||||||
setData(transactions)
|
setData(transactions)
|
||||||
}
|
}
|
||||||
}
|
}, [accountId, chainIds, data, poolAddress, poolChainId])
|
||||||
|
|
||||||
function refetchPoolTransactions() {
|
const getPoolTransactions = useCallback(
|
||||||
if (!dataFetchInterval) {
|
async (cancelToken: CancelToken) => {
|
||||||
setDataFetchInterval(
|
if (!data) return
|
||||||
setInterval(function () {
|
|
||||||
fetchPoolTransactionData()
|
const poolTransactions: PoolTransaction[] = []
|
||||||
}, REFETCH_INTERVAL)
|
|
||||||
|
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(() => {
|
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 () => {
|
return () => {
|
||||||
clearInterval(dataFetchInterval)
|
clearInterval(dataFetchInterval)
|
||||||
}
|
}
|
||||||
}, [dataFetchInterval])
|
}, [getPoolTransactionData, dataFetchInterval, appConfig.metadataCacheUri])
|
||||||
|
|
||||||
|
//
|
||||||
|
// Transform to final transactions
|
||||||
|
//
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!appConfig.metadataCacheUri) return
|
const cancelTokenSource = axios.CancelToken.source()
|
||||||
|
|
||||||
async function getTransactions() {
|
async function transformData() {
|
||||||
const poolTransactions: PoolTransaction[] = []
|
|
||||||
const source = axios.CancelToken.source()
|
|
||||||
try {
|
try {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
|
await getPoolTransactions(cancelTokenSource.token)
|
||||||
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()
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching pool transactions: ', error.message)
|
Logger.error('Error fetching pool transactions: ', error.message)
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getTransactions()
|
transformData()
|
||||||
}, [accountId, chainIds, appConfig.metadataCacheUri, poolAddress, data])
|
|
||||||
|
return () => {
|
||||||
|
cancelTokenSource.cancel()
|
||||||
|
}
|
||||||
|
}, [getPoolTransactions])
|
||||||
|
|
||||||
return accountId ? (
|
return accountId ? (
|
||||||
<Table
|
<Table
|
||||||
columns={minimal ? columnsMinimal : columns}
|
columns={minimal ? columnsMinimal : columns}
|
||||||
data={logs}
|
data={transactions}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
noTableHead={minimal}
|
noTableHead={minimal}
|
||||||
dense={minimal}
|
dense={minimal}
|
||||||
pagination={minimal ? logs?.length >= 4 : logs?.length >= 9}
|
pagination={
|
||||||
|
minimal ? transactions?.length >= 4 : transactions?.length >= 9
|
||||||
|
}
|
||||||
paginationPerPage={minimal ? 5 : 10}
|
paginationPerPage={minimal ? 5 : 10}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
@ -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 {
|
.address {
|
||||||
display: none;
|
display: none;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
border-right: 1px solid var(--border-color);
|
border-right: 1px solid var(--border-color);
|
||||||
padding-right: calc(var(--spacer) / 3);
|
padding-right: calc(var(--spacer) / 3);
|
||||||
|
padding-left: calc(var(--spacer) / 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 60rem) {
|
@media screen and (min-width: 60rem) {
|
||||||
|
@ -1,29 +1,15 @@
|
|||||||
import { toDataUrl } from 'ethereum-blockies'
|
|
||||||
import React, { FormEvent } from 'react'
|
import React, { FormEvent } from 'react'
|
||||||
import { ReactComponent as Caret } from '../../../images/caret.svg'
|
import { ReactComponent as Caret } from '../../../images/caret.svg'
|
||||||
import { accountTruncate } from '../../../utils/web3'
|
import { accountTruncate } from '../../../utils/web3'
|
||||||
import Loader from '../../atoms/Loader'
|
import Loader from '../../atoms/Loader'
|
||||||
import styles from './Account.module.css'
|
import styles from './Account.module.css'
|
||||||
import { useWeb3 } from '../../../providers/Web3'
|
import { useWeb3 } from '../../../providers/Web3'
|
||||||
|
import Blockies from '../../atoms/Blockies'
|
||||||
const Blockies = ({ account }: { account: string | undefined }) => {
|
|
||||||
if (!account) return null
|
|
||||||
const blockies = toDataUrl(account)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<img
|
|
||||||
className={styles.blockies}
|
|
||||||
src={blockies}
|
|
||||||
alt="Blockies"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Forward ref for Tippy.js
|
// Forward ref for Tippy.js
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const Account = React.forwardRef((props, ref: any) => {
|
const Account = React.forwardRef((props, ref: any) => {
|
||||||
const { accountId, web3Modal, connect } = useWeb3()
|
const { accountId, accountEns, web3Modal, connect } = useWeb3()
|
||||||
|
|
||||||
async function handleActivation(e: FormEvent<HTMLButtonElement>) {
|
async function handleActivation(e: FormEvent<HTMLButtonElement>) {
|
||||||
// prevent accidentially submitting a form the button might be in
|
// prevent accidentially submitting a form the button might be in
|
||||||
@ -44,9 +30,9 @@ const Account = React.forwardRef((props, ref: any) => {
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
onClick={(e) => e.preventDefault()}
|
onClick={(e) => e.preventDefault()}
|
||||||
>
|
>
|
||||||
<Blockies account={accountId} />
|
<Blockies accountId={accountId} />
|
||||||
<span className={styles.address} title={accountId}>
|
<span className={styles.address} title={accountId}>
|
||||||
{accountTruncate(accountId)}
|
{accountTruncate(accountEns || accountId)}
|
||||||
</span>
|
</span>
|
||||||
<Caret aria-hidden="true" className={styles.caret} />
|
<Caret aria-hidden="true" className={styles.caret} />
|
||||||
</button>
|
</button>
|
||||||
|
@ -38,11 +38,11 @@ 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/History/ComputeJobs'
|
import ComputeJobs from '../../../pages/Profile/History/ComputeJobs'
|
||||||
import { BestPrice } from '../../../../models/BestPrice'
|
import { BestPrice } from '../../../../models/BestPrice'
|
||||||
|
|
||||||
const SuccessAction = () => (
|
const SuccessAction = () => (
|
||||||
<Button style="text" to="/history?defaultTab=ComputeJobs" size="small">
|
<Button style="text" to="/profile?defaultTab=ComputeJobs" size="small">
|
||||||
Go to history →
|
Go to history →
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
@ -473,11 +473,7 @@ export default function Compute({
|
|||||||
</footer>
|
</footer>
|
||||||
{accountId && (
|
{accountId && (
|
||||||
<AssetActionHistoryTable title="Your Compute Jobs">
|
<AssetActionHistoryTable title="Your Compute Jobs">
|
||||||
<ComputeJobs
|
<ComputeJobs minimal />
|
||||||
minimal
|
|
||||||
assetDTAddress={ddo.dataTokenInfo.address}
|
|
||||||
chainId={ddo.chainId}
|
|
||||||
/>
|
|
||||||
</AssetActionHistoryTable>
|
</AssetActionHistoryTable>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -515,6 +515,7 @@ export default function Pool(): ReactElement {
|
|||||||
{accountId && (
|
{accountId && (
|
||||||
<AssetActionHistoryTable title="Your Pool Transactions">
|
<AssetActionHistoryTable title="Your Pool Transactions">
|
||||||
<PoolTransactions
|
<PoolTransactions
|
||||||
|
accountId={accountId}
|
||||||
poolAddress={price?.address}
|
poolAddress={price?.address}
|
||||||
poolChainId={[ddo.chainId]}
|
poolChainId={[ddo.chainId]}
|
||||||
minimal
|
minimal
|
||||||
|
@ -26,6 +26,7 @@ declare type AssetListProps = {
|
|||||||
isLoading?: boolean
|
isLoading?: boolean
|
||||||
onPageChange?: React.Dispatch<React.SetStateAction<number>>
|
onPageChange?: React.Dispatch<React.SetStateAction<number>>
|
||||||
className?: string
|
className?: string
|
||||||
|
noPublisher?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const AssetList: React.FC<AssetListProps> = ({
|
const AssetList: React.FC<AssetListProps> = ({
|
||||||
@ -35,7 +36,8 @@ const AssetList: React.FC<AssetListProps> = ({
|
|||||||
totalPages,
|
totalPages,
|
||||||
isLoading,
|
isLoading,
|
||||||
onPageChange,
|
onPageChange,
|
||||||
className
|
className,
|
||||||
|
noPublisher
|
||||||
}) => {
|
}) => {
|
||||||
const { chainIds } = useUserPreferences()
|
const { chainIds } = useUserPreferences()
|
||||||
const [assetsWithPrices, setAssetWithPrices] = useState<AssetListPrices[]>()
|
const [assetsWithPrices, setAssetWithPrices] = useState<AssetListPrices[]>()
|
||||||
@ -71,6 +73,7 @@ const AssetList: React.FC<AssetListProps> = ({
|
|||||||
ddo={assetWithPrice.ddo}
|
ddo={assetWithPrice.ddo}
|
||||||
price={assetWithPrice.price}
|
price={assetWithPrice.price}
|
||||||
key={assetWithPrice.ddo.id}
|
key={assetWithPrice.ddo.id}
|
||||||
|
noPublisher={noPublisher}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
) : chainIds.length === 0 ? (
|
) : chainIds.length === 0 ? (
|
||||||
|
@ -1,358 +0,0 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
|
||||||
import web3 from 'web3'
|
|
||||||
import Time from '../../../atoms/Time'
|
|
||||||
import { Link } from 'gatsby'
|
|
||||||
import { DDO, Logger, Service, Provider } from '@oceanprotocol/lib'
|
|
||||||
import { ComputeJobMetaData } from '../../../../@types/ComputeJobMetaData'
|
|
||||||
import Dotdotdot from 'react-dotdotdot'
|
|
||||||
import Table from '../../../atoms/Table'
|
|
||||||
import Button from '../../../atoms/Button'
|
|
||||||
import { useOcean } from '../../../../providers/Ocean'
|
|
||||||
import { gql } from 'urql'
|
|
||||||
import { useWeb3 } from '../../../../providers/Web3'
|
|
||||||
import {
|
|
||||||
queryMetadata,
|
|
||||||
transformChainIdsListToQuery
|
|
||||||
} from '../../../../utils/aquarius'
|
|
||||||
import axios, { CancelToken } from 'axios'
|
|
||||||
import Details from './Details'
|
|
||||||
import { ComputeJob } from '@oceanprotocol/lib/dist/node/ocean/interfaces/Compute'
|
|
||||||
import { ReactComponent as Refresh } from '../../../../images/refresh.svg'
|
|
||||||
import styles from './index.module.css'
|
|
||||||
import { useUserPreferences } from '../../../../providers/UserPreferences'
|
|
||||||
import { getOceanConfig } from '../../../../utils/ocean'
|
|
||||||
import { fetchDataForMultipleChains } from '../../../../utils/subgraph'
|
|
||||||
import {
|
|
||||||
OrdersData_tokenOrders as OrdersData,
|
|
||||||
OrdersData_tokenOrders_datatokenId as OrdersDatatoken
|
|
||||||
} from '../../../../@types/apollo/OrdersData'
|
|
||||||
import NetworkName from '../../../atoms/NetworkName'
|
|
||||||
|
|
||||||
const getComputeOrders = gql`
|
|
||||||
query ComputeOrders($user: String!) {
|
|
||||||
tokenOrders(
|
|
||||||
orderBy: timestamp
|
|
||||||
orderDirection: desc
|
|
||||||
where: { payer: $user }
|
|
||||||
) {
|
|
||||||
id
|
|
||||||
serviceId
|
|
||||||
datatokenId {
|
|
||||||
address
|
|
||||||
}
|
|
||||||
tx
|
|
||||||
timestamp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const getComputeOrdersByDatatokenAddress = gql`
|
|
||||||
query ComputeOrdersByDatatokenAddress(
|
|
||||||
$user: String!
|
|
||||||
$datatokenAddress: String!
|
|
||||||
) {
|
|
||||||
tokenOrders(
|
|
||||||
orderBy: timestamp
|
|
||||||
orderDirection: desc
|
|
||||||
where: { payer: $user, datatokenId: $datatokenAddress }
|
|
||||||
) {
|
|
||||||
id
|
|
||||||
serviceId
|
|
||||||
datatokenId {
|
|
||||||
address
|
|
||||||
}
|
|
||||||
tx
|
|
||||||
timestamp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
interface TokenOrder {
|
|
||||||
id: string
|
|
||||||
serviceId: number
|
|
||||||
datatokenId: OrdersDatatoken
|
|
||||||
tx: any | null
|
|
||||||
timestamp: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Status({ children }: { children: string }): ReactElement {
|
|
||||||
return <div className={styles.status}>{children}</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
name: 'Data Set',
|
|
||||||
selector: function getAssetRow(row: ComputeJobMetaData) {
|
|
||||||
return (
|
|
||||||
<Dotdotdot clamp={2}>
|
|
||||||
<Link to={`/asset/${row.inputDID[0]}`}>{row.assetName}</Link>
|
|
||||||
</Dotdotdot>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Network',
|
|
||||||
selector: function getNetwork(row: ComputeJobMetaData) {
|
|
||||||
return <NetworkName networkId={row.networkId} />
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Created',
|
|
||||||
selector: function getTimeRow(row: ComputeJobMetaData) {
|
|
||||||
return <Time date={row.dateCreated} isUnix relative />
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Finished',
|
|
||||||
selector: function getTimeRow(row: ComputeJobMetaData) {
|
|
||||||
return row.dateFinished ? (
|
|
||||||
<Time date={row.dateFinished} isUnix relative />
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Status',
|
|
||||||
selector: function getStatus(row: ComputeJobMetaData) {
|
|
||||||
return <Status>{row.statusText}</Status>
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Actions',
|
|
||||||
selector: function getActions(row: ComputeJobMetaData) {
|
|
||||||
return <Details job={row} />
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
async function getAssetMetadata(
|
|
||||||
queryDtList: string,
|
|
||||||
cancelToken: CancelToken,
|
|
||||||
chainIds: number[]
|
|
||||||
): Promise<DDO[]> {
|
|
||||||
const queryDid = {
|
|
||||||
page: 1,
|
|
||||||
offset: 100,
|
|
||||||
query: {
|
|
||||||
query_string: {
|
|
||||||
query: `(${queryDtList}) AND (${transformChainIdsListToQuery(
|
|
||||||
chainIds
|
|
||||||
)}) AND service.attributes.main.type:dataset AND service.type:compute`,
|
|
||||||
fields: ['dataToken']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await queryMetadata(queryDid, cancelToken)
|
|
||||||
return result.results
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ComputeJobs({
|
|
||||||
minimal,
|
|
||||||
assetDTAddress,
|
|
||||||
chainId
|
|
||||||
}: {
|
|
||||||
minimal?: boolean
|
|
||||||
assetDTAddress?: string
|
|
||||||
chainId?: number
|
|
||||||
}): ReactElement {
|
|
||||||
const { ocean, account, config, connect } = useOcean()
|
|
||||||
const { accountId, networkId } = useWeb3()
|
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
|
||||||
const { chainIds } = useUserPreferences()
|
|
||||||
const [jobs, setJobs] = useState<ComputeJobMetaData[]>([])
|
|
||||||
|
|
||||||
const columnsMinimal = [columns[4], columns[5], columns[3]]
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function initOcean() {
|
|
||||||
const oceanInitialConfig = getOceanConfig(networkId)
|
|
||||||
await connect(oceanInitialConfig)
|
|
||||||
}
|
|
||||||
if (ocean === undefined) {
|
|
||||||
initOcean()
|
|
||||||
}
|
|
||||||
}, [networkId, ocean])
|
|
||||||
|
|
||||||
async function getJobs() {
|
|
||||||
if (!accountId) return
|
|
||||||
setIsLoading(true)
|
|
||||||
const variables = assetDTAddress
|
|
||||||
? {
|
|
||||||
user: accountId?.toLowerCase(),
|
|
||||||
datatokenAddress: assetDTAddress.toLowerCase()
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
user: accountId?.toLowerCase()
|
|
||||||
}
|
|
||||||
const result = await fetchDataForMultipleChains(
|
|
||||||
assetDTAddress ? getComputeOrdersByDatatokenAddress : getComputeOrders,
|
|
||||||
variables,
|
|
||||||
assetDTAddress ? [chainId] : chainIds
|
|
||||||
)
|
|
||||||
let data: TokenOrder[] = []
|
|
||||||
for (let i = 0; i < result.length; i++) {
|
|
||||||
if (!result[i].tokenOrders) continue
|
|
||||||
result[i].tokenOrders.forEach((tokenOrder: TokenOrder) => {
|
|
||||||
data.push(tokenOrder)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (!ocean || !account || !data) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data = data.sort((a, b) => b.timestamp - a.timestamp)
|
|
||||||
const dtList = []
|
|
||||||
const computeJobs: ComputeJobMetaData[] = []
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
|
||||||
dtList.push(data[i].datatokenId.address)
|
|
||||||
}
|
|
||||||
const queryDtList = JSON.stringify(dtList)
|
|
||||||
.replace(/,/g, ' ')
|
|
||||||
.replace(/"/g, '')
|
|
||||||
.replace(/(\[|\])/g, '')
|
|
||||||
if (queryDtList === '') {
|
|
||||||
setJobs([])
|
|
||||||
setIsLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
setIsLoading(true)
|
|
||||||
const source = axios.CancelToken.source()
|
|
||||||
const assets = await getAssetMetadata(queryDtList, source.token, chainIds)
|
|
||||||
const providers: Provider[] = []
|
|
||||||
const serviceEndpoints: string[] = []
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
|
||||||
try {
|
|
||||||
const did = web3.utils
|
|
||||||
.toChecksumAddress(data[i].datatokenId.address)
|
|
||||||
.replace('0x', 'did:op:')
|
|
||||||
const ddo = assets.filter((x) => x.id === did)[0]
|
|
||||||
if (ddo === undefined) continue
|
|
||||||
|
|
||||||
const service = ddo.service.filter(
|
|
||||||
(x: Service) => x.index === data[i].serviceId
|
|
||||||
)[0]
|
|
||||||
|
|
||||||
if (!service || service.type !== 'compute') continue
|
|
||||||
const { serviceEndpoint } = service
|
|
||||||
|
|
||||||
const wasProviderQueried =
|
|
||||||
serviceEndpoints.filter((x) => x === serviceEndpoint).length > 0
|
|
||||||
|
|
||||||
if (wasProviderQueried) continue
|
|
||||||
serviceEndpoints.push(serviceEndpoint)
|
|
||||||
} catch (err) {
|
|
||||||
Logger.error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
setIsLoading(true)
|
|
||||||
for (let i = 0; i < serviceEndpoints.length; i++) {
|
|
||||||
const instanceConfig = {
|
|
||||||
config,
|
|
||||||
web3: config.web3Provider,
|
|
||||||
logger: Logger,
|
|
||||||
ocean: ocean
|
|
||||||
}
|
|
||||||
const provider = await Provider.getInstance(instanceConfig)
|
|
||||||
await provider.setBaseUrl(serviceEndpoints[i])
|
|
||||||
const hasSameCompute =
|
|
||||||
providers.filter(
|
|
||||||
(x) => x.computeAddress === provider.computeAddress
|
|
||||||
).length > 0
|
|
||||||
if (!hasSameCompute) providers.push(provider)
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
Logger.error(err)
|
|
||||||
}
|
|
||||||
for (let i = 0; i < providers.length; i++) {
|
|
||||||
try {
|
|
||||||
const providerComputeJobs = (await providers[i].computeStatus(
|
|
||||||
'',
|
|
||||||
account,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
false
|
|
||||||
)) as ComputeJob[]
|
|
||||||
|
|
||||||
// means the provider uri is not good, so we ignore it and move on
|
|
||||||
if (!providerComputeJobs) continue
|
|
||||||
providerComputeJobs.sort((a, b) => {
|
|
||||||
if (a.dateCreated > b.dateCreated) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if (a.dateCreated < b.dateCreated) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
})
|
|
||||||
|
|
||||||
for (let j = 0; j < providerComputeJobs.length; j++) {
|
|
||||||
const job = providerComputeJobs[j]
|
|
||||||
const did = job.inputDID[0]
|
|
||||||
const ddo = assets.filter((x) => x.id === did)[0]
|
|
||||||
|
|
||||||
if (!ddo) continue
|
|
||||||
const serviceMetadata = ddo.service.filter(
|
|
||||||
(x: Service) => x.type === 'metadata'
|
|
||||||
)[0]
|
|
||||||
|
|
||||||
const compJob: ComputeJobMetaData = {
|
|
||||||
...job,
|
|
||||||
assetName: serviceMetadata.attributes.main.name,
|
|
||||||
assetDtSymbol: ddo.dataTokenInfo.symbol,
|
|
||||||
networkId: ddo.chainId
|
|
||||||
}
|
|
||||||
computeJobs.push(compJob)
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
Logger.error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setJobs(computeJobs)
|
|
||||||
} catch (error) {
|
|
||||||
Logger.log(error.message)
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!chainIds || !accountId) {
|
|
||||||
setIsLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
getJobs()
|
|
||||||
}, [ocean, account, chainIds, accountId])
|
|
||||||
|
|
||||||
return accountId ? (
|
|
||||||
<>
|
|
||||||
{jobs.length <= 0 || minimal || (
|
|
||||||
<Button
|
|
||||||
style="text"
|
|
||||||
size="small"
|
|
||||||
title="Refresh compute jobs"
|
|
||||||
onClick={() => getJobs()}
|
|
||||||
disabled={isLoading}
|
|
||||||
className={styles.refresh}
|
|
||||||
>
|
|
||||||
<Refresh />
|
|
||||||
Refresh
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Table
|
|
||||||
columns={minimal ? columnsMinimal : columns}
|
|
||||||
data={jobs}
|
|
||||||
isLoading={isLoading}
|
|
||||||
defaultSortField="row.dateCreated"
|
|
||||||
defaultSortAsc={false}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<div>Please connect your Web3 wallet.</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,135 +0,0 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
|
||||||
import Table from '../../atoms/Table'
|
|
||||||
import { gql } from 'urql'
|
|
||||||
import Time from '../../atoms/Time'
|
|
||||||
import web3 from 'web3'
|
|
||||||
import AssetTitle from '../../molecules/AssetListTitle'
|
|
||||||
import { useWeb3 } from '../../../providers/Web3'
|
|
||||||
import axios from 'axios'
|
|
||||||
import { retrieveDDO } from '../../../utils/aquarius'
|
|
||||||
import { Logger } from '@oceanprotocol/lib'
|
|
||||||
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
|
||||||
import { useUserPreferences } from '../../../providers/UserPreferences'
|
|
||||||
import { fetchDataForMultipleChains } from '../../../utils/subgraph'
|
|
||||||
import { OrdersData_tokenOrders as OrdersData } from '../../../@types/apollo/OrdersData'
|
|
||||||
import NetworkName from '../../atoms/NetworkName'
|
|
||||||
|
|
||||||
const getTokenOrders = gql`
|
|
||||||
query OrdersData($user: String!) {
|
|
||||||
tokenOrders(
|
|
||||||
orderBy: timestamp
|
|
||||||
orderDirection: desc
|
|
||||||
where: { consumer: $user }
|
|
||||||
) {
|
|
||||||
datatokenId {
|
|
||||||
address
|
|
||||||
symbol
|
|
||||||
}
|
|
||||||
timestamp
|
|
||||||
tx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
interface DownloadedAssets {
|
|
||||||
did: string
|
|
||||||
dtSymbol: string
|
|
||||||
timestamp: number
|
|
||||||
networkId: number
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
name: 'Data Set',
|
|
||||||
selector: function getAssetRow(row: DownloadedAssets) {
|
|
||||||
return <AssetTitle did={row.did} />
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Network',
|
|
||||||
selector: function getNetwork(row: DownloadedAssets) {
|
|
||||||
return <NetworkName networkId={row.networkId} />
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Datatoken',
|
|
||||||
selector: function getTitleRow(row: DownloadedAssets) {
|
|
||||||
return row.dtSymbol
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Time',
|
|
||||||
selector: function getTimeRow(row: DownloadedAssets) {
|
|
||||||
return <Time date={row.timestamp.toString()} relative isUnix />
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
export default function ComputeDownloads(): ReactElement {
|
|
||||||
const { accountId } = useWeb3()
|
|
||||||
const { appConfig } = useSiteMetadata()
|
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
|
||||||
const [orders, setOrders] = useState<DownloadedAssets[]>()
|
|
||||||
const { chainIds } = useUserPreferences()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const variables = { user: accountId?.toLowerCase() }
|
|
||||||
|
|
||||||
async function filterAssets() {
|
|
||||||
const filteredOrders: DownloadedAssets[] = []
|
|
||||||
const source = axios.CancelToken.source()
|
|
||||||
try {
|
|
||||||
setIsLoading(true)
|
|
||||||
const response = await fetchDataForMultipleChains(
|
|
||||||
getTokenOrders,
|
|
||||||
variables,
|
|
||||||
chainIds
|
|
||||||
)
|
|
||||||
|
|
||||||
const data: OrdersData[] = []
|
|
||||||
for (let i = 0; i < response.length; i++) {
|
|
||||||
response[i].tokenOrders.forEach((tokenOrder: OrdersData) => {
|
|
||||||
data.push(tokenOrder)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
|
||||||
const did = web3.utils
|
|
||||||
.toChecksumAddress(data[i].datatokenId.address)
|
|
||||||
.replace('0x', 'did:op:')
|
|
||||||
const ddo = await retrieveDDO(did, source.token)
|
|
||||||
if (!ddo) continue
|
|
||||||
if (ddo.service[1].type === 'access') {
|
|
||||||
filteredOrders.push({
|
|
||||||
did: did,
|
|
||||||
networkId: ddo.chainId,
|
|
||||||
dtSymbol: data[i].datatokenId.symbol,
|
|
||||||
timestamp: data[i].timestamp
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const sortedOrders = filteredOrders.sort(
|
|
||||||
(a, b) => b.timestamp - a.timestamp
|
|
||||||
)
|
|
||||||
setOrders(sortedOrders)
|
|
||||||
} catch (err) {
|
|
||||||
Logger.log(err.message)
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filterAssets()
|
|
||||||
}, [accountId, appConfig.metadataCacheUri, chainIds])
|
|
||||||
|
|
||||||
return accountId ? (
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
data={orders}
|
|
||||||
paginationPerPage={10}
|
|
||||||
isLoading={isLoading}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div>Please connect your Web3 wallet.</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,269 +0,0 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
|
||||||
import Table from '../../atoms/Table'
|
|
||||||
import Conversion from '../../atoms/Price/Conversion'
|
|
||||||
import styles from './PoolShares.module.css'
|
|
||||||
import AssetTitle from '../../molecules/AssetListTitle'
|
|
||||||
import { gql } from 'urql'
|
|
||||||
import {
|
|
||||||
PoolShares as PoolSharesList,
|
|
||||||
PoolShares_poolShares as PoolShare,
|
|
||||||
PoolShares_poolShares_poolId_tokens as PoolSharePoolIdTokens
|
|
||||||
} from '../../../@types/apollo/PoolShares'
|
|
||||||
import web3 from 'web3'
|
|
||||||
import Token from '../../organisms/AssetActions/Pool/Token'
|
|
||||||
import { useWeb3 } from '../../../providers/Web3'
|
|
||||||
import { useUserPreferences } from '../../../providers/UserPreferences'
|
|
||||||
import { fetchDataForMultipleChains } from '../../../utils/subgraph'
|
|
||||||
import NetworkName from '../../atoms/NetworkName'
|
|
||||||
import axios from 'axios'
|
|
||||||
import { retrieveDDO } from '../../../utils/aquarius'
|
|
||||||
import { isValidNumber } from './../../../utils/numberValidations'
|
|
||||||
import Decimal from 'decimal.js'
|
|
||||||
|
|
||||||
Decimal.set({ toExpNeg: -18, precision: 18, rounding: 1 })
|
|
||||||
|
|
||||||
const REFETCH_INTERVAL = 20000
|
|
||||||
|
|
||||||
const poolSharesQuery = gql`
|
|
||||||
query PoolShares($user: String) {
|
|
||||||
poolShares(where: { userAddress: $user, balance_gt: 0.001 }, first: 1000) {
|
|
||||||
id
|
|
||||||
balance
|
|
||||||
userAddress {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
poolId {
|
|
||||||
id
|
|
||||||
datatokenAddress
|
|
||||||
valueLocked
|
|
||||||
tokens {
|
|
||||||
id
|
|
||||||
isDatatoken
|
|
||||||
symbol
|
|
||||||
}
|
|
||||||
oceanReserve
|
|
||||||
datatokenReserve
|
|
||||||
totalShares
|
|
||||||
consumePrice
|
|
||||||
spotPrice
|
|
||||||
createTime
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
interface Asset {
|
|
||||||
userLiquidity: number
|
|
||||||
poolShare: PoolShare
|
|
||||||
networkId: 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 findTokenByType(tokens: PoolSharePoolIdTokens[], type: string) {
|
|
||||||
const { symbol } = tokens.find((token) =>
|
|
||||||
type === 'datatoken'
|
|
||||||
? token.isDatatoken === true
|
|
||||||
: token.isDatatoken === false
|
|
||||||
)
|
|
||||||
return symbol
|
|
||||||
}
|
|
||||||
|
|
||||||
function Symbol({ tokens }: { tokens: PoolSharePoolIdTokens[] }) {
|
|
||||||
return <>{findTokenByType(tokens, 'datatoken')}</>
|
|
||||||
}
|
|
||||||
|
|
||||||
function Liquidity({ row, type }: { row: Asset; type: string }) {
|
|
||||||
let price = ``
|
|
||||||
let oceanTokenBalance = ''
|
|
||||||
let dataTokenBalance = ''
|
|
||||||
if (type === 'user') {
|
|
||||||
price = `${row.userLiquidity}`
|
|
||||||
const userShare = row.poolShare.balance / row.poolShare.poolId.totalShares
|
|
||||||
oceanTokenBalance = (
|
|
||||||
userShare * row.poolShare.poolId.oceanReserve
|
|
||||||
).toString()
|
|
||||||
dataTokenBalance = (
|
|
||||||
userShare * row.poolShare.poolId.datatokenReserve
|
|
||||||
).toString()
|
|
||||||
}
|
|
||||||
if (type === 'pool') {
|
|
||||||
price =
|
|
||||||
isValidNumber(row.poolShare.poolId.oceanReserve) &&
|
|
||||||
isValidNumber(row.poolShare.poolId.datatokenReserve) &&
|
|
||||||
isValidNumber(row.poolShare.poolId.consumePrice)
|
|
||||||
? new Decimal(row.poolShare.poolId.datatokenReserve)
|
|
||||||
.mul(new Decimal(row.poolShare.poolId.consumePrice))
|
|
||||||
.plus(row.poolShare.poolId.oceanReserve)
|
|
||||||
.toString()
|
|
||||||
: '0'
|
|
||||||
|
|
||||||
oceanTokenBalance = row.poolShare.poolId.oceanReserve.toString()
|
|
||||||
dataTokenBalance = row.poolShare.poolId.datatokenReserve.toString()
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className={styles.yourLiquidity}>
|
|
||||||
<Conversion
|
|
||||||
price={price}
|
|
||||||
className={styles.totalLiquidity}
|
|
||||||
hideApproximateSymbol
|
|
||||||
/>
|
|
||||||
<Token
|
|
||||||
symbol={findTokenByType(row.poolShare.poolId.tokens, 'ocean')}
|
|
||||||
balance={oceanTokenBalance}
|
|
||||||
noIcon
|
|
||||||
/>
|
|
||||||
<Token
|
|
||||||
symbol={findTokenByType(row.poolShare.poolId.tokens, 'datatoken')}
|
|
||||||
balance={dataTokenBalance}
|
|
||||||
noIcon
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
name: 'Data Set',
|
|
||||||
selector: function getAssetRow(row: Asset) {
|
|
||||||
const did = web3.utils
|
|
||||||
.toChecksumAddress(row.poolShare.poolId.datatokenAddress)
|
|
||||||
.replace('0x', 'did:op:')
|
|
||||||
return <AssetTitle did={did} />
|
|
||||||
},
|
|
||||||
grow: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Network',
|
|
||||||
selector: function getNetwork(row: Asset) {
|
|
||||||
return <NetworkName networkId={row.networkId} />
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Datatoken',
|
|
||||||
selector: function getSymbol(row: Asset) {
|
|
||||||
return <Symbol tokens={row.poolShare.poolId.tokens} />
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Your Liquidity',
|
|
||||||
selector: function getAssetRow(row: Asset) {
|
|
||||||
return <Liquidity row={row} type="user" />
|
|
||||||
},
|
|
||||||
right: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Pool Liquidity',
|
|
||||||
selector: function getAssetRow(row: Asset) {
|
|
||||||
return <Liquidity row={row} type="pool" />
|
|
||||||
},
|
|
||||||
right: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
export default function PoolShares(): ReactElement {
|
|
||||||
const { accountId } = useWeb3()
|
|
||||||
const [assets, setAssets] = useState<Asset[]>()
|
|
||||||
const [loading, setLoading] = useState<boolean>(false)
|
|
||||||
const [data, setData] = useState<PoolShare[]>()
|
|
||||||
const [dataFetchInterval, setDataFetchInterval] = useState<NodeJS.Timeout>()
|
|
||||||
const { chainIds } = useUserPreferences()
|
|
||||||
|
|
||||||
async function fetchPoolSharesData() {
|
|
||||||
const variables = { user: accountId?.toLowerCase() }
|
|
||||||
const shares: PoolShare[] = []
|
|
||||||
const result = await fetchDataForMultipleChains(
|
|
||||||
poolSharesQuery,
|
|
||||||
variables,
|
|
||||||
chainIds
|
|
||||||
)
|
|
||||||
for (let i = 0; i < result.length; i++) {
|
|
||||||
result[i].poolShares.forEach((poolShare: PoolShare) => {
|
|
||||||
shares.push(poolShare)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (JSON.stringify(data) !== JSON.stringify(shares)) {
|
|
||||||
setData(shares)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function refetchPoolShares() {
|
|
||||||
if (!dataFetchInterval) {
|
|
||||||
setDataFetchInterval(
|
|
||||||
setInterval(function () {
|
|
||||||
fetchPoolSharesData()
|
|
||||||
}, REFETCH_INTERVAL)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
clearInterval(dataFetchInterval)
|
|
||||||
}
|
|
||||||
}, [dataFetchInterval])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function getShares() {
|
|
||||||
const assetList: Asset[] = []
|
|
||||||
const source = axios.CancelToken.source()
|
|
||||||
|
|
||||||
try {
|
|
||||||
setLoading(true)
|
|
||||||
if (!data) {
|
|
||||||
await fetchPoolSharesData()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
|
||||||
const did = web3.utils
|
|
||||||
.toChecksumAddress(data[i].poolId.datatokenAddress)
|
|
||||||
.replace('0x', 'did:op:')
|
|
||||||
const ddo = await retrieveDDO(did, source.token)
|
|
||||||
const userLiquidity = calculateUserLiquidity(data[i])
|
|
||||||
assetList.push({
|
|
||||||
poolShare: data[i],
|
|
||||||
userLiquidity: userLiquidity,
|
|
||||||
networkId: ddo.chainId,
|
|
||||||
createTime: data[i].poolId.createTime
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const orderedAssets = assetList.sort(
|
|
||||||
(a, b) => b.createTime - a.createTime
|
|
||||||
)
|
|
||||||
setAssets(orderedAssets)
|
|
||||||
refetchPoolShares()
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching pool shares: ', error.message)
|
|
||||||
} finally {
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getShares()
|
|
||||||
}, [accountId, chainIds, data])
|
|
||||||
|
|
||||||
return accountId ? (
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
className={styles.poolSharesTable}
|
|
||||||
data={assets}
|
|
||||||
pagination
|
|
||||||
paginationPerPage={5}
|
|
||||||
isLoading={loading}
|
|
||||||
sortField="userLiquidity"
|
|
||||||
sortAsc={false}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div>Please connect your Web3 wallet.</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
import { Logger } from '@oceanprotocol/lib'
|
|
||||||
import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
|
||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
|
||||||
import AssetList from '../../organisms/AssetList'
|
|
||||||
import axios from 'axios'
|
|
||||||
import {
|
|
||||||
queryMetadata,
|
|
||||||
transformChainIdsListToQuery
|
|
||||||
} from '../../../utils/aquarius'
|
|
||||||
import { useWeb3 } from '../../../providers/Web3'
|
|
||||||
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
|
||||||
import { useUserPreferences } from '../../../providers/UserPreferences'
|
|
||||||
|
|
||||||
export default function PublishedList(): ReactElement {
|
|
||||||
const { accountId } = useWeb3()
|
|
||||||
const { appConfig } = useSiteMetadata()
|
|
||||||
const { chainIds } = useUserPreferences()
|
|
||||||
|
|
||||||
const [queryResult, setQueryResult] = useState<QueryResult>()
|
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
|
||||||
const [page, setPage] = useState<number>(1)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function getPublished() {
|
|
||||||
if (!accountId) return
|
|
||||||
const queryPublishedAssets = {
|
|
||||||
page: page,
|
|
||||||
offset: 9,
|
|
||||||
query: {
|
|
||||||
query_string: {
|
|
||||||
query: `(publicKey.owner:${accountId}) AND (${transformChainIdsListToQuery(
|
|
||||||
chainIds
|
|
||||||
)})`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sort: { created: -1 }
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const source = axios.CancelToken.source()
|
|
||||||
|
|
||||||
queryResult || setIsLoading(true)
|
|
||||||
const result = await queryMetadata(queryPublishedAssets, source.token)
|
|
||||||
setQueryResult(result)
|
|
||||||
} catch (error) {
|
|
||||||
Logger.error(error.message)
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getPublished()
|
|
||||||
}, [accountId, page, appConfig.metadataCacheUri, chainIds])
|
|
||||||
|
|
||||||
return accountId ? (
|
|
||||||
<AssetList
|
|
||||||
assets={queryResult?.results}
|
|
||||||
isLoading={isLoading}
|
|
||||||
showPagination
|
|
||||||
page={queryResult?.page}
|
|
||||||
totalPages={queryResult?.totalPages}
|
|
||||||
onPageChange={(newPage) => {
|
|
||||||
setPage(newPage)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div>Please connect your Web3 wallet.</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
import React, { ReactElement } from 'react'
|
|
||||||
import Tabs from '../../atoms/Tabs'
|
|
||||||
import PoolShares from './PoolShares'
|
|
||||||
import PoolTransactions from '../../molecules/PoolTransactions'
|
|
||||||
import PublishedList from './PublishedList'
|
|
||||||
import Downloads from './Downloads'
|
|
||||||
import ComputeJobs from './ComputeJobs'
|
|
||||||
import styles from './index.module.css'
|
|
||||||
import { useUserPreferences } from '../../../providers/UserPreferences'
|
|
||||||
import OceanProvider from '../../../providers/Ocean'
|
|
||||||
|
|
||||||
const tabs = [
|
|
||||||
{
|
|
||||||
title: 'Published',
|
|
||||||
content: <PublishedList />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Pool Shares',
|
|
||||||
content: <PoolShares />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Pool Transactions',
|
|
||||||
content: <PoolTransactions />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Downloads',
|
|
||||||
content: <Downloads />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Compute Jobs',
|
|
||||||
content: (
|
|
||||||
<OceanProvider>
|
|
||||||
<ComputeJobs />
|
|
||||||
</OceanProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
export default function HistoryPage(): ReactElement {
|
|
||||||
const { chainIds } = useUserPreferences()
|
|
||||||
const url = new URL(window.location.href)
|
|
||||||
const defaultTab = url.searchParams.get('defaultTab')
|
|
||||||
let defaultTabIndex = 0
|
|
||||||
defaultTab === 'ComputeJobs' ? (defaultTabIndex = 4) : (defaultTabIndex = 0)
|
|
||||||
return (
|
|
||||||
<article className={styles.content}>
|
|
||||||
<Tabs
|
|
||||||
items={tabs}
|
|
||||||
className={styles.tabs}
|
|
||||||
defaultIndex={defaultTabIndex}
|
|
||||||
/>
|
|
||||||
</article>
|
|
||||||
)
|
|
||||||
}
|
|
66
src/components/pages/Profile/Header/Account.module.css
Normal file
66
src/components/pages/Profile/Header/Account.module.css
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
.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,
|
||||||
|
.image {
|
||||||
|
width: 96px;
|
||||||
|
height: 96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imageWrap {
|
||||||
|
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);
|
||||||
|
}
|
67
src/components/pages/Profile/Header/Account.tsx
Normal file
67
src/components/pages/Profile/Header/Account.tsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import { useUserPreferences } from '../../../../providers/UserPreferences'
|
||||||
|
import ExplorerLink from '../../../atoms/ExplorerLink'
|
||||||
|
import NetworkName from '../../../atoms/NetworkName'
|
||||||
|
import jellyfish from '@oceanprotocol/art/creatures/jellyfish/jellyfish-grid.svg'
|
||||||
|
import Copy from '../../../atoms/Copy'
|
||||||
|
import Blockies from '../../../atoms/Blockies'
|
||||||
|
import styles from './Account.module.css'
|
||||||
|
import { useProfile } from '../../../../providers/Profile'
|
||||||
|
|
||||||
|
export default function Account({
|
||||||
|
accountId
|
||||||
|
}: {
|
||||||
|
accountId: string
|
||||||
|
}): ReactElement {
|
||||||
|
const { chainIds } = useUserPreferences()
|
||||||
|
const { profile } = useProfile()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.account}>
|
||||||
|
<figure className={styles.imageWrap}>
|
||||||
|
{profile?.image ? (
|
||||||
|
<img
|
||||||
|
src={profile?.image}
|
||||||
|
className={styles.image}
|
||||||
|
width="96"
|
||||||
|
height="96"
|
||||||
|
/>
|
||||||
|
) : accountId ? (
|
||||||
|
<Blockies accountId={accountId} className={styles.image} />
|
||||||
|
) : (
|
||||||
|
<img
|
||||||
|
src={jellyfish}
|
||||||
|
className={styles.image}
|
||||||
|
width="96"
|
||||||
|
height="96"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className={styles.name}>{profile?.name}</h3>
|
||||||
|
{accountId && (
|
||||||
|
<code
|
||||||
|
className={styles.accountId}
|
||||||
|
title={profile?.accountEns ? accountId : null}
|
||||||
|
>
|
||||||
|
{profile?.accountEns || 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>
|
||||||
|
)
|
||||||
|
}
|
@ -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,17 +1,27 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
|
import classNames from 'classnames/bind'
|
||||||
|
import { ReactComponent as External } from '../../../../images/external.svg'
|
||||||
import styles from './PublisherLinks.module.css'
|
import styles from './PublisherLinks.module.css'
|
||||||
import { ProfileLink } from '../../../models/Profile'
|
import { useProfile } from '../../../../providers/Profile'
|
||||||
import { ReactComponent as External } from '../../../images/external.svg'
|
|
||||||
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
export default function PublisherLinks({
|
export default function PublisherLinks({
|
||||||
links
|
className
|
||||||
}: {
|
}: {
|
||||||
links: ProfileLink[]
|
className: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
|
const { profile } = useProfile()
|
||||||
|
|
||||||
|
const styleClasses = cx({
|
||||||
|
links: true,
|
||||||
|
[className]: className
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.links}>
|
<div className={styleClasses}>
|
||||||
{' — '}
|
{' — '}
|
||||||
{links?.map((link: ProfileLink) => {
|
{profile?.links?.map((link) => {
|
||||||
const href =
|
const href =
|
||||||
link.name === 'Twitter'
|
link.name === 'Twitter'
|
||||||
? `https://twitter.com/${link.value}`
|
? `https://twitter.com/${link.value}`
|
6
src/components/pages/Profile/Header/Stats.module.css
Normal file
6
src/components/pages/Profile/Header/Stats.module.css
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.stats {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--spacer);
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
|
||||||
|
margin-top: var(--spacer);
|
||||||
|
}
|
124
src/components/pages/Profile/Header/Stats.tsx
Normal file
124
src/components/pages/Profile/Header/Stats.tsx
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import { Logger } from '@oceanprotocol/lib'
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { ReactElement } from 'react-markdown'
|
||||||
|
import { useUserPreferences } from '../../../../providers/UserPreferences'
|
||||||
|
import {
|
||||||
|
getAccountLiquidityInOwnAssets,
|
||||||
|
getAccountNumberOfOrders,
|
||||||
|
getAssetsBestPrices,
|
||||||
|
UserLiquidity,
|
||||||
|
calculateUserLiquidity
|
||||||
|
} from '../../../../utils/subgraph'
|
||||||
|
import Conversion from '../../../atoms/Price/Conversion'
|
||||||
|
import NumberUnit from '../../../molecules/NumberUnit'
|
||||||
|
import styles from './Stats.module.css'
|
||||||
|
import { useProfile } from '../../../../providers/Profile'
|
||||||
|
import { PoolShares_poolShares as PoolShare } from '../../../../@types/apollo/PoolShares'
|
||||||
|
|
||||||
|
async function getPoolSharesLiquidity(
|
||||||
|
poolShares: PoolShare[]
|
||||||
|
): Promise<number> {
|
||||||
|
let totalLiquidity = 0
|
||||||
|
|
||||||
|
for (const poolShare of poolShares) {
|
||||||
|
const poolLiquidity = calculateUserLiquidity(poolShare)
|
||||||
|
totalLiquidity += poolLiquidity
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalLiquidity
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Stats({
|
||||||
|
accountId
|
||||||
|
}: {
|
||||||
|
accountId: string
|
||||||
|
}): ReactElement {
|
||||||
|
const { chainIds } = useUserPreferences()
|
||||||
|
const { poolShares, assets, assetsTotal, downloadsTotal } = useProfile()
|
||||||
|
|
||||||
|
const [sold, setSold] = useState(0)
|
||||||
|
const [publisherLiquidity, setPublisherLiquidity] = useState<UserLiquidity>()
|
||||||
|
const [totalLiquidity, setTotalLiquidity] = useState(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!accountId) {
|
||||||
|
setSold(0)
|
||||||
|
setPublisherLiquidity({ price: '0', oceanBalance: '0' })
|
||||||
|
setTotalLiquidity(0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getSales() {
|
||||||
|
if (!assets) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const nrOrders = await getAccountNumberOfOrders(assets, chainIds)
|
||||||
|
setSold(nrOrders)
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getSales()
|
||||||
|
}, [accountId, assets])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!assets || !accountId || !chainIds) return
|
||||||
|
|
||||||
|
async function getPublisherLiquidity() {
|
||||||
|
try {
|
||||||
|
const accountPoolAdresses: string[] = []
|
||||||
|
const assetsPrices = await getAssetsBestPrices(assets)
|
||||||
|
for (const priceInfo of assetsPrices) {
|
||||||
|
if (priceInfo.price.type === 'pool') {
|
||||||
|
accountPoolAdresses.push(priceInfo.price.address.toLowerCase())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const userLiquidity = await getAccountLiquidityInOwnAssets(
|
||||||
|
accountId,
|
||||||
|
chainIds,
|
||||||
|
accountPoolAdresses
|
||||||
|
)
|
||||||
|
setPublisherLiquidity(userLiquidity)
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getPublisherLiquidity()
|
||||||
|
}, [assets, accountId])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!poolShares) return
|
||||||
|
|
||||||
|
async function getTotalLiquidity() {
|
||||||
|
try {
|
||||||
|
const totalLiquidity = await getPoolSharesLiquidity(poolShares)
|
||||||
|
setTotalLiquidity(totalLiquidity)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching pool shares: ', error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getTotalLiquidity()
|
||||||
|
}, [poolShares])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.stats}>
|
||||||
|
<NumberUnit
|
||||||
|
label="Liquidity in Own Assets"
|
||||||
|
value={
|
||||||
|
<Conversion price={publisherLiquidity?.price} hideApproximateSymbol />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<NumberUnit
|
||||||
|
label="Total Liquidity"
|
||||||
|
value={<Conversion price={`${totalLiquidity}`} hideApproximateSymbol />}
|
||||||
|
/>
|
||||||
|
<NumberUnit label={`Sale${sold === 1 ? '' : 's'}`} value={sold} />
|
||||||
|
<NumberUnit label="Published" value={assetsTotal} />
|
||||||
|
<NumberUnit
|
||||||
|
label={`Download${downloadsTotal === 1 ? '' : 's'}`}
|
||||||
|
tooltip="Datatoken orders for assets with `access` service, as opposed to `compute`. As one order could allow multiple or infinite downloads this number does not reflect the actual download count of an asset file."
|
||||||
|
value={downloadsTotal}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
54
src/components/pages/Profile/Header/index.module.css
Normal file
54
src/components/pages/Profile/Header/index.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);
|
||||||
|
}
|
75
src/components/pages/Profile/Header/index.tsx
Normal file
75
src/components/pages/Profile/Header/index.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import React, { ReactElement, useState } from 'react'
|
||||||
|
import PublisherLinks from './PublisherLinks'
|
||||||
|
import Markdown from '../../../atoms/Markdown'
|
||||||
|
import Stats from './Stats'
|
||||||
|
import Account from './Account'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
import { useProfile } from '../../../../providers/Profile'
|
||||||
|
|
||||||
|
const isDescriptionTextClamped = () => {
|
||||||
|
const el = document.getElementById('description')
|
||||||
|
if (el) return el.scrollHeight > el.clientHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
const LinkExternal = ({ url, text }: { url: string; text: string }) => {
|
||||||
|
return (
|
||||||
|
<a href={url} target="_blank" rel="noreferrer">
|
||||||
|
{text}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AccountHeader({
|
||||||
|
accountId
|
||||||
|
}: {
|
||||||
|
accountId: string
|
||||||
|
}): ReactElement {
|
||||||
|
const { profile } = useProfile()
|
||||||
|
const [isShowMore, setIsShowMore] = useState(false)
|
||||||
|
|
||||||
|
const toogleShowMore = () => {
|
||||||
|
setIsShowMore(!isShowMore)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.grid}>
|
||||||
|
<div>
|
||||||
|
<Account accountId={accountId} />
|
||||||
|
<Stats accountId={accountId} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Markdown text={profile?.description} className={styles.description} />
|
||||||
|
{isDescriptionTextClamped() ? (
|
||||||
|
<span className={styles.more} onClick={toogleShowMore}>
|
||||||
|
<LinkExternal
|
||||||
|
url={`https://www.3box.io/${accountId}`}
|
||||||
|
text="Read more on 3box"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
{profile?.links?.length > 0 && (
|
||||||
|
<PublisherLinks className={styles.publisherLinks} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={styles.meta}>
|
||||||
|
Profile data from{' '}
|
||||||
|
{profile?.accountEns && (
|
||||||
|
<>
|
||||||
|
<LinkExternal
|
||||||
|
url={`https://app.ens.domains/name/${profile.accountEns}`}
|
||||||
|
text="ENS"
|
||||||
|
/>{' '}
|
||||||
|
&{' '}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<LinkExternal
|
||||||
|
url={`https://www.3box.io/${accountId}`}
|
||||||
|
text="3Box Hub"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -7,7 +7,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.asset {
|
.asset {
|
||||||
composes: box from '../../../atoms/Box.module.css';
|
composes: box from '../../../../atoms/Box.module.css';
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
padding: calc(var(--spacer) / 2);
|
padding: calc(var(--spacer) / 2);
|
||||||
margin-bottom: calc(var(--spacer) / 2);
|
margin-bottom: calc(var(--spacer) / 2);
|
@ -1,16 +1,15 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { ComputeJobMetaData } from '../../../../@types/ComputeJobMetaData'
|
import { ComputeJobMetaData } from '../../../../../@types/ComputeJobMetaData'
|
||||||
import Time from '../../../atoms/Time'
|
import Time from '../../../../atoms/Time'
|
||||||
import Button from '../../../atoms/Button'
|
import Button from '../../../../atoms/Button'
|
||||||
import Modal from '../../../atoms/Modal'
|
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'
|
||||||
|
|
||||||
function Asset({
|
function Asset({
|
||||||
title,
|
title,
|
@ -1,12 +1,12 @@
|
|||||||
import { Logger } from '@oceanprotocol/lib'
|
import { Logger } from '@oceanprotocol/lib'
|
||||||
import React, { ReactElement, useState } from 'react'
|
import React, { ReactElement, useState } from 'react'
|
||||||
import Loader from '../../../atoms/Loader'
|
import Loader from '../../../../atoms/Loader'
|
||||||
import { ComputeJobMetaData } from '../../../../@types/ComputeJobMetaData'
|
import { ComputeJobMetaData } from '../../../../../@types/ComputeJobMetaData'
|
||||||
import { ListItem } from '../../../atoms/Lists'
|
import { ListItem } from '../../../../atoms/Lists'
|
||||||
import Button from '../../../atoms/Button'
|
import Button from '../../../../atoms/Button'
|
||||||
import { useOcean } from '../../../../providers/Ocean'
|
import { useOcean } from '../../../../../providers/Ocean'
|
||||||
import styles from './Results.module.css'
|
import styles from './Results.module.css'
|
||||||
import FormHelp from '../../../atoms/Input/Help'
|
import FormHelp from '../../../../atoms/Input/Help'
|
||||||
import { graphql, useStaticQuery } from 'gatsby'
|
import { graphql, useStaticQuery } from 'gatsby'
|
||||||
|
|
||||||
export const contentQuery = graphql`
|
export const contentQuery = graphql`
|
142
src/components/pages/Profile/History/ComputeJobs/index.tsx
Normal file
142
src/components/pages/Profile/History/ComputeJobs/index.tsx
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import React, { ReactElement, useEffect, useState, useCallback } from 'react'
|
||||||
|
import Time from '../../../../atoms/Time'
|
||||||
|
import { Link } from 'gatsby'
|
||||||
|
import { Logger } from '@oceanprotocol/lib'
|
||||||
|
import { ComputeJobMetaData } from '../../../../../@types/ComputeJobMetaData'
|
||||||
|
import Dotdotdot from 'react-dotdotdot'
|
||||||
|
import Table from '../../../../atoms/Table'
|
||||||
|
import Button from '../../../../atoms/Button'
|
||||||
|
import { useOcean } from '../../../../../providers/Ocean'
|
||||||
|
import { useWeb3 } from '../../../../../providers/Web3'
|
||||||
|
import Details from './Details'
|
||||||
|
import { ReactComponent as Refresh } from '../../../../../images/refresh.svg'
|
||||||
|
import { useUserPreferences } from '../../../../../providers/UserPreferences'
|
||||||
|
import { getOceanConfig } from '../../../../../utils/ocean'
|
||||||
|
import NetworkName from '../../../../atoms/NetworkName'
|
||||||
|
import { getComputeJobs } from '../../../../../utils/compute'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
import { useAsset } from '../../../../../providers/Asset'
|
||||||
|
|
||||||
|
export function Status({ children }: { children: string }): ReactElement {
|
||||||
|
return <div className={styles.status}>{children}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
name: 'Data Set',
|
||||||
|
selector: function getAssetRow(row: ComputeJobMetaData) {
|
||||||
|
return (
|
||||||
|
<Dotdotdot clamp={2}>
|
||||||
|
<Link to={`/asset/${row.inputDID[0]}`}>{row.assetName}</Link>
|
||||||
|
</Dotdotdot>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Network',
|
||||||
|
selector: function getNetwork(row: ComputeJobMetaData) {
|
||||||
|
return <NetworkName networkId={row.networkId} />
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Created',
|
||||||
|
selector: function getTimeRow(row: ComputeJobMetaData) {
|
||||||
|
return <Time date={row.dateCreated} isUnix relative />
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Finished',
|
||||||
|
selector: function getTimeRow(row: ComputeJobMetaData) {
|
||||||
|
return row.dateFinished ? (
|
||||||
|
<Time date={row.dateFinished} isUnix relative />
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Status',
|
||||||
|
selector: function getStatus(row: ComputeJobMetaData) {
|
||||||
|
return <Status>{row.statusText}</Status>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Actions',
|
||||||
|
selector: function getActions(row: ComputeJobMetaData) {
|
||||||
|
return <Details job={row} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function ComputeJobs({
|
||||||
|
minimal
|
||||||
|
}: {
|
||||||
|
minimal?: boolean
|
||||||
|
}): ReactElement {
|
||||||
|
const { ocean, account, config, connect } = useOcean()
|
||||||
|
const { accountId, networkId } = useWeb3()
|
||||||
|
const { ddo } = useAsset()
|
||||||
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
const { chainIds } = useUserPreferences()
|
||||||
|
const [jobs, setJobs] = useState<ComputeJobMetaData[]>([])
|
||||||
|
|
||||||
|
const columnsMinimal = [columns[4], columns[5], columns[3]]
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function initOcean() {
|
||||||
|
const oceanInitialConfig = getOceanConfig(networkId)
|
||||||
|
await connect(oceanInitialConfig)
|
||||||
|
}
|
||||||
|
if (ocean === undefined) {
|
||||||
|
initOcean()
|
||||||
|
}
|
||||||
|
}, [networkId, ocean, connect])
|
||||||
|
|
||||||
|
const fetchJobs = useCallback(async () => {
|
||||||
|
if (!chainIds || !accountId) {
|
||||||
|
setIsLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setIsLoading(true)
|
||||||
|
const jobs = await getComputeJobs(chainIds, config, ocean, account, ddo)
|
||||||
|
setJobs(jobs)
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error(error.message)
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
}, [account, accountId, chainIds, ddo, config, ocean])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchJobs()
|
||||||
|
}, [fetchJobs])
|
||||||
|
|
||||||
|
return accountId ? (
|
||||||
|
<>
|
||||||
|
{jobs?.length >= 0 && !minimal && (
|
||||||
|
<Button
|
||||||
|
style="text"
|
||||||
|
size="small"
|
||||||
|
title="Refresh compute jobs"
|
||||||
|
onClick={async () => await fetchJobs()}
|
||||||
|
disabled={isLoading}
|
||||||
|
className={styles.refresh}
|
||||||
|
>
|
||||||
|
<Refresh />
|
||||||
|
Refresh
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Table
|
||||||
|
columns={minimal ? columnsMinimal : columns}
|
||||||
|
data={jobs}
|
||||||
|
isLoading={isLoading}
|
||||||
|
defaultSortField="row.dateCreated"
|
||||||
|
defaultSortAsc={false}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div>Please connect your Web3 wallet.</div>
|
||||||
|
)
|
||||||
|
}
|
53
src/components/pages/Profile/History/Downloads.tsx
Normal file
53
src/components/pages/Profile/History/Downloads.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import Table from '../../../atoms/Table'
|
||||||
|
import Time from '../../../atoms/Time'
|
||||||
|
import AssetTitle from '../../../molecules/AssetListTitle'
|
||||||
|
import NetworkName from '../../../atoms/NetworkName'
|
||||||
|
import { useProfile } from '../../../../providers/Profile'
|
||||||
|
import { DownloadedAsset } from '../../../../utils/aquarius'
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
name: 'Data Set',
|
||||||
|
selector: function getAssetRow(row: DownloadedAsset) {
|
||||||
|
return <AssetTitle ddo={row.ddo} />
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Network',
|
||||||
|
selector: function getNetwork(row: DownloadedAsset) {
|
||||||
|
return <NetworkName networkId={row.networkId} />
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Datatoken',
|
||||||
|
selector: function getTitleRow(row: DownloadedAsset) {
|
||||||
|
return row.dtSymbol
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Time',
|
||||||
|
selector: function getTimeRow(row: DownloadedAsset) {
|
||||||
|
return <Time date={row.timestamp.toString()} relative isUnix />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function ComputeDownloads({
|
||||||
|
accountId
|
||||||
|
}: {
|
||||||
|
accountId: string
|
||||||
|
}): ReactElement {
|
||||||
|
const { downloads, isDownloadsLoading } = useProfile()
|
||||||
|
|
||||||
|
return accountId ? (
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
data={downloads}
|
||||||
|
paginationPerPage={10}
|
||||||
|
isLoading={isDownloadsLoading}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div>Please connect your Web3 wallet.</div>
|
||||||
|
)
|
||||||
|
}
|
@ -30,31 +30,31 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.yourLiquidity {
|
.userLiquidity {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yourLiquidity [class*='Conversion-module--'] {
|
.userLiquidity [class*='Conversion-module--'] {
|
||||||
margin-bottom: calc(var(--spacer) / 8);
|
margin-bottom: calc(var(--spacer) / 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
.yourLiquidity [class*='Conversion-module--'] strong {
|
.userLiquidity [class*='Conversion-module--'] strong {
|
||||||
font-size: var(--font-size-base);
|
font-size: var(--font-size-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
.yourLiquidity [class*='Token-module--token'] {
|
.userLiquidity [class*='Token-module--token'] {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
margin-bottom: calc(var(--spacer) / 8);
|
margin-bottom: calc(var(--spacer) / 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
.yourLiquidity [class*='Token-module--token'] div {
|
.userLiquidity [class*='Token-module--token'] div {
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
.yourLiquidity [class*='Token-module--icon'] {
|
.userLiquidity [class*='Token-module--icon'] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
217
src/components/pages/Profile/History/PoolShares.tsx
Normal file
217
src/components/pages/Profile/History/PoolShares.tsx
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
import React, { ReactElement, useCallback, useEffect, useState } from 'react'
|
||||||
|
import Table from '../../../atoms/Table'
|
||||||
|
import Conversion from '../../../atoms/Price/Conversion'
|
||||||
|
import styles from './PoolShares.module.css'
|
||||||
|
import AssetTitle from '../../../molecules/AssetListTitle'
|
||||||
|
import {
|
||||||
|
PoolShares_poolShares as PoolShare,
|
||||||
|
PoolShares_poolShares_poolId_tokens as PoolSharePoolIdTokens
|
||||||
|
} from '../../../../@types/apollo/PoolShares'
|
||||||
|
import web3 from 'web3'
|
||||||
|
import Token from '../../../organisms/AssetActions/Pool/Token'
|
||||||
|
import { calculateUserLiquidity } from '../../../../utils/subgraph'
|
||||||
|
import NetworkName from '../../../atoms/NetworkName'
|
||||||
|
import axios, { CancelToken } from 'axios'
|
||||||
|
import { retrieveDDO } from '../../../../utils/aquarius'
|
||||||
|
import { isValidNumber } from '../../../../utils/numberValidations'
|
||||||
|
import Decimal from 'decimal.js'
|
||||||
|
import { useProfile } from '../../../../providers/Profile'
|
||||||
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
|
|
||||||
|
Decimal.set({ toExpNeg: -18, precision: 18, rounding: 1 })
|
||||||
|
|
||||||
|
const REFETCH_INTERVAL = 20000
|
||||||
|
|
||||||
|
interface Asset {
|
||||||
|
userLiquidity: number
|
||||||
|
poolShare: PoolShare
|
||||||
|
networkId: number
|
||||||
|
createTime: number
|
||||||
|
ddo: DDO
|
||||||
|
}
|
||||||
|
|
||||||
|
function findTokenByType(tokens: PoolSharePoolIdTokens[], type: string) {
|
||||||
|
const { symbol } = tokens.find((token) =>
|
||||||
|
type === 'datatoken'
|
||||||
|
? token.isDatatoken === true
|
||||||
|
: token.isDatatoken === false
|
||||||
|
)
|
||||||
|
return symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
function Symbol({ tokens }: { tokens: PoolSharePoolIdTokens[] }) {
|
||||||
|
return <>{findTokenByType(tokens, 'datatoken')}</>
|
||||||
|
}
|
||||||
|
|
||||||
|
function Liquidity({ row, type }: { row: Asset; type: string }) {
|
||||||
|
let price = ``
|
||||||
|
let oceanTokenBalance = ''
|
||||||
|
let dataTokenBalance = ''
|
||||||
|
if (type === 'user') {
|
||||||
|
price = `${row.userLiquidity}`
|
||||||
|
const userShare = row.poolShare.balance / row.poolShare.poolId.totalShares
|
||||||
|
oceanTokenBalance = (
|
||||||
|
userShare * row.poolShare.poolId.oceanReserve
|
||||||
|
).toString()
|
||||||
|
dataTokenBalance = (
|
||||||
|
userShare * row.poolShare.poolId.datatokenReserve
|
||||||
|
).toString()
|
||||||
|
}
|
||||||
|
if (type === 'pool') {
|
||||||
|
price =
|
||||||
|
isValidNumber(row.poolShare.poolId.oceanReserve) &&
|
||||||
|
isValidNumber(row.poolShare.poolId.datatokenReserve) &&
|
||||||
|
isValidNumber(row.poolShare.poolId.consumePrice)
|
||||||
|
? new Decimal(row.poolShare.poolId.datatokenReserve)
|
||||||
|
.mul(new Decimal(row.poolShare.poolId.consumePrice))
|
||||||
|
.plus(row.poolShare.poolId.oceanReserve)
|
||||||
|
.toString()
|
||||||
|
: '0'
|
||||||
|
|
||||||
|
oceanTokenBalance = row.poolShare.poolId.oceanReserve.toString()
|
||||||
|
dataTokenBalance = row.poolShare.poolId.datatokenReserve.toString()
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={styles.userLiquidity}>
|
||||||
|
<Conversion
|
||||||
|
price={price}
|
||||||
|
className={styles.totalLiquidity}
|
||||||
|
hideApproximateSymbol
|
||||||
|
/>
|
||||||
|
<Token
|
||||||
|
symbol={findTokenByType(row.poolShare.poolId.tokens, 'ocean')}
|
||||||
|
balance={oceanTokenBalance}
|
||||||
|
noIcon
|
||||||
|
/>
|
||||||
|
<Token
|
||||||
|
symbol={findTokenByType(row.poolShare.poolId.tokens, 'datatoken')}
|
||||||
|
balance={dataTokenBalance}
|
||||||
|
noIcon
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
name: 'Data Set',
|
||||||
|
selector: function getAssetRow(row: Asset) {
|
||||||
|
return <AssetTitle ddo={row.ddo} />
|
||||||
|
},
|
||||||
|
grow: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Network',
|
||||||
|
selector: function getNetwork(row: Asset) {
|
||||||
|
return <NetworkName networkId={row.networkId} />
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Datatoken',
|
||||||
|
selector: function getSymbol(row: Asset) {
|
||||||
|
return <Symbol tokens={row.poolShare.poolId.tokens} />
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Liquidity',
|
||||||
|
selector: function getAssetRow(row: Asset) {
|
||||||
|
return <Liquidity row={row} type="user" />
|
||||||
|
},
|
||||||
|
right: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Pool Liquidity',
|
||||||
|
selector: function getAssetRow(row: Asset) {
|
||||||
|
return <Liquidity row={row} type="pool" />
|
||||||
|
},
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
async function getPoolSharesAssets(
|
||||||
|
data: PoolShare[],
|
||||||
|
cancelToken: CancelToken
|
||||||
|
) {
|
||||||
|
const assetList: Asset[] = []
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
const did = web3.utils
|
||||||
|
.toChecksumAddress(data[i].poolId.datatokenAddress)
|
||||||
|
.replace('0x', 'did:op:')
|
||||||
|
const ddo = await retrieveDDO(did, cancelToken)
|
||||||
|
const userLiquidity = calculateUserLiquidity(data[i])
|
||||||
|
|
||||||
|
ddo &&
|
||||||
|
assetList.push({
|
||||||
|
poolShare: data[i],
|
||||||
|
userLiquidity: userLiquidity,
|
||||||
|
networkId: ddo?.chainId,
|
||||||
|
createTime: data[i].poolId.createTime,
|
||||||
|
ddo
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const assets = assetList.sort((a, b) => b.createTime - a.createTime)
|
||||||
|
return assets
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PoolShares({
|
||||||
|
accountId
|
||||||
|
}: {
|
||||||
|
accountId: string
|
||||||
|
}): ReactElement {
|
||||||
|
const { poolShares, isPoolSharesLoading } = useProfile()
|
||||||
|
const [assets, setAssets] = useState<Asset[]>()
|
||||||
|
const [loading, setLoading] = useState<boolean>(false)
|
||||||
|
const [dataFetchInterval, setDataFetchInterval] = useState<NodeJS.Timeout>()
|
||||||
|
|
||||||
|
const fetchPoolSharesAssets = useCallback(
|
||||||
|
async (cancelToken: CancelToken) => {
|
||||||
|
if (!poolShares || isPoolSharesLoading) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const assets = await getPoolSharesAssets(poolShares, cancelToken)
|
||||||
|
setAssets(assets)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching pool shares: ', error.message)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[poolShares, isPoolSharesLoading]
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const cancelTokenSource = axios.CancelToken.source()
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
setLoading(true)
|
||||||
|
await fetchPoolSharesAssets(cancelTokenSource.token)
|
||||||
|
setLoading(false)
|
||||||
|
|
||||||
|
if (dataFetchInterval) return
|
||||||
|
const interval = setInterval(async () => {
|
||||||
|
await fetchPoolSharesAssets(cancelTokenSource.token)
|
||||||
|
}, REFETCH_INTERVAL)
|
||||||
|
setDataFetchInterval(interval)
|
||||||
|
}
|
||||||
|
init()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearInterval(dataFetchInterval)
|
||||||
|
cancelTokenSource.cancel()
|
||||||
|
}
|
||||||
|
}, [dataFetchInterval, fetchPoolSharesAssets])
|
||||||
|
|
||||||
|
return accountId ? (
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
className={styles.poolSharesTable}
|
||||||
|
data={assets}
|
||||||
|
pagination
|
||||||
|
paginationPerPage={5}
|
||||||
|
isLoading={loading}
|
||||||
|
sortField="userLiquidity"
|
||||||
|
sortAsc={false}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div>Please connect your Web3 wallet.</div>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
.filters {
|
||||||
|
margin-top: -1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assets {
|
||||||
|
margin-top: calc(var(--spacer) / 3);
|
||||||
|
}
|
77
src/components/pages/Profile/History/PublishedList.tsx
Normal file
77
src/components/pages/Profile/History/PublishedList.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { Logger } from '@oceanprotocol/lib'
|
||||||
|
import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
||||||
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
|
import AssetList from '../../../organisms/AssetList'
|
||||||
|
import { getPublishedAssets } from '../../../../utils/aquarius'
|
||||||
|
// import Filters from '../../../templates/Search/Filters'
|
||||||
|
import { useSiteMetadata } from '../../../../hooks/useSiteMetadata'
|
||||||
|
import { useUserPreferences } from '../../../../providers/UserPreferences'
|
||||||
|
import styles from './PublishedList.module.css'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
export default function PublishedList({
|
||||||
|
accountId
|
||||||
|
}: {
|
||||||
|
accountId: string
|
||||||
|
}): ReactElement {
|
||||||
|
const { appConfig } = useSiteMetadata()
|
||||||
|
const { chainIds } = useUserPreferences()
|
||||||
|
|
||||||
|
const [queryResult, setQueryResult] = useState<QueryResult>()
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const [page, setPage] = useState<number>(1)
|
||||||
|
const [service, setServiceType] = useState('dataset OR algorithm')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!accountId) return
|
||||||
|
|
||||||
|
const cancelTokenSource = axios.CancelToken.source()
|
||||||
|
|
||||||
|
async function getPublished() {
|
||||||
|
try {
|
||||||
|
setIsLoading(true)
|
||||||
|
const result = await getPublishedAssets(
|
||||||
|
accountId,
|
||||||
|
chainIds,
|
||||||
|
cancelTokenSource.token,
|
||||||
|
page,
|
||||||
|
service
|
||||||
|
)
|
||||||
|
setQueryResult(result)
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error(error.message)
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getPublished()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelTokenSource.cancel()
|
||||||
|
}
|
||||||
|
}, [accountId, page, appConfig.metadataCacheUri, chainIds, service])
|
||||||
|
|
||||||
|
return accountId ? (
|
||||||
|
<>
|
||||||
|
{/* <Filters
|
||||||
|
serviceType={service}
|
||||||
|
setServiceType={setServiceType}
|
||||||
|
className={styles.filters}
|
||||||
|
/> */}
|
||||||
|
<AssetList
|
||||||
|
assets={queryResult?.results}
|
||||||
|
isLoading={isLoading}
|
||||||
|
showPagination
|
||||||
|
page={queryResult?.page}
|
||||||
|
totalPages={queryResult?.totalPages}
|
||||||
|
onPageChange={(newPage) => {
|
||||||
|
setPage(newPage)
|
||||||
|
}}
|
||||||
|
className={styles.assets}
|
||||||
|
noPublisher
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div>Please connect your Web3 wallet.</div>
|
||||||
|
)
|
||||||
|
}
|
@ -16,7 +16,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.tabs {
|
||||||
margin-top: var(--spacer);
|
margin-top: var(--spacer);
|
||||||
background-color: var(--background-body);
|
background-color: var(--background-body);
|
||||||
}
|
}
|
69
src/components/pages/Profile/History/index.tsx
Normal file
69
src/components/pages/Profile/History/index.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import Tabs from '../../../atoms/Tabs'
|
||||||
|
import PoolShares from './PoolShares'
|
||||||
|
import PoolTransactions from '../../../molecules/PoolTransactions'
|
||||||
|
import PublishedList from './PublishedList'
|
||||||
|
import Downloads from './Downloads'
|
||||||
|
import ComputeJobs from './ComputeJobs'
|
||||||
|
import { useLocation } from '@reach/router'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
import OceanProvider from '../../../../providers/Ocean'
|
||||||
|
import { useWeb3 } from '../../../../providers/Web3'
|
||||||
|
|
||||||
|
interface HistoryTab {
|
||||||
|
title: string
|
||||||
|
content: JSX.Element
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTabs(accountId: string, userAccountId: string): HistoryTab[] {
|
||||||
|
const defaultTabs: HistoryTab[] = [
|
||||||
|
{
|
||||||
|
title: 'Published',
|
||||||
|
content: <PublishedList accountId={accountId} />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Pool Shares',
|
||||||
|
content: <PoolShares accountId={accountId} />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Pool Transactions',
|
||||||
|
content: <PoolTransactions accountId={accountId} />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Downloads',
|
||||||
|
content: <Downloads accountId={accountId} />
|
||||||
|
}
|
||||||
|
]
|
||||||
|
const computeTab: HistoryTab = {
|
||||||
|
title: 'Compute Jobs',
|
||||||
|
content: (
|
||||||
|
<OceanProvider>
|
||||||
|
<ComputeJobs />
|
||||||
|
</OceanProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (accountId === userAccountId) {
|
||||||
|
defaultTabs.push(computeTab)
|
||||||
|
}
|
||||||
|
return defaultTabs
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function HistoryPage({
|
||||||
|
accountIdentifier
|
||||||
|
}: {
|
||||||
|
accountIdentifier: string
|
||||||
|
}): ReactElement {
|
||||||
|
const { accountId } = useWeb3()
|
||||||
|
const location = useLocation()
|
||||||
|
|
||||||
|
const url = new URL(location.href)
|
||||||
|
const defaultTab = url.searchParams.get('defaultTab')
|
||||||
|
const tabs = getTabs(accountIdentifier, accountId)
|
||||||
|
|
||||||
|
let defaultTabIndex = 0
|
||||||
|
defaultTab === 'ComputeJobs' ? (defaultTabIndex = 4) : (defaultTabIndex = 0)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tabs items={tabs} className={styles.tabs} defaultIndex={defaultTabIndex} />
|
||||||
|
)
|
||||||
|
}
|
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
|
||||||
|
@ -50,10 +50,6 @@ button.filter,
|
|||||||
color: var(--background-body);
|
color: var(--background-body);
|
||||||
}
|
}
|
||||||
|
|
||||||
.filterList:first-of-type {
|
|
||||||
margin-bottom: calc(var(--spacer) / 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
.showClear:hover {
|
.showClear:hover {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
@ -1,6 +1,5 @@
|
|||||||
import React, { ReactElement, useState } from 'react'
|
import React, { ReactElement, useState } from 'react'
|
||||||
import { useNavigate } from '@reach/router'
|
import { useNavigate } from '@reach/router'
|
||||||
import styles from './filterService.module.css'
|
|
||||||
import classNames from 'classnames/bind'
|
import classNames from 'classnames/bind'
|
||||||
import {
|
import {
|
||||||
addExistingParamsToUrl,
|
addExistingParamsToUrl,
|
||||||
@ -8,6 +7,7 @@ import {
|
|||||||
FilterByTypeOptions
|
FilterByTypeOptions
|
||||||
} from './utils'
|
} from './utils'
|
||||||
import Button from '../../atoms/Button'
|
import Button from '../../atoms/Button'
|
||||||
|
import styles from './Filters.module.css'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
|
|
@ -2,15 +2,15 @@ import React, { ReactElement, useState, useEffect } from 'react'
|
|||||||
import Permission from '../../organisms/Permission'
|
import Permission from '../../organisms/Permission'
|
||||||
import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
import { QueryResult } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
||||||
import AssetList from '../../organisms/AssetList'
|
import AssetList from '../../organisms/AssetList'
|
||||||
import styles from './index.module.css'
|
|
||||||
import queryString from 'query-string'
|
import queryString from 'query-string'
|
||||||
import ServiceFilter from './filterService'
|
import Filters from './Filters'
|
||||||
import Sort from './sort'
|
import Sort from './sort'
|
||||||
import { getResults } from './utils'
|
import { getResults } from './utils'
|
||||||
import { navigate } from 'gatsby'
|
import { navigate } from 'gatsby'
|
||||||
import { updateQueryStringParameter } from '../../../utils'
|
import { updateQueryStringParameter } from '../../../utils'
|
||||||
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
||||||
import { useUserPreferences } from '../../../providers/UserPreferences'
|
import { useUserPreferences } from '../../../providers/UserPreferences'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
|
||||||
export default function SearchPage({
|
export default function SearchPage({
|
||||||
location,
|
location,
|
||||||
@ -78,7 +78,7 @@ export default function SearchPage({
|
|||||||
<>
|
<>
|
||||||
<div className={styles.search}>
|
<div className={styles.search}>
|
||||||
<div className={styles.row}>
|
<div className={styles.row}>
|
||||||
<ServiceFilter
|
<Filters
|
||||||
serviceType={service}
|
serviceType={service}
|
||||||
accessType={access}
|
accessType={access}
|
||||||
setServiceType={setServiceType}
|
setServiceType={setServiceType}
|
||||||
|
@ -4,8 +4,9 @@ export interface ProfileLink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Profile {
|
export interface Profile {
|
||||||
did: string
|
did?: string
|
||||||
name?: string
|
name?: string
|
||||||
|
accountEns?: string
|
||||||
description?: string
|
description?: string
|
||||||
emoji?: string
|
emoji?: string
|
||||||
image?: string
|
image?: string
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
import React, { ReactElement } from 'react'
|
|
||||||
import PageHistory from '../components/pages/History'
|
|
||||||
import Page from '../components/templates/Page'
|
|
||||||
import { graphql, PageProps } from 'gatsby'
|
|
||||||
|
|
||||||
export default function PageGatsbyHistory(props: PageProps): ReactElement {
|
|
||||||
const content = (props.data as any).content.edges[0].node.childPagesJson
|
|
||||||
const { title, description } = content
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Page title={title} description={description} uri={props.uri}>
|
|
||||||
<PageHistory />
|
|
||||||
</Page>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const contentQuery = graphql`
|
|
||||||
query HistoryPageQuery {
|
|
||||||
content: allFile(filter: { relativePath: { eq: "pages/history.json" } }) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
childPagesJson {
|
|
||||||
title
|
|
||||||
description
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
80
src/pages/profile/index.tsx
Normal file
80
src/pages/profile/index.tsx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
|
import Page from '../../components/templates/Page'
|
||||||
|
import { graphql, PageProps, navigate } from 'gatsby'
|
||||||
|
import ProfilePage from '../../components/pages/Profile'
|
||||||
|
import { accountTruncate } from '../../utils/web3'
|
||||||
|
import { useWeb3 } from '../../providers/Web3'
|
||||||
|
import ProfileProvider from '../../providers/Profile'
|
||||||
|
import { getEnsAddress, getEnsName } from '../../utils/ens'
|
||||||
|
import ethereumAddress from 'ethereum-address'
|
||||||
|
|
||||||
|
export default function PageGatsbyProfile(props: PageProps): ReactElement {
|
||||||
|
const { accountId, accountEns } = useWeb3()
|
||||||
|
const [finalAccountId, setFinalAccountId] = useState<string>()
|
||||||
|
const [finalAccountEns, setFinalAccountEns] = useState<string>()
|
||||||
|
|
||||||
|
// Have accountId in path take over, if not present fall back to web3
|
||||||
|
useEffect(() => {
|
||||||
|
async function init() {
|
||||||
|
if (!props?.location?.pathname) return
|
||||||
|
|
||||||
|
// Path is root /profile, have web3 take over
|
||||||
|
if (props.location.pathname === '/profile') {
|
||||||
|
setFinalAccountEns(accountEns)
|
||||||
|
setFinalAccountId(accountId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const pathAccount = props.location.pathname.split('/')[2]
|
||||||
|
|
||||||
|
// Path has ETH addreess
|
||||||
|
if (ethereumAddress.isAddress(pathAccount)) {
|
||||||
|
const finalAccountId = pathAccount || accountId
|
||||||
|
setFinalAccountId(finalAccountId)
|
||||||
|
|
||||||
|
const accountEns = await getEnsName(finalAccountId)
|
||||||
|
if (!accountEns) return
|
||||||
|
setFinalAccountEns(accountEns)
|
||||||
|
} else {
|
||||||
|
// Path has ENS name
|
||||||
|
setFinalAccountEns(pathAccount)
|
||||||
|
const resolvedAccountId = await getEnsAddress(pathAccount)
|
||||||
|
setFinalAccountId(resolvedAccountId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init()
|
||||||
|
}, [props.location.pathname, accountId, accountEns])
|
||||||
|
|
||||||
|
// Replace pathname with ENS name if present
|
||||||
|
useEffect(() => {
|
||||||
|
if (!finalAccountEns || props.location.pathname === '/profile') return
|
||||||
|
|
||||||
|
const newProfilePath = `/profile/${finalAccountEns}`
|
||||||
|
// make sure we only replace path once
|
||||||
|
if (newProfilePath !== props.location.pathname)
|
||||||
|
navigate(newProfilePath, { replace: true })
|
||||||
|
}, [props.location, finalAccountEns, accountId])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page uri={props.uri} title={accountTruncate(finalAccountId)} noPageHeader>
|
||||||
|
<ProfileProvider accountId={finalAccountId} accountEns={finalAccountEns}>
|
||||||
|
<ProfilePage accountId={finalAccountId} />
|
||||||
|
</ProfileProvider>
|
||||||
|
</Page>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const contentQuery = graphql`
|
||||||
|
query ProfilePageQuery {
|
||||||
|
content: allFile(filter: { relativePath: { eq: "pages/profile.json" } }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
childPagesJson {
|
||||||
|
title
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
299
src/providers/Profile.tsx
Normal file
299
src/providers/Profile.tsx
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
import React, {
|
||||||
|
useContext,
|
||||||
|
useState,
|
||||||
|
useEffect,
|
||||||
|
createContext,
|
||||||
|
ReactElement,
|
||||||
|
useCallback,
|
||||||
|
ReactNode
|
||||||
|
} from 'react'
|
||||||
|
import { getPoolSharesData, getUserTokenOrders } from '../utils/subgraph'
|
||||||
|
import { useUserPreferences } from './UserPreferences'
|
||||||
|
import { PoolShares_poolShares as PoolShare } from '../@types/apollo/PoolShares'
|
||||||
|
import { DDO, Logger } from '@oceanprotocol/lib'
|
||||||
|
import {
|
||||||
|
DownloadedAsset,
|
||||||
|
getDownloadAssets,
|
||||||
|
getPublishedAssets
|
||||||
|
} from '../utils/aquarius'
|
||||||
|
import { useSiteMetadata } from '../hooks/useSiteMetadata'
|
||||||
|
import { Profile } from '../models/Profile'
|
||||||
|
import { accountTruncate } from '../utils/web3'
|
||||||
|
import axios, { CancelToken } from 'axios'
|
||||||
|
import ethereumAddress from 'ethereum-address'
|
||||||
|
import get3BoxProfile from '../utils/profile'
|
||||||
|
import web3 from 'web3'
|
||||||
|
|
||||||
|
interface ProfileProviderValue {
|
||||||
|
profile: Profile
|
||||||
|
poolShares: PoolShare[]
|
||||||
|
isPoolSharesLoading: boolean
|
||||||
|
assets: DDO[]
|
||||||
|
assetsTotal: number
|
||||||
|
isEthAddress: boolean
|
||||||
|
downloads: DownloadedAsset[]
|
||||||
|
downloadsTotal: number
|
||||||
|
isDownloadsLoading: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProfileContext = createContext({} as ProfileProviderValue)
|
||||||
|
|
||||||
|
const refreshInterval = 10000 // 10 sec.
|
||||||
|
|
||||||
|
function ProfileProvider({
|
||||||
|
accountId,
|
||||||
|
accountEns,
|
||||||
|
children
|
||||||
|
}: {
|
||||||
|
accountId: string
|
||||||
|
accountEns: string
|
||||||
|
children: ReactNode
|
||||||
|
}): ReactElement {
|
||||||
|
const { chainIds } = useUserPreferences()
|
||||||
|
const { appConfig } = useSiteMetadata()
|
||||||
|
|
||||||
|
const [isEthAddress, setIsEthAddress] = useState<boolean>()
|
||||||
|
|
||||||
|
//
|
||||||
|
// Do nothing in all following effects
|
||||||
|
// when accountId is no ETH address
|
||||||
|
//
|
||||||
|
useEffect(() => {
|
||||||
|
const isEthAddress = ethereumAddress.isAddress(accountId)
|
||||||
|
setIsEthAddress(isEthAddress)
|
||||||
|
}, [accountId])
|
||||||
|
|
||||||
|
//
|
||||||
|
// User profile: ENS + 3Box
|
||||||
|
//
|
||||||
|
const [profile, setProfile] = useState<Profile>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!accountEns) return
|
||||||
|
Logger.log(`[profile] ENS name found for ${accountId}:`, accountEns)
|
||||||
|
}, [accountId, accountEns])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const clearedProfile: Profile = {
|
||||||
|
name: null,
|
||||||
|
accountEns: null,
|
||||||
|
image: null,
|
||||||
|
description: null,
|
||||||
|
links: null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!accountId || !isEthAddress) {
|
||||||
|
setProfile(clearedProfile)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelTokenSource = axios.CancelToken.source()
|
||||||
|
|
||||||
|
async function getInfo() {
|
||||||
|
setProfile({ name: accountEns || accountTruncate(accountId), accountEns })
|
||||||
|
|
||||||
|
const profile3Box = await get3BoxProfile(
|
||||||
|
accountId,
|
||||||
|
cancelTokenSource.token
|
||||||
|
)
|
||||||
|
if (profile3Box) {
|
||||||
|
const { name, emoji, description, image, links } = profile3Box
|
||||||
|
const newName = `${emoji || ''} ${name || accountTruncate(accountId)}`
|
||||||
|
const newProfile = {
|
||||||
|
name: newName,
|
||||||
|
image,
|
||||||
|
description,
|
||||||
|
links
|
||||||
|
}
|
||||||
|
setProfile((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
...newProfile
|
||||||
|
}))
|
||||||
|
Logger.log('[profile] Found and set 3box profile.', newProfile)
|
||||||
|
} else {
|
||||||
|
// setProfile(clearedProfile)
|
||||||
|
Logger.log('[profile] No 3box profile found.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getInfo()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelTokenSource.cancel()
|
||||||
|
}
|
||||||
|
}, [accountId, accountEns, isEthAddress])
|
||||||
|
|
||||||
|
//
|
||||||
|
// POOL SHARES
|
||||||
|
//
|
||||||
|
const [poolShares, setPoolShares] = useState<PoolShare[]>()
|
||||||
|
const [isPoolSharesLoading, setIsPoolSharesLoading] = useState<boolean>(false)
|
||||||
|
const [poolSharesInterval, setPoolSharesInterval] = useState<NodeJS.Timeout>()
|
||||||
|
|
||||||
|
const fetchPoolShares = useCallback(async () => {
|
||||||
|
if (!accountId || !chainIds || !isEthAddress) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
setIsPoolSharesLoading(true)
|
||||||
|
const poolShares = await getPoolSharesData(accountId, chainIds)
|
||||||
|
setPoolShares(poolShares)
|
||||||
|
Logger.log(
|
||||||
|
`[profile] Fetched ${poolShares.length} pool shares.`,
|
||||||
|
poolShares
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error('Error fetching pool shares: ', error.message)
|
||||||
|
} finally {
|
||||||
|
setIsPoolSharesLoading(false)
|
||||||
|
}
|
||||||
|
}, [accountId, chainIds, isEthAddress])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function init() {
|
||||||
|
await fetchPoolShares()
|
||||||
|
|
||||||
|
if (poolSharesInterval) return
|
||||||
|
const interval = setInterval(async () => {
|
||||||
|
await fetchPoolShares()
|
||||||
|
}, refreshInterval)
|
||||||
|
setPoolSharesInterval(interval)
|
||||||
|
}
|
||||||
|
init()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearInterval(poolSharesInterval)
|
||||||
|
}
|
||||||
|
}, [poolSharesInterval, fetchPoolShares])
|
||||||
|
|
||||||
|
//
|
||||||
|
// PUBLISHED ASSETS
|
||||||
|
//
|
||||||
|
const [assets, setAssets] = useState<DDO[]>()
|
||||||
|
const [assetsTotal, setAssetsTotal] = useState(0)
|
||||||
|
// const [assetsWithPrices, setAssetsWithPrices] = useState<AssetListPrices[]>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!accountId || !isEthAddress) return
|
||||||
|
|
||||||
|
const cancelTokenSource = axios.CancelToken.source()
|
||||||
|
|
||||||
|
async function getAllPublished() {
|
||||||
|
try {
|
||||||
|
const result = await getPublishedAssets(
|
||||||
|
accountId,
|
||||||
|
chainIds,
|
||||||
|
cancelTokenSource.token
|
||||||
|
)
|
||||||
|
setAssets(result.results)
|
||||||
|
setAssetsTotal(result.totalResults)
|
||||||
|
Logger.log(
|
||||||
|
`[profile] Fetched ${result.totalResults} assets.`,
|
||||||
|
result.results
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hint: this would only make sense if we "search" in all subcomponents
|
||||||
|
// against this provider's state, meaning filtering via js rather then sending
|
||||||
|
// more queries to Aquarius.
|
||||||
|
// const assetsWithPrices = await getAssetsBestPrices(result.results)
|
||||||
|
// setAssetsWithPrices(assetsWithPrices)
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getAllPublished()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelTokenSource.cancel()
|
||||||
|
}
|
||||||
|
}, [accountId, appConfig.metadataCacheUri, chainIds, isEthAddress])
|
||||||
|
|
||||||
|
//
|
||||||
|
// DOWNLOADS
|
||||||
|
//
|
||||||
|
const [downloads, setDownloads] = useState<DownloadedAsset[]>()
|
||||||
|
const [downloadsTotal, setDownloadsTotal] = useState(0)
|
||||||
|
const [isDownloadsLoading, setIsDownloadsLoading] = useState<boolean>()
|
||||||
|
const [downloadsInterval, setDownloadsInterval] = useState<NodeJS.Timeout>()
|
||||||
|
|
||||||
|
const fetchDownloads = useCallback(
|
||||||
|
async (cancelToken: CancelToken) => {
|
||||||
|
if (!accountId || !chainIds) return
|
||||||
|
|
||||||
|
const didList: string[] = []
|
||||||
|
const tokenOrders = await getUserTokenOrders(accountId, chainIds)
|
||||||
|
|
||||||
|
for (let i = 0; i < tokenOrders?.length; i++) {
|
||||||
|
const did = web3.utils
|
||||||
|
.toChecksumAddress(tokenOrders[i].datatokenId.address)
|
||||||
|
.replace('0x', 'did:op:')
|
||||||
|
didList.push(did)
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloads = await getDownloadAssets(
|
||||||
|
didList,
|
||||||
|
tokenOrders,
|
||||||
|
chainIds,
|
||||||
|
cancelToken
|
||||||
|
)
|
||||||
|
setDownloads(downloads)
|
||||||
|
setDownloadsTotal(downloads.length)
|
||||||
|
Logger.log(
|
||||||
|
`[profile] Fetched ${downloads.length} download orders.`,
|
||||||
|
downloads
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[accountId, chainIds]
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const cancelTokenSource = axios.CancelToken.source()
|
||||||
|
|
||||||
|
async function getDownloadAssets() {
|
||||||
|
if (!appConfig?.metadataCacheUri) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
setIsDownloadsLoading(true)
|
||||||
|
await fetchDownloads(cancelTokenSource.token)
|
||||||
|
} catch (err) {
|
||||||
|
Logger.log(err.message)
|
||||||
|
} finally {
|
||||||
|
setIsDownloadsLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getDownloadAssets()
|
||||||
|
|
||||||
|
if (downloadsInterval) return
|
||||||
|
const interval = setInterval(async () => {
|
||||||
|
await fetchDownloads(cancelTokenSource.token)
|
||||||
|
}, refreshInterval)
|
||||||
|
setDownloadsInterval(interval)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelTokenSource.cancel()
|
||||||
|
clearInterval(downloadsInterval)
|
||||||
|
}
|
||||||
|
}, [fetchDownloads, appConfig.metadataCacheUri, downloadsInterval])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProfileContext.Provider
|
||||||
|
value={{
|
||||||
|
profile,
|
||||||
|
poolShares,
|
||||||
|
isPoolSharesLoading,
|
||||||
|
assets,
|
||||||
|
assetsTotal,
|
||||||
|
isEthAddress,
|
||||||
|
downloads,
|
||||||
|
downloadsTotal,
|
||||||
|
isDownloadsLoading
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ProfileContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper hook to access the provider values
|
||||||
|
const useProfile = (): ProfileProviderValue => useContext(ProfileContext)
|
||||||
|
|
||||||
|
export { ProfileProvider, useProfile, ProfileProviderValue, ProfileContext }
|
||||||
|
export default ProfileProvider
|
@ -18,7 +18,7 @@ import {
|
|||||||
getNetworkDataById,
|
getNetworkDataById,
|
||||||
getNetworkDisplayName
|
getNetworkDisplayName
|
||||||
} from '../utils/web3'
|
} from '../utils/web3'
|
||||||
import { graphql } from 'gatsby'
|
import { getEnsName } from '../utils/ens'
|
||||||
import { UserBalance } from '../@types/TokenBalance'
|
import { UserBalance } from '../@types/TokenBalance'
|
||||||
import { getOceanBalance } from '../utils/ocean'
|
import { getOceanBalance } from '../utils/ocean'
|
||||||
import useNetworkMetadata from '../hooks/useNetworkMetadata'
|
import useNetworkMetadata from '../hooks/useNetworkMetadata'
|
||||||
@ -29,6 +29,7 @@ interface Web3ProviderValue {
|
|||||||
web3Modal: Web3Modal
|
web3Modal: Web3Modal
|
||||||
web3ProviderInfo: IProviderInfo
|
web3ProviderInfo: IProviderInfo
|
||||||
accountId: string
|
accountId: string
|
||||||
|
accountEns: string
|
||||||
balance: UserBalance
|
balance: UserBalance
|
||||||
networkId: number
|
networkId: number
|
||||||
chainId: number
|
chainId: number
|
||||||
@ -84,26 +85,6 @@ export const web3ModalOpts = {
|
|||||||
|
|
||||||
const refreshInterval = 20000 // 20 sec.
|
const refreshInterval = 20000 // 20 sec.
|
||||||
|
|
||||||
const networksQuery = graphql`
|
|
||||||
query {
|
|
||||||
allNetworksMetadataJson {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
chain
|
|
||||||
network
|
|
||||||
networkId
|
|
||||||
chainId
|
|
||||||
nativeCurrency {
|
|
||||||
name
|
|
||||||
symbol
|
|
||||||
decimals
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const Web3Context = createContext({} as Web3ProviderValue)
|
const Web3Context = createContext({} as Web3ProviderValue)
|
||||||
|
|
||||||
function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
||||||
@ -120,6 +101,7 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
const [block, setBlock] = useState<number>()
|
const [block, setBlock] = useState<number>()
|
||||||
const [isTestnet, setIsTestnet] = useState<boolean>()
|
const [isTestnet, setIsTestnet] = useState<boolean>()
|
||||||
const [accountId, setAccountId] = useState<string>()
|
const [accountId, setAccountId] = useState<string>()
|
||||||
|
const [accountEns, setAccountEns] = useState<string>()
|
||||||
const [web3Loading, setWeb3Loading] = useState<boolean>(true)
|
const [web3Loading, setWeb3Loading] = useState<boolean>(true)
|
||||||
const [balance, setBalance] = useState<UserBalance>({
|
const [balance, setBalance] = useState<UserBalance>({
|
||||||
eth: '0',
|
eth: '0',
|
||||||
@ -181,6 +163,27 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
}
|
}
|
||||||
}, [accountId, networkId, web3])
|
}, [accountId, networkId, web3])
|
||||||
|
|
||||||
|
// -----------------------------------
|
||||||
|
// Helper: Get user ENS name
|
||||||
|
// -----------------------------------
|
||||||
|
const getUserEnsName = useCallback(async () => {
|
||||||
|
if (!accountId) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
// const accountEns = await getEnsNameWithWeb3(
|
||||||
|
// accountId,
|
||||||
|
// web3Provider,
|
||||||
|
// `${networkId}`
|
||||||
|
// )
|
||||||
|
const accountEns = await getEnsName(accountId)
|
||||||
|
setAccountEns(accountEns)
|
||||||
|
accountEns &&
|
||||||
|
Logger.log(`[web3] ENS name found for ${accountId}:`, accountEns)
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error('[web3] Error: ', error.message)
|
||||||
|
}
|
||||||
|
}, [accountId])
|
||||||
|
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
// Create initial Web3Modal instance
|
// Create initial Web3Modal instance
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
@ -229,6 +232,13 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
}
|
}
|
||||||
}, [getUserBalance])
|
}, [getUserBalance])
|
||||||
|
|
||||||
|
// -----------------------------------
|
||||||
|
// Get and set user ENS name
|
||||||
|
// -----------------------------------
|
||||||
|
useEffect(() => {
|
||||||
|
getUserEnsName()
|
||||||
|
}, [getUserEnsName])
|
||||||
|
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
// Get and set network metadata
|
// Get and set network metadata
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
@ -333,6 +343,7 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
web3Modal,
|
web3Modal,
|
||||||
web3ProviderInfo,
|
web3ProviderInfo,
|
||||||
accountId,
|
accountId,
|
||||||
|
accountEns,
|
||||||
balance,
|
balance,
|
||||||
networkId,
|
networkId,
|
||||||
chainId,
|
chainId,
|
||||||
|
@ -12,7 +12,16 @@ import {
|
|||||||
import { AssetSelectionAsset } from '../components/molecules/FormFields/AssetSelection'
|
import { AssetSelectionAsset } from '../components/molecules/FormFields/AssetSelection'
|
||||||
import { PriceList, getAssetsPriceList } from './subgraph'
|
import { PriceList, getAssetsPriceList } from './subgraph'
|
||||||
import axios, { CancelToken, AxiosResponse } from 'axios'
|
import axios, { CancelToken, AxiosResponse } from 'axios'
|
||||||
|
import { OrdersData_tokenOrders as OrdersData } from '../@types/apollo/OrdersData'
|
||||||
import { metadataCacheUri } from '../../app.config'
|
import { metadataCacheUri } from '../../app.config'
|
||||||
|
import web3 from '../../tests/unit/__mocks__/web3'
|
||||||
|
|
||||||
|
export interface DownloadedAsset {
|
||||||
|
dtSymbol: string
|
||||||
|
timestamp: number
|
||||||
|
networkId: number
|
||||||
|
ddo: DDO
|
||||||
|
}
|
||||||
|
|
||||||
export const MAXIMUM_NUMBER_OF_PAGES_WITH_RESULTS = 476
|
export const MAXIMUM_NUMBER_OF_PAGES_WITH_RESULTS = 476
|
||||||
|
|
||||||
@ -68,7 +77,8 @@ export async function queryMetadata(
|
|||||||
try {
|
try {
|
||||||
const response: AxiosResponse<any> = await axios.post(
|
const response: AxiosResponse<any> = await axios.post(
|
||||||
`${metadataCacheUri}/api/v1/aquarius/assets/ddo/query`,
|
`${metadataCacheUri}/api/v1/aquarius/assets/ddo/query`,
|
||||||
{ ...query, cancelToken }
|
{ ...query },
|
||||||
|
{ cancelToken }
|
||||||
)
|
)
|
||||||
if (!response || response.status !== 200 || !response.data) return
|
if (!response || response.status !== 200 || !response.data) return
|
||||||
return transformQueryResult(response.data)
|
return transformQueryResult(response.data)
|
||||||
@ -110,10 +120,8 @@ export async function getAssetsNames(
|
|||||||
try {
|
try {
|
||||||
const response: AxiosResponse<Record<string, string>> = await axios.post(
|
const response: AxiosResponse<Record<string, string>> = await axios.post(
|
||||||
`${metadataCacheUri}/api/v1/aquarius/assets/names`,
|
`${metadataCacheUri}/api/v1/aquarius/assets/names`,
|
||||||
{
|
{ didList },
|
||||||
didList,
|
{ cancelToken }
|
||||||
cancelToken
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
if (!response || response.status !== 200 || !response.data) return
|
if (!response || response.status !== 200 || !response.data) return
|
||||||
return response.data
|
return response.data
|
||||||
@ -206,3 +214,122 @@ export async function getAlgorithmDatasetsForCompute(
|
|||||||
)
|
)
|
||||||
return datasets
|
return datasets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getPublishedAssets(
|
||||||
|
accountId: string,
|
||||||
|
chainIds: number[],
|
||||||
|
cancelToken: CancelToken,
|
||||||
|
page?: number,
|
||||||
|
type?: string
|
||||||
|
): Promise<QueryResult> {
|
||||||
|
if (!accountId) return
|
||||||
|
|
||||||
|
page = page || 1
|
||||||
|
type = type || 'dataset OR algorithm'
|
||||||
|
|
||||||
|
const queryPublishedAssets = {
|
||||||
|
page,
|
||||||
|
offset: 9,
|
||||||
|
query: {
|
||||||
|
query_string: {
|
||||||
|
query: `(publicKey.owner:${accountId}) AND (service.attributes.main.type:${type}) AND (${transformChainIdsListToQuery(
|
||||||
|
chainIds
|
||||||
|
)})`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sort: { created: -1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await queryMetadata(queryPublishedAssets, cancelToken)
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isCancel(error)) {
|
||||||
|
Logger.log(error.message)
|
||||||
|
} else {
|
||||||
|
Logger.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAssetsFromDidList(
|
||||||
|
didList: string[],
|
||||||
|
chainIds: number[],
|
||||||
|
cancelToken: CancelToken
|
||||||
|
): Promise<QueryResult> {
|
||||||
|
try {
|
||||||
|
// TODO: figure out cleaner way to transform string[] into csv
|
||||||
|
const searchDids = JSON.stringify(didList)
|
||||||
|
.replace(/,/g, ' ')
|
||||||
|
.replace(/"/g, '')
|
||||||
|
.replace(/(\[|\])/g, '')
|
||||||
|
// for whatever reason ddo.id is not searchable, so use ddo.dataToken instead
|
||||||
|
.replace(/(did:op:)/g, '0x')
|
||||||
|
|
||||||
|
// safeguard against passed empty didList, preventing 500 from Aquarius
|
||||||
|
if (!searchDids) return
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
page: 1,
|
||||||
|
offset: 1000,
|
||||||
|
query: {
|
||||||
|
query_string: {
|
||||||
|
query: `(${searchDids}) AND (${transformChainIdsListToQuery(
|
||||||
|
chainIds
|
||||||
|
)})`,
|
||||||
|
fields: ['dataToken'],
|
||||||
|
default_operator: 'OR'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sort: { created: -1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryResult = await queryMetadata(query, cancelToken)
|
||||||
|
return queryResult
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getDownloadAssets(
|
||||||
|
didList: string[],
|
||||||
|
tokenOrders: OrdersData[],
|
||||||
|
chainIds: number[],
|
||||||
|
cancelToken: CancelToken
|
||||||
|
): Promise<DownloadedAsset[]> {
|
||||||
|
const downloadedAssets: DownloadedAsset[] = []
|
||||||
|
|
||||||
|
try {
|
||||||
|
const queryResult = await getAssetsFromDidList(
|
||||||
|
didList,
|
||||||
|
chainIds,
|
||||||
|
cancelToken
|
||||||
|
)
|
||||||
|
const ddoList = queryResult?.results
|
||||||
|
|
||||||
|
for (let i = 0; i < tokenOrders?.length; i++) {
|
||||||
|
const ddo = ddoList.filter(
|
||||||
|
(ddo) =>
|
||||||
|
tokenOrders[i].datatokenId.address.toLowerCase() ===
|
||||||
|
ddo.dataToken.toLowerCase()
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
// make sure we are only pushing download orders
|
||||||
|
if (ddo.service[1].type !== 'access') continue
|
||||||
|
|
||||||
|
downloadedAssets.push({
|
||||||
|
ddo,
|
||||||
|
networkId: ddo.chainId,
|
||||||
|
dtSymbol: tokenOrders[i].datatokenId.symbol,
|
||||||
|
timestamp: tokenOrders[i].timestamp
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const sortedOrders = downloadedAssets.sort(
|
||||||
|
(a, b) => b.timestamp - a.timestamp
|
||||||
|
)
|
||||||
|
return sortedOrders
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,10 +1,272 @@
|
|||||||
import {
|
import {
|
||||||
DDO,
|
|
||||||
Ocean,
|
|
||||||
ServiceComputePrivacy,
|
ServiceComputePrivacy,
|
||||||
publisherTrustedAlgorithm as PublisherTrustedAlgorithm
|
publisherTrustedAlgorithm as PublisherTrustedAlgorithm,
|
||||||
|
DDO,
|
||||||
|
Service,
|
||||||
|
Logger,
|
||||||
|
Provider,
|
||||||
|
Config,
|
||||||
|
Ocean,
|
||||||
|
Account
|
||||||
} from '@oceanprotocol/lib'
|
} from '@oceanprotocol/lib'
|
||||||
import { ComputePrivacyForm } from '../models/FormEditComputeDataset'
|
import { ComputePrivacyForm } from '../models/FormEditComputeDataset'
|
||||||
|
import web3 from 'web3'
|
||||||
|
import { ComputeJob } from '@oceanprotocol/lib/dist/node/ocean/interfaces/Compute'
|
||||||
|
import axios, { CancelToken } from 'axios'
|
||||||
|
import { gql } from 'urql'
|
||||||
|
import { ComputeJobMetaData } from '../@types/ComputeJobMetaData'
|
||||||
|
import { transformChainIdsListToQuery, queryMetadata } from './aquarius'
|
||||||
|
import { fetchDataForMultipleChains } from './subgraph'
|
||||||
|
import { OrdersData_tokenOrders_datatokenId as OrdersDatatoken } from '../@types/apollo/OrdersData'
|
||||||
|
|
||||||
|
const getComputeOrders = gql`
|
||||||
|
query ComputeOrders($user: String!) {
|
||||||
|
tokenOrders(
|
||||||
|
orderBy: timestamp
|
||||||
|
orderDirection: desc
|
||||||
|
where: { payer: $user }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
serviceId
|
||||||
|
datatokenId {
|
||||||
|
address
|
||||||
|
}
|
||||||
|
tx
|
||||||
|
timestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const getComputeOrdersByDatatokenAddress = gql`
|
||||||
|
query ComputeOrdersByDatatokenAddress(
|
||||||
|
$user: String!
|
||||||
|
$datatokenAddress: String!
|
||||||
|
) {
|
||||||
|
tokenOrders(
|
||||||
|
orderBy: timestamp
|
||||||
|
orderDirection: desc
|
||||||
|
where: { payer: $user, datatokenId: $datatokenAddress }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
serviceId
|
||||||
|
datatokenId {
|
||||||
|
address
|
||||||
|
}
|
||||||
|
tx
|
||||||
|
timestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
interface TokenOrder {
|
||||||
|
id: string
|
||||||
|
serviceId: number
|
||||||
|
datatokenId: OrdersDatatoken
|
||||||
|
tx: any
|
||||||
|
timestamp: number
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getAssetMetadata(
|
||||||
|
queryDtList: string,
|
||||||
|
cancelToken: CancelToken,
|
||||||
|
chainIds: number[]
|
||||||
|
): Promise<DDO[]> {
|
||||||
|
const queryDid = {
|
||||||
|
page: 1,
|
||||||
|
offset: 100,
|
||||||
|
query: {
|
||||||
|
query_string: {
|
||||||
|
query: `(${queryDtList}) AND (${transformChainIdsListToQuery(
|
||||||
|
chainIds
|
||||||
|
)}) AND service.attributes.main.type:dataset AND service.type:compute`,
|
||||||
|
fields: ['dataToken']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await queryMetadata(queryDid, cancelToken)
|
||||||
|
return result.results
|
||||||
|
}
|
||||||
|
|
||||||
|
function getServiceEndpoints(data: TokenOrder[], assets: DDO[]): string[] {
|
||||||
|
const serviceEndpoints: string[] = []
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
try {
|
||||||
|
const did = web3.utils
|
||||||
|
.toChecksumAddress(data[i].datatokenId.address)
|
||||||
|
.replace('0x', 'did:op:')
|
||||||
|
const ddo = assets.filter((x) => x.id === did)[0]
|
||||||
|
if (ddo === undefined) continue
|
||||||
|
|
||||||
|
const service = ddo.service.filter(
|
||||||
|
(x: Service) => x.index === data[i].serviceId
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
if (!service || service.type !== 'compute') continue
|
||||||
|
const { serviceEndpoint } = service
|
||||||
|
|
||||||
|
const wasProviderQueried =
|
||||||
|
serviceEndpoints?.filter((x) => x === serviceEndpoint).length > 0
|
||||||
|
|
||||||
|
if (wasProviderQueried) continue
|
||||||
|
serviceEndpoints.push(serviceEndpoint)
|
||||||
|
} catch (err) {
|
||||||
|
Logger.error(err.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return serviceEndpoints
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getProviders(
|
||||||
|
serviceEndpoints: string[],
|
||||||
|
config: Config,
|
||||||
|
ocean: Ocean
|
||||||
|
): Promise<Provider[]> {
|
||||||
|
const providers: Provider[] = []
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (let i = 0; i < serviceEndpoints?.length; i++) {
|
||||||
|
const instanceConfig = {
|
||||||
|
config,
|
||||||
|
web3: config.web3Provider,
|
||||||
|
logger: Logger,
|
||||||
|
ocean
|
||||||
|
}
|
||||||
|
const provider = await Provider.getInstance(instanceConfig)
|
||||||
|
await provider.setBaseUrl(serviceEndpoints[i])
|
||||||
|
const hasSameCompute =
|
||||||
|
providers.filter((x) => x.computeAddress === provider.computeAddress)
|
||||||
|
.length > 0
|
||||||
|
if (!hasSameCompute) providers.push(provider)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
Logger.error(err.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return providers
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getJobs(
|
||||||
|
providers: Provider[],
|
||||||
|
account: Account,
|
||||||
|
assets: DDO[]
|
||||||
|
): Promise<ComputeJobMetaData[]> {
|
||||||
|
const computeJobs: ComputeJobMetaData[] = []
|
||||||
|
|
||||||
|
for (let i = 0; i < providers.length; i++) {
|
||||||
|
try {
|
||||||
|
const providerComputeJobs = (await providers[i].computeStatus(
|
||||||
|
'',
|
||||||
|
account,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
false
|
||||||
|
)) as ComputeJob[]
|
||||||
|
|
||||||
|
// means the provider uri is not good, so we ignore it and move on
|
||||||
|
if (!providerComputeJobs) continue
|
||||||
|
providerComputeJobs.sort((a, b) => {
|
||||||
|
if (a.dateCreated > b.dateCreated) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if (a.dateCreated < b.dateCreated) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
|
for (let j = 0; j < providerComputeJobs?.length; j++) {
|
||||||
|
const job = providerComputeJobs[j]
|
||||||
|
const did = job.inputDID[0]
|
||||||
|
const ddo = assets.filter((x) => x.id === did)[0]
|
||||||
|
|
||||||
|
if (!ddo) continue
|
||||||
|
const serviceMetadata = ddo.service.filter(
|
||||||
|
(x: Service) => x.type === 'metadata'
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
const compJob: ComputeJobMetaData = {
|
||||||
|
...job,
|
||||||
|
assetName: serviceMetadata.attributes.main.name,
|
||||||
|
assetDtSymbol: ddo.dataTokenInfo.symbol,
|
||||||
|
networkId: ddo.chainId
|
||||||
|
}
|
||||||
|
computeJobs.push(compJob)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
Logger.error(err.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return computeJobs
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDtList(data: TokenOrder[]) {
|
||||||
|
const dtList = []
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
dtList.push(data[i].datatokenId.address)
|
||||||
|
}
|
||||||
|
const queryDtList = JSON.stringify(dtList)
|
||||||
|
.replace(/,/g, ' ')
|
||||||
|
.replace(/"/g, '')
|
||||||
|
.replace(/(\[|\])/g, '')
|
||||||
|
|
||||||
|
return queryDtList
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getComputeJobs(
|
||||||
|
chainIds: number[],
|
||||||
|
config: Config,
|
||||||
|
ocean: Ocean,
|
||||||
|
account: Account,
|
||||||
|
ddo?: DDO
|
||||||
|
): Promise<ComputeJobMetaData[]> {
|
||||||
|
const assetDTAddress = ddo?.dataTokenInfo?.address
|
||||||
|
|
||||||
|
const variables = assetDTAddress
|
||||||
|
? {
|
||||||
|
user: account?.getId().toLowerCase(),
|
||||||
|
datatokenAddress: assetDTAddress.toLowerCase()
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
user: account?.getId().toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await fetchDataForMultipleChains(
|
||||||
|
assetDTAddress ? getComputeOrdersByDatatokenAddress : getComputeOrders,
|
||||||
|
variables,
|
||||||
|
assetDTAddress ? [ddo?.chainId] : chainIds
|
||||||
|
)
|
||||||
|
let data: TokenOrder[] = []
|
||||||
|
|
||||||
|
for (let i = 0; i < result.length; i++) {
|
||||||
|
if (!result[i]?.tokenOrders) continue
|
||||||
|
result[i]?.tokenOrders.forEach((tokenOrder: TokenOrder) => {
|
||||||
|
data.push(tokenOrder)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ocean || !account || !data) return
|
||||||
|
|
||||||
|
data = data.sort((a, b) => b.timestamp - a.timestamp)
|
||||||
|
const queryDtList = getDtList(data)
|
||||||
|
if (queryDtList === '') return
|
||||||
|
|
||||||
|
const source = axios.CancelToken.source()
|
||||||
|
const assets = await getAssetMetadata(queryDtList, source.token, chainIds)
|
||||||
|
const serviceEndpoints = getServiceEndpoints(data, assets)
|
||||||
|
const providers: Provider[] = await getProviders(
|
||||||
|
serviceEndpoints,
|
||||||
|
config,
|
||||||
|
ocean
|
||||||
|
)
|
||||||
|
const computeJobs = await getJobs(providers, account, assets)
|
||||||
|
|
||||||
|
return computeJobs
|
||||||
|
}
|
||||||
|
|
||||||
export async function createTrustedAlgorithmList(
|
export async function createTrustedAlgorithmList(
|
||||||
selectedAlgorithms: string[], // list of DIDs
|
selectedAlgorithms: string[], // list of DIDs
|
||||||
|
52
src/utils/ens.ts
Normal file
52
src/utils/ens.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { gql, OperationContext, OperationResult } from 'urql'
|
||||||
|
import { fetchData } from './subgraph'
|
||||||
|
|
||||||
|
// make sure to only query for domains owned by account, so domains
|
||||||
|
// solely set by 3rd parties like *.gitcoin.eth won't show up
|
||||||
|
const UserEnsNames = gql<any>`
|
||||||
|
query UserEnsDomains($accountId: String!) {
|
||||||
|
domains(where: { resolvedAddress: $accountId, owner: $accountId }) {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const UserEnsAddress = gql<any>`
|
||||||
|
query UserEnsDomainsAddress($name: String!) {
|
||||||
|
domains(where: { name: $name }) {
|
||||||
|
resolvedAddress {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const ensSubgraphQueryContext: OperationContext = {
|
||||||
|
url: `https://api.thegraph.com/subgraphs/name/ensdomains/ens`,
|
||||||
|
requestPolicy: 'cache-and-network'
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getEnsName(accountId: string): Promise<string> {
|
||||||
|
const response: OperationResult<any> = await fetchData(
|
||||||
|
UserEnsNames,
|
||||||
|
{ accountId: accountId.toLowerCase() },
|
||||||
|
ensSubgraphQueryContext
|
||||||
|
)
|
||||||
|
if (!response?.data?.domains?.length) return
|
||||||
|
|
||||||
|
// Default order of response.data.domains seems to be by creation time, from oldest to newest.
|
||||||
|
// Pick the last one as that is what direct web3 calls do.
|
||||||
|
const { name } = response.data.domains.slice(-1)[0]
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getEnsAddress(ensName: string): Promise<string> {
|
||||||
|
const response: OperationResult<any> = await fetchData(
|
||||||
|
UserEnsAddress,
|
||||||
|
{ name: ensName },
|
||||||
|
ensSubgraphQueryContext
|
||||||
|
)
|
||||||
|
if (!response?.data?.domains?.length) return
|
||||||
|
const { id } = response.data.domains[0].resolvedAddress
|
||||||
|
return id
|
||||||
|
}
|
@ -1,11 +1,12 @@
|
|||||||
import { Logger, Ocean } from '@oceanprotocol/lib'
|
import { Logger, Ocean } from '@oceanprotocol/lib'
|
||||||
|
import { TransactionReceipt } from 'web3-core'
|
||||||
|
|
||||||
export async function setMinterToPublisher(
|
export async function setMinterToPublisher(
|
||||||
ocean: Ocean,
|
ocean: Ocean,
|
||||||
dataTokenAddress: string,
|
dataTokenAddress: string,
|
||||||
accountId: string,
|
accountId: string,
|
||||||
setError: (msg: string) => void
|
setError: (msg: string) => void
|
||||||
): Promise<any> {
|
): Promise<TransactionReceipt> {
|
||||||
// free pricing v3 workaround part1
|
// free pricing v3 workaround part1
|
||||||
const response = await ocean.OceanDispenser.cancelMinter(
|
const response = await ocean.OceanDispenser.cancelMinter(
|
||||||
dataTokenAddress,
|
dataTokenAddress,
|
||||||
@ -23,7 +24,7 @@ export async function setMinterToDispenser(
|
|||||||
dataTokenAddress: string,
|
dataTokenAddress: string,
|
||||||
accountId: string,
|
accountId: string,
|
||||||
setError: (msg: string) => void
|
setError: (msg: string) => void
|
||||||
): Promise<any> {
|
): Promise<TransactionReceipt> {
|
||||||
// free pricing v3 workaround part2
|
// free pricing v3 workaround part2
|
||||||
const response = await ocean.OceanDispenser.makeMinter(
|
const response = await ocean.OceanDispenser.makeMinter(
|
||||||
dataTokenAddress,
|
dataTokenAddress,
|
||||||
|
@ -76,7 +76,7 @@ export function checkIfTimeoutInPredefinedValues(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAlgoithComponent(
|
function getAlgorithmComponent(
|
||||||
image: string,
|
image: string,
|
||||||
containerTag: string,
|
containerTag: string,
|
||||||
entrypoint: string,
|
entrypoint: string,
|
||||||
@ -94,7 +94,7 @@ function getAlgoithComponent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAlgoithFileExtension(fileUrl: string): string {
|
function getAlgorithmFileExtension(fileUrl: string): string {
|
||||||
const splitedFileUrl = fileUrl.split('.')
|
const splitedFileUrl = fileUrl.split('.')
|
||||||
return splitedFileUrl[splitedFileUrl.length - 1]
|
return splitedFileUrl[splitedFileUrl.length - 1]
|
||||||
}
|
}
|
||||||
@ -203,7 +203,6 @@ export function transformPublishAlgorithmFormToMetadata(
|
|||||||
author,
|
author,
|
||||||
description,
|
description,
|
||||||
tags,
|
tags,
|
||||||
dockerImage,
|
|
||||||
image,
|
image,
|
||||||
containerTag,
|
containerTag,
|
||||||
entrypoint,
|
entrypoint,
|
||||||
@ -214,12 +213,12 @@ export function transformPublishAlgorithmFormToMetadata(
|
|||||||
): MetadataMarket {
|
): MetadataMarket {
|
||||||
const currentTime = toStringNoMS(new Date())
|
const currentTime = toStringNoMS(new Date())
|
||||||
const fileUrl = typeof files !== 'string' && files[0].url
|
const fileUrl = typeof files !== 'string' && files[0].url
|
||||||
const algorithmLanguace = getAlgoithFileExtension(fileUrl)
|
const algorithmLanguage = getAlgorithmFileExtension(fileUrl)
|
||||||
const algorithm = getAlgoithComponent(
|
const algorithm = getAlgorithmComponent(
|
||||||
image,
|
image,
|
||||||
containerTag,
|
containerTag,
|
||||||
entrypoint,
|
entrypoint,
|
||||||
algorithmLanguace
|
algorithmLanguage
|
||||||
)
|
)
|
||||||
const metadata: MetadataMarket = {
|
const metadata: MetadataMarket = {
|
||||||
main: {
|
main: {
|
||||||
@ -230,7 +229,7 @@ export function transformPublishAlgorithmFormToMetadata(
|
|||||||
dateCreated: ddo ? ddo.created : currentTime,
|
dateCreated: ddo ? ddo.created : currentTime,
|
||||||
files: typeof files !== 'string' && files,
|
files: typeof files !== 'string' && files,
|
||||||
license: 'https://market.oceanprotocol.com/terms',
|
license: 'https://market.oceanprotocol.com/terms',
|
||||||
algorithm: algorithm
|
algorithm
|
||||||
},
|
},
|
||||||
additionalInformation: {
|
additionalInformation: {
|
||||||
...AssetModel.additionalInformation,
|
...AssetModel.additionalInformation,
|
||||||
@ -242,29 +241,3 @@ export function transformPublishAlgorithmFormToMetadata(
|
|||||||
|
|
||||||
return metadata
|
return metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
function idToName(id: number): string {
|
|
||||||
switch (id) {
|
|
||||||
case 1:
|
|
||||||
return 'eth'
|
|
||||||
case 137:
|
|
||||||
return 'polygon'
|
|
||||||
case 3:
|
|
||||||
return 'ropsten'
|
|
||||||
case 4:
|
|
||||||
return 'rinkeby'
|
|
||||||
case 1287:
|
|
||||||
return 'moonbase'
|
|
||||||
default:
|
|
||||||
return 'eth'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function mapChainIdsToNetworkNames(chainIds: number[]): string[] {
|
|
||||||
const networkNames: string[] = []
|
|
||||||
for (let i = 0; i < chainIds.length; i++) {
|
|
||||||
const networkName = idToName(chainIds[i])
|
|
||||||
networkNames.push(networkName)
|
|
||||||
}
|
|
||||||
return networkNames
|
|
||||||
}
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import { Profile, ProfileLink, ResponseData3Box } from '../models/Profile'
|
import { Profile, ProfileLink, ResponseData3Box } from '../models/Profile'
|
||||||
import axios, { AxiosResponse, CancelToken } from 'axios'
|
import axios, { AxiosResponse, CancelToken } from 'axios'
|
||||||
import jwtDecode from 'jwt-decode'
|
import jwtDecode from 'jwt-decode'
|
||||||
import { Logger } from '@oceanprotocol/lib'
|
|
||||||
|
|
||||||
// https://docs.3box.io/api/rest-api
|
// https://docs.3box.io/api/rest-api
|
||||||
const apiUri = 'https://3box.oceanprotocol.com'
|
const apiUri = 'https://3box.oceanprotocol.com'
|
||||||
const ipfsUrl = 'https://dweb.link'
|
const ipfsUrl = 'https://infura-ipfs.io'
|
||||||
|
|
||||||
function decodeProof(proofJWT: string) {
|
function decodeProof(proofJWT: string) {
|
||||||
if (!proofJWT) return
|
if (!proofJWT) return
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { gql, OperationResult, TypedDocumentNode, OperationContext } from 'urql'
|
import { gql, OperationResult, TypedDocumentNode, OperationContext } from 'urql'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO, Logger } from '@oceanprotocol/lib'
|
||||||
import { getUrqlClientInstance } from '../providers/UrqlProvider'
|
import { getUrqlClientInstance } from '../providers/UrqlProvider'
|
||||||
import { getOceanConfig } from './ocean'
|
import { getOceanConfig } from './ocean'
|
||||||
import web3 from 'web3'
|
import web3 from 'web3'
|
||||||
import {
|
import {
|
||||||
AssetsPoolPrice,
|
AssetsPoolPrice,
|
||||||
AssetsPoolPrice_pools as AssetsPoolPricePools
|
AssetsPoolPrice_pools as AssetsPoolPricePool
|
||||||
} from '../@types/apollo/AssetsPoolPrice'
|
} from '../@types/apollo/AssetsPoolPrice'
|
||||||
import {
|
import {
|
||||||
AssetsFrePrice,
|
AssetsFrePrice,
|
||||||
AssetsFrePrice_fixedRateExchanges as AssetsFrePriceFixedRateExchanges
|
AssetsFrePrice_fixedRateExchanges as AssetsFrePriceFixedRateExchange
|
||||||
} from '../@types/apollo/AssetsFrePrice'
|
} from '../@types/apollo/AssetsFrePrice'
|
||||||
import {
|
import {
|
||||||
AssetsFreePrice,
|
AssetsFreePrice,
|
||||||
@ -17,10 +17,20 @@ import {
|
|||||||
} from '../@types/apollo/AssetsFreePrice'
|
} from '../@types/apollo/AssetsFreePrice'
|
||||||
import { AssetPreviousOrder } from '../@types/apollo/AssetPreviousOrder'
|
import { AssetPreviousOrder } from '../@types/apollo/AssetPreviousOrder'
|
||||||
import {
|
import {
|
||||||
HighestLiquidityAssets_pools as HighestLiquidityAssetsPools,
|
HighestLiquidityAssets_pools as HighestLiquidityAssetsPool,
|
||||||
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'
|
||||||
import { BestPrice } from '../models/BestPrice'
|
import { BestPrice } from '../models/BestPrice'
|
||||||
|
import { OrdersData_tokenOrders as OrdersData } from '../@types/apollo/OrdersData'
|
||||||
|
|
||||||
|
export interface UserLiquidity {
|
||||||
|
price: string
|
||||||
|
oceanBalance: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface PriceList {
|
export interface PriceList {
|
||||||
[key: string]: string
|
[key: string]: string
|
||||||
@ -112,7 +122,7 @@ const PoolQuery = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const AssetPoolPriceQuerry = gql`
|
const AssetPoolPriceQuery = gql`
|
||||||
query AssetPoolPrice($datatokenAddress: String) {
|
query AssetPoolPrice($datatokenAddress: String) {
|
||||||
pools(where: { datatokenAddress: $datatokenAddress }) {
|
pools(where: { datatokenAddress: $datatokenAddress }) {
|
||||||
id
|
id
|
||||||
@ -157,6 +167,92 @@ const HighestLiquidityAssets = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const TotalAccountOrders = gql`
|
||||||
|
query TotalAccountOrders($datatokenId_in: [String!]) {
|
||||||
|
tokenOrders(where: { datatokenId_in: $datatokenId_in }) {
|
||||||
|
payer {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
datatokenId {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const userPoolSharesQuery = gql`
|
||||||
|
query PoolShares($user: String) {
|
||||||
|
poolShares(where: { userAddress: $user, balance_gt: 0.001 }, first: 1000) {
|
||||||
|
id
|
||||||
|
balance
|
||||||
|
userAddress {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
poolId {
|
||||||
|
id
|
||||||
|
datatokenAddress
|
||||||
|
valueLocked
|
||||||
|
tokens {
|
||||||
|
id
|
||||||
|
isDatatoken
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
oceanReserve
|
||||||
|
datatokenReserve
|
||||||
|
totalShares
|
||||||
|
consumePrice
|
||||||
|
spotPrice
|
||||||
|
createTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const UserTokenOrders = gql`
|
||||||
|
query OrdersData($user: String!) {
|
||||||
|
tokenOrders(
|
||||||
|
orderBy: timestamp
|
||||||
|
orderDirection: desc
|
||||||
|
where: { consumer: $user }
|
||||||
|
) {
|
||||||
|
datatokenId {
|
||||||
|
address
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
timestamp
|
||||||
|
tx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
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
|
||||||
@ -238,8 +334,8 @@ export async function getPreviousOrders(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function transformPriceToBestPrice(
|
function transformPriceToBestPrice(
|
||||||
frePrice: AssetsFrePriceFixedRateExchanges[],
|
frePrice: AssetsFrePriceFixedRateExchange[],
|
||||||
poolPrice: AssetsPoolPricePools[],
|
poolPrice: AssetsPoolPricePool[],
|
||||||
freePrice: AssetFreePriceDispenser[]
|
freePrice: AssetFreePriceDispenser[]
|
||||||
) {
|
) {
|
||||||
if (poolPrice?.length > 0) {
|
if (poolPrice?.length > 0) {
|
||||||
@ -303,8 +399,8 @@ async function getAssetsPoolsExchangesAndDatatokenMap(
|
|||||||
assets: DDO[]
|
assets: DDO[]
|
||||||
): Promise<
|
): Promise<
|
||||||
[
|
[
|
||||||
AssetsPoolPricePools[],
|
AssetsPoolPricePool[],
|
||||||
AssetsFrePriceFixedRateExchanges[],
|
AssetsFrePriceFixedRateExchange[],
|
||||||
AssetFreePriceDispenser[],
|
AssetFreePriceDispenser[],
|
||||||
DidAndDatatokenMap
|
DidAndDatatokenMap
|
||||||
]
|
]
|
||||||
@ -322,8 +418,8 @@ async function getAssetsPoolsExchangesAndDatatokenMap(
|
|||||||
chainAssetLists[ddo.chainId].push(ddo?.dataToken.toLowerCase())
|
chainAssetLists[ddo.chainId].push(ddo?.dataToken.toLowerCase())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let poolPriceResponse: AssetsPoolPricePools[] = []
|
let poolPriceResponse: AssetsPoolPricePool[] = []
|
||||||
let frePriceResponse: AssetsFrePriceFixedRateExchanges[] = []
|
let frePriceResponse: AssetsFrePriceFixedRateExchange[] = []
|
||||||
let freePriceResponse: AssetFreePriceDispenser[] = []
|
let freePriceResponse: AssetFreePriceDispenser[] = []
|
||||||
|
|
||||||
for (const chainKey in chainAssetLists) {
|
for (const chainKey in chainAssetLists) {
|
||||||
@ -366,8 +462,8 @@ export async function getAssetsPriceList(assets: DDO[]): Promise<PriceList> {
|
|||||||
const priceList: PriceList = {}
|
const priceList: PriceList = {}
|
||||||
|
|
||||||
const values: [
|
const values: [
|
||||||
AssetsPoolPricePools[],
|
AssetsPoolPricePool[],
|
||||||
AssetsFrePriceFixedRateExchanges[],
|
AssetsFrePriceFixedRateExchange[],
|
||||||
AssetFreePriceDispenser[],
|
AssetFreePriceDispenser[],
|
||||||
DidAndDatatokenMap
|
DidAndDatatokenMap
|
||||||
] = await getAssetsPoolsExchangesAndDatatokenMap(assets)
|
] = await getAssetsPoolsExchangesAndDatatokenMap(assets)
|
||||||
@ -404,7 +500,7 @@ export async function getPrice(asset: DDO): Promise<BestPrice> {
|
|||||||
const queryContext = getQueryContext(Number(asset.chainId))
|
const queryContext = getQueryContext(Number(asset.chainId))
|
||||||
|
|
||||||
const poolPriceResponse: OperationResult<AssetsPoolPrice> = await fetchData(
|
const poolPriceResponse: OperationResult<AssetsPoolPrice> = await fetchData(
|
||||||
AssetPoolPriceQuerry,
|
AssetPoolPriceQuery,
|
||||||
poolVariables,
|
poolVariables,
|
||||||
queryContext
|
queryContext
|
||||||
)
|
)
|
||||||
@ -435,7 +531,7 @@ export async function getSpotPrice(asset: DDO): Promise<number> {
|
|||||||
const queryContext = getQueryContext(Number(asset.chainId))
|
const queryContext = getQueryContext(Number(asset.chainId))
|
||||||
|
|
||||||
const poolPriceResponse: OperationResult<AssetsPoolPrice> = await fetchData(
|
const poolPriceResponse: OperationResult<AssetsPoolPrice> = await fetchData(
|
||||||
AssetPoolPriceQuerry,
|
AssetPoolPriceQuery,
|
||||||
poolVariables,
|
poolVariables,
|
||||||
queryContext
|
queryContext
|
||||||
)
|
)
|
||||||
@ -449,8 +545,8 @@ export async function getAssetsBestPrices(
|
|||||||
const assetsWithPrice: AssetListPrices[] = []
|
const assetsWithPrice: AssetListPrices[] = []
|
||||||
|
|
||||||
const values: [
|
const values: [
|
||||||
AssetsPoolPricePools[],
|
AssetsPoolPricePool[],
|
||||||
AssetsFrePriceFixedRateExchanges[],
|
AssetsFrePriceFixedRateExchange[],
|
||||||
AssetFreePriceDispenser[],
|
AssetFreePriceDispenser[],
|
||||||
DidAndDatatokenMap
|
DidAndDatatokenMap
|
||||||
] = await getAssetsPoolsExchangesAndDatatokenMap(assets)
|
] = await getAssetsPoolsExchangesAndDatatokenMap(assets)
|
||||||
@ -460,19 +556,20 @@ export async function getAssetsBestPrices(
|
|||||||
const freePriceResponse = values[2]
|
const freePriceResponse = values[2]
|
||||||
for (const ddo of assets) {
|
for (const ddo of assets) {
|
||||||
const dataToken = ddo.dataToken.toLowerCase()
|
const dataToken = ddo.dataToken.toLowerCase()
|
||||||
const poolPrice: AssetsPoolPricePools[] = []
|
const poolPrice: AssetsPoolPricePool[] = []
|
||||||
const frePrice: AssetsFrePriceFixedRateExchanges[] = []
|
const frePrice: AssetsFrePriceFixedRateExchange[] = []
|
||||||
const freePrice: AssetFreePriceDispenser[] = []
|
const freePrice: AssetFreePriceDispenser[] = []
|
||||||
const pool = poolPriceResponse.find(
|
const pool = poolPriceResponse.find(
|
||||||
(pool: any) => pool.datatokenAddress === dataToken
|
(pool: AssetsPoolPricePool) => pool.datatokenAddress === dataToken
|
||||||
)
|
)
|
||||||
pool && poolPrice.push(pool)
|
pool && poolPrice.push(pool)
|
||||||
const fre = frePriceResponse.find(
|
const fre = frePriceResponse.find(
|
||||||
(fre: any) => fre.datatoken.address === dataToken
|
(fre: AssetsFrePriceFixedRateExchange) =>
|
||||||
|
fre.datatoken.address === dataToken
|
||||||
)
|
)
|
||||||
fre && frePrice.push(fre)
|
fre && frePrice.push(fre)
|
||||||
const free = freePriceResponse.find(
|
const free = freePriceResponse.find(
|
||||||
(free: any) => free.datatoken.address === dataToken
|
(free: AssetFreePriceDispenser) => free.datatoken.address === dataToken
|
||||||
)
|
)
|
||||||
free && freePrice.push(free)
|
free && freePrice.push(free)
|
||||||
const bestPrice = transformPriceToBestPrice(frePrice, poolPrice, freePrice)
|
const bestPrice = transformPriceToBestPrice(frePrice, poolPrice, freePrice)
|
||||||
@ -489,22 +586,22 @@ export async function getHighestLiquidityDIDs(
|
|||||||
chainIds: number[]
|
chainIds: number[]
|
||||||
): Promise<[string, number]> {
|
): Promise<[string, number]> {
|
||||||
const didList: string[] = []
|
const didList: string[] = []
|
||||||
let highestLiquidiyAssets: HighestLiquidityAssetsPools[] = []
|
let highestLiquidityAssets: HighestLiquidityAssetsPool[] = []
|
||||||
for (const chain of chainIds) {
|
for (const chain of chainIds) {
|
||||||
const queryContext = getQueryContext(Number(chain))
|
const queryContext = getQueryContext(Number(chain))
|
||||||
const fetchedPools: OperationResult<HighestLiquidityGraphAssets, any> =
|
const fetchedPools: OperationResult<HighestLiquidityGraphAssets, any> =
|
||||||
await fetchData(HighestLiquidityAssets, null, queryContext)
|
await fetchData(HighestLiquidityAssets, null, queryContext)
|
||||||
highestLiquidiyAssets = highestLiquidiyAssets.concat(
|
highestLiquidityAssets = highestLiquidityAssets.concat(
|
||||||
fetchedPools.data.pools
|
fetchedPools.data.pools
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
highestLiquidiyAssets
|
highestLiquidityAssets
|
||||||
.sort((a, b) => a.oceanReserve - b.oceanReserve)
|
.sort((a, b) => a.oceanReserve - b.oceanReserve)
|
||||||
.reverse()
|
.reverse()
|
||||||
for (let i = 0; i < highestLiquidiyAssets.length; i++) {
|
for (let i = 0; i < highestLiquidityAssets.length; i++) {
|
||||||
if (!highestLiquidiyAssets[i].datatokenAddress) continue
|
if (!highestLiquidityAssets[i].datatokenAddress) continue
|
||||||
const did = web3.utils
|
const did = web3.utils
|
||||||
.toChecksumAddress(highestLiquidiyAssets[i].datatokenAddress)
|
.toChecksumAddress(highestLiquidityAssets[i].datatokenAddress)
|
||||||
.replace('0x', 'did:op:')
|
.replace('0x', 'did:op:')
|
||||||
didList.push(did)
|
didList.push(did)
|
||||||
}
|
}
|
||||||
@ -515,3 +612,113 @@ 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(
|
||||||
|
assets: DDO[],
|
||||||
|
chainIds: number[]
|
||||||
|
): Promise<number> {
|
||||||
|
const datatokens: string[] = []
|
||||||
|
assets.forEach((ddo) => {
|
||||||
|
datatokens.push(ddo?.dataToken?.toLowerCase())
|
||||||
|
})
|
||||||
|
const queryVariables = {
|
||||||
|
datatokenId_in: datatokens
|
||||||
|
}
|
||||||
|
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): number {
|
||||||
|
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<UserLiquidity> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPoolSharesData(
|
||||||
|
accountId: string,
|
||||||
|
chainIds: number[]
|
||||||
|
): Promise<PoolShare[]> {
|
||||||
|
const variables = { user: accountId?.toLowerCase() }
|
||||||
|
const data: PoolShare[] = []
|
||||||
|
const result = await fetchDataForMultipleChains(
|
||||||
|
userPoolSharesQuery,
|
||||||
|
variables,
|
||||||
|
chainIds
|
||||||
|
)
|
||||||
|
for (let i = 0; i < result.length; i++) {
|
||||||
|
result[i].poolShares.forEach((poolShare: PoolShare) => {
|
||||||
|
data.push(poolShare)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserTokenOrders(
|
||||||
|
accountId: string,
|
||||||
|
chainIds: number[]
|
||||||
|
): Promise<OrdersData[]> {
|
||||||
|
const data: OrdersData[] = []
|
||||||
|
const variables = { user: accountId?.toLowerCase() }
|
||||||
|
|
||||||
|
try {
|
||||||
|
const tokenOrders = await fetchDataForMultipleChains(
|
||||||
|
UserTokenOrders,
|
||||||
|
variables,
|
||||||
|
chainIds
|
||||||
|
)
|
||||||
|
|
||||||
|
for (let i = 0; i < tokenOrders?.length; i++) {
|
||||||
|
tokenOrders[i].tokenOrders.forEach((tokenOrder: OrdersData) => {
|
||||||
|
data.push(tokenOrder)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -23,8 +23,8 @@
|
|||||||
"link": "/publish"
|
"link": "/publish"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "History",
|
"name": "Profile",
|
||||||
"link": "/history"
|
"link": "/profile"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user