1
0
mirror of https://github.com/oceanprotocol/commons.git synced 2023-03-15 18:03:00 +01:00

Merge pull request #101 from oceanprotocol/feature/web3-detection

Refactor web3 detection and bootstrapping process
This commit is contained in:
Matthias Kretschmann 2019-04-15 20:16:02 +02:00 committed by GitHub
commit 2aa46699a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 884 additions and 476 deletions

339
client/package-lock.json generated
View File

@ -1044,6 +1044,16 @@
"integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==", "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==",
"dev": true "dev": true
}, },
"@jest/types": {
"version": "24.7.0",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-24.7.0.tgz",
"integrity": "sha512-ipJUa2rFWiKoBqMKP63Myb6h9+iT3FHRTF2M8OR6irxWzItisa8i4dcSg14IbvmXUnBlHBlUQPYUHWyX3UPpYA==",
"dev": true,
"requires": {
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/yargs": "^12.0.9"
}
},
"@mrmlnc/readdir-enhanced": { "@mrmlnc/readdir-enhanced": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
@ -1071,15 +1081,16 @@
"integrity": "sha512-c1LvaH+e1tzow0gZLwSWe19ap+DrZuNmZfxBdwEVEPQXarI0jTXa5qVDoiBow8kBWaqSIUgFAzQOJW8rKdlS1A==" "integrity": "sha512-c1LvaH+e1tzow0gZLwSWe19ap+DrZuNmZfxBdwEVEPQXarI0jTXa5qVDoiBow8kBWaqSIUgFAzQOJW8rKdlS1A=="
}, },
"@oceanprotocol/squid": { "@oceanprotocol/squid": {
"version": "0.5.3", "version": "0.5.4",
"resolved": "https://registry.npmjs.org/@oceanprotocol/squid/-/squid-0.5.3.tgz", "resolved": "https://registry.npmjs.org/@oceanprotocol/squid/-/squid-0.5.4.tgz",
"integrity": "sha512-tPOg+z9hYYN/6QoM7BDw1N5uQQQvbEj8xITtC1aYEoAM1tHaauAIecQjVQQaumkepaz8F1t1kmWUP2t7RQVqgg==", "integrity": "sha512-ozOTLNJX0GiKShum09e13LZ9zUKedWA5G8+H3Pwu3+lbdLYkRSeCg5eLXDUJxUN12+leiCnW/TZDoV9ZOmcd4w==",
"requires": { "requires": {
"@oceanprotocol/keeper-contracts": "^0.9.0", "@oceanprotocol/keeper-contracts": "^0.9.1",
"bignumber.js": "^8.0.1", "bignumber.js": "^8.1.1",
"deprecated-decorator": "^0.1.6", "deprecated-decorator": "^0.1.6",
"ethereumjs-util": "^6.0.0", "ethereumjs-util": "^6.1.0",
"node-fetch": "^2.3.0", "node-fetch": "^2.3.0",
"save-file": "^2.3.1",
"uuid": "^3.3.2", "uuid": "^3.3.2",
"web3": "1.0.0-beta.37", "web3": "1.0.0-beta.37",
"whatwg-url": "^7.0.0" "whatwg-url": "^7.0.0"
@ -1106,6 +1117,12 @@
"resolved": "https://registry.npmjs.org/@oceanprotocol/typographies/-/typographies-0.1.0.tgz", "resolved": "https://registry.npmjs.org/@oceanprotocol/typographies/-/typographies-0.1.0.tgz",
"integrity": "sha512-kMsZsqvzpz9KzVbVZzllwhPoIC3zbqsdRrClagZL/C2PHzgLrKGC1kYn3gPt0RMIFg9ZjrwieKaxlgIK9i9zzg==" "integrity": "sha512-kMsZsqvzpz9KzVbVZzllwhPoIC3zbqsdRrClagZL/C2PHzgLrKGC1kYn3gPt0RMIFg9ZjrwieKaxlgIK9i9zzg=="
}, },
"@sheerun/mutationobserver-shim": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz",
"integrity": "sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q==",
"dev": true
},
"@svgr/babel-plugin-add-jsx-attribute": { "@svgr/babel-plugin-add-jsx-attribute": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz",
@ -1394,6 +1411,12 @@
"integrity": "sha1-kU2r1QVG2bAUKAbkLHK8fCt+B4c=", "integrity": "sha1-kU2r1QVG2bAUKAbkLHK8fCt+B4c=",
"dev": true "dev": true
}, },
"@types/istanbul-lib-coverage": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.0.tgz",
"integrity": "sha512-eAtOAFZefEnfJiRFQBGw1eYqa5GTLCZ1y86N0XSI/D6EB+E8z6VPV/UL7Gi5UEclFqoQk+6NRqEDsfmDLXn8sg==",
"dev": true
},
"@types/jest": { "@types/jest": {
"version": "24.0.11", "version": "24.0.11",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.11.tgz", "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.11.tgz",
@ -1552,6 +1575,12 @@
"@types/underscore": "*" "@types/underscore": "*"
} }
}, },
"@types/yargs": {
"version": "12.0.12",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.12.tgz",
"integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==",
"dev": true
},
"@webassemblyjs/ast": { "@webassemblyjs/ast": {
"version": "1.7.11", "version": "1.7.11",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz",
@ -2417,6 +2446,11 @@
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
"dev": true "dev": true
}, },
"atob-lite": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz",
"integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY="
},
"autoprefixer": { "autoprefixer": {
"version": "9.5.1", "version": "9.5.1",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.5.1.tgz", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.5.1.tgz",
@ -4818,6 +4852,26 @@
"randomfill": "^1.0.3" "randomfill": "^1.0.3"
} }
}, },
"css": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz",
"integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"source-map": "^0.6.1",
"source-map-resolve": "^0.5.2",
"urix": "^0.1.0"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"css-blank-pseudo": { "css-blank-pseudo": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz", "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz",
@ -5217,6 +5271,12 @@
"integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==",
"dev": true "dev": true
}, },
"css.escape": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
"integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=",
"dev": true
},
"cssdb": { "cssdb": {
"version": "4.4.0", "version": "4.4.0",
"resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz",
@ -5920,6 +5980,12 @@
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
"dev": true "dev": true
}, },
"diff-sequences": {
"version": "24.3.0",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz",
"integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==",
"dev": true
},
"diffie-hellman": { "diffie-hellman": {
"version": "5.0.3", "version": "5.0.3",
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
@ -6017,6 +6083,47 @@
"entities": "^1.1.1" "entities": "^1.1.1"
} }
}, },
"dom-testing-library": {
"version": "3.19.1",
"resolved": "https://registry.npmjs.org/dom-testing-library/-/dom-testing-library-3.19.1.tgz",
"integrity": "sha512-cv5k+OiDhmdjHimDcal5Yz6yHv6nXwhzW3wyXrFeIGk7jSdqqFxqzhjriPvYMrV8XNNZHUDwp0dTu6SeCtO/4w==",
"dev": true,
"requires": {
"@babel/runtime": "^7.3.4",
"@sheerun/mutationobserver-shim": "^0.3.2",
"pretty-format": "^24.5.0",
"wait-for-expect": "^1.1.0"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"pretty-format": {
"version": "24.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.7.0.tgz",
"integrity": "sha512-apen5cjf/U4dj7tHetpC7UEFCvtAgnNZnBDkfPv3fokzIqyOJckAG9OlAPC1BlFALnqT/lGB2tl9EJjlK6eCsA==",
"dev": true,
"requires": {
"@jest/types": "^24.7.0",
"ansi-regex": "^4.0.0",
"ansi-styles": "^3.2.0",
"react-is": "^16.8.4"
}
}
}
},
"dom-walk": { "dom-walk": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz",
@ -6090,6 +6197,11 @@
"create-hmac": "^1.1.4" "create-hmac": "^1.1.4"
} }
}, },
"dtype": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dtype/-/dtype-2.0.0.tgz",
"integrity": "sha1-zQUjI84GFETs0uj1dI9popvihDQ="
},
"duplexer": { "duplexer": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
@ -7487,6 +7599,11 @@
"schema-utils": "^1.0.0" "schema-utils": "^1.0.0"
} }
}, },
"file-saver": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.1.tgz",
"integrity": "sha512-dCB3K7/BvAcUmtmh1DzFdv0eXSVJ9IAFt1mw3XZfAexodNRoE29l3xB2EX4wH2q8m/UTzwzEPq/ArYk98kUkBQ=="
},
"file-type": { "file-type": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz",
@ -7620,6 +7737,14 @@
"integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=",
"dev": true "dev": true
}, },
"flatten-vertex-data": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/flatten-vertex-data/-/flatten-vertex-data-1.0.2.tgz",
"integrity": "sha512-BvCBFK2NZqerFTdMDgqfHBwxYWnxeCkwONsw6PvBMcUXqo8U/KDWwmXhqx1x2kLIg7DqIsJfOaJFOmlua3Lxuw==",
"requires": {
"dtype": "^2.0.0"
}
},
"flush-write-stream": { "flush-write-stream": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
@ -10005,6 +10130,11 @@
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
"dev": true "dev": true
}, },
"is-base64": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/is-base64/-/is-base64-0.1.0.tgz",
"integrity": "sha512-WRRyllsGXJM7ZN7gPTCCQ/6wNPTRDwiWdPK66l5sJzcU/oOzcIcRRf0Rux8bkpox/1yjt0F6VJRsQOIG2qz5sg=="
},
"is-binary-path": { "is-binary-path": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
@ -10014,6 +10144,11 @@
"binary-extensions": "^1.0.0" "binary-extensions": "^1.0.0"
} }
}, },
"is-blob": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-blob/-/is-blob-1.0.0.tgz",
"integrity": "sha1-o9fZb+HD/wZex84nwsIea6ksGDI="
},
"is-buffer": { "is-buffer": {
"version": "1.1.6", "version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
@ -10861,6 +10996,123 @@
"detect-newline": "^2.1.0" "detect-newline": "^2.1.0"
} }
}, },
"jest-dom": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/jest-dom/-/jest-dom-3.1.3.tgz",
"integrity": "sha512-V9LdySiA74/spcAKEG3FRMRKnisKlcYr3EeCNYI4n7CWNE7uYg5WoBUHeGXirjWjRYLLZ5vx8rUaR/6x6o75oQ==",
"dev": true,
"requires": {
"chalk": "^2.4.1",
"css": "^2.2.3",
"css.escape": "^1.5.1",
"jest-diff": "^24.0.0",
"jest-matcher-utils": "^24.0.0",
"lodash": "^4.17.11",
"pretty-format": "^24.0.0",
"redent": "^2.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"indent-string": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
"integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=",
"dev": true
},
"jest-diff": {
"version": "24.7.0",
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.7.0.tgz",
"integrity": "sha512-ULQZ5B1lWpH70O4xsANC4tf4Ko6RrpwhE3PtG6ERjMg1TiYTC2Wp4IntJVGro6a8HG9luYHhhmF4grF0Pltckg==",
"dev": true,
"requires": {
"chalk": "^2.0.1",
"diff-sequences": "^24.3.0",
"jest-get-type": "^24.3.0",
"pretty-format": "^24.7.0"
}
},
"jest-get-type": {
"version": "24.3.0",
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz",
"integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==",
"dev": true
},
"jest-matcher-utils": {
"version": "24.7.0",
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.7.0.tgz",
"integrity": "sha512-158ieSgk3LNXeUhbVJYRXyTPSCqNgVXOp/GT7O94mYd3pk/8+odKTyR1JLtNOQSPzNi8NFYVONtvSWA/e1RDXg==",
"dev": true,
"requires": {
"chalk": "^2.0.1",
"jest-diff": "^24.7.0",
"jest-get-type": "^24.3.0",
"pretty-format": "^24.7.0"
}
},
"pretty-format": {
"version": "24.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.7.0.tgz",
"integrity": "sha512-apen5cjf/U4dj7tHetpC7UEFCvtAgnNZnBDkfPv3fokzIqyOJckAG9OlAPC1BlFALnqT/lGB2tl9EJjlK6eCsA==",
"dev": true,
"requires": {
"@jest/types": "^24.7.0",
"ansi-regex": "^4.0.0",
"ansi-styles": "^3.2.0",
"react-is": "^16.8.4"
}
},
"redent": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz",
"integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=",
"dev": true,
"requires": {
"indent-string": "^3.0.0",
"strip-indent": "^2.0.0"
}
},
"strip-indent": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz",
"integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=",
"dev": true
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
"jest-each": { "jest-each": {
"version": "23.6.0", "version": "23.6.0",
"resolved": "https://registry.npmjs.org/jest-each/-/jest-each-23.6.0.tgz", "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-23.6.0.tgz",
@ -19382,6 +19634,16 @@
"shallowequal": "^1.0.1" "shallowequal": "^1.0.1"
} }
}, },
"react-testing-library": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/react-testing-library/-/react-testing-library-6.1.2.tgz",
"integrity": "sha512-z69lhRDGe7u/NOjDCeFRoe1cB5ckJ4656n0tj/Fdcr6OoBUu7q9DBw0ftR7v5i3GRpdSWelnvl+feZFOyXyxwg==",
"dev": true,
"requires": {
"@babel/runtime": "^7.4.2",
"dom-testing-library": "^3.19.0"
}
},
"react-transition-group": { "react-transition-group": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-3.0.0.tgz", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-3.0.0.tgz",
@ -20524,6 +20786,34 @@
} }
} }
}, },
"save-file": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/save-file/-/save-file-2.3.1.tgz",
"integrity": "sha512-VOD2Ojb1/kuj0XbvSXzZ5xr4rRSZD8f+HzKWGztXNp93gBQDj3njFt9HMhmLtnwd7q0BjJkzLXqd8M2+PFS1qg==",
"requires": {
"file-saver": "^2.0.0-rc.4",
"is-blob": "^1.0.0",
"is-buffer": "^2.0.0",
"simple-mime": "^0.1.0",
"to-array-buffer": "^3.2.0",
"write": "^1.0.0"
},
"dependencies": {
"is-buffer": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
"integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw=="
},
"write": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
"integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
"requires": {
"mkdirp": "^0.5.1"
}
}
}
},
"sax": { "sax": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@ -20856,6 +21146,11 @@
"simple-concat": "^1.0.0" "simple-concat": "^1.0.0"
} }
}, },
"simple-mime": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/simple-mime/-/simple-mime-0.1.0.tgz",
"integrity": "sha1-lfUXxPRm18/1YacfydqyWW6p7y4="
},
"simple-swizzle": { "simple-swizzle": {
"version": "0.2.2", "version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
@ -21416,6 +21711,15 @@
} }
} }
}, },
"string-to-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-to-arraybuffer/-/string-to-arraybuffer-1.0.2.tgz",
"integrity": "sha512-DaGZidzi93dwjQen5I2osxR9ERS/R7B1PFyufNMnzhj+fmlDQAc1DSDIJVJhgI8Oq221efIMbABUBdPHDRt43Q==",
"requires": {
"atob-lite": "^2.0.0",
"is-base64": "^0.1.0"
}
},
"string-width": { "string-width": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
@ -21958,6 +22262,23 @@
"integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=",
"dev": true "dev": true
}, },
"to-array-buffer": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/to-array-buffer/-/to-array-buffer-3.2.0.tgz",
"integrity": "sha512-zN33mwi0gpL+7xW1ITLfJ48CEj6ZQW0ZAP0MU+2W3kEY0PAIncyuxmD4OqkUVhPAbTP7amq9j/iwvZKYS+lzSQ==",
"requires": {
"flatten-vertex-data": "^1.0.2",
"is-blob": "^2.0.1",
"string-to-arraybuffer": "^1.0.0"
},
"dependencies": {
"is-blob": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-blob/-/is-blob-2.0.1.tgz",
"integrity": "sha512-SmqVJYMnAeqrKLcwq6TXu1rpAg3yipVlMZIqR5u510rxoOzJGW9GQY6g+WtWkcc44pjbWAuxzZDCkbgf5e6r0Q=="
}
}
},
"to-arraybuffer": { "to-arraybuffer": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
@ -22644,6 +22965,12 @@
"browser-process-hrtime": "^0.1.2" "browser-process-hrtime": "^0.1.2"
} }
}, },
"wait-for-expect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/wait-for-expect/-/wait-for-expect-1.1.1.tgz",
"integrity": "sha512-vd9JOqqEcBbCDhARWhW85ecjaEcfBLuXgVBqatfS3iw6oU4kzAcs+sCNjF+TC9YHPImCW7ypsuQc+htscIAQCw==",
"dev": true
},
"walker": { "walker": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz",

View File

@ -46,8 +46,10 @@
"@types/react-router-dom": "^4.3.1", "@types/react-router-dom": "^4.3.1",
"@types/react-transition-group": "^2.8.0", "@types/react-transition-group": "^2.8.0",
"@types/web3": "^1.0.18", "@types/web3": "^1.0.18",
"jest-dom": "^3.1.3",
"node-sass": "^4.11.0", "node-sass": "^4.11.0",
"react-scripts": "^2.1.8", "react-scripts": "^2.1.8",
"react-testing-library": "^6.1.2",
"typescript": "^3.4.2" "typescript": "^3.4.2"
}, },
"repository": { "repository": {

View File

@ -1,275 +1,44 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import Web3 from 'web3'
import { BrowserRouter as Router } from 'react-router-dom' import { BrowserRouter as Router } from 'react-router-dom'
import { Logger } from '@oceanprotocol/squid'
import Header from './components/organisms/Header' import Header from './components/organisms/Header'
import Footer from './components/organisms/Footer' import Footer from './components/organisms/Footer'
import Spinner from './components/atoms/Spinner' import Spinner from './components/atoms/Spinner'
import { User } from './context/User' import { User } from './context'
import { provideOcean } from './ocean' import UserProvider from './context/UserProvider'
import Routes from './Routes' import Routes from './Routes'
import './styles/global.scss' import './styles/global.scss'
import styles from './App.module.scss' import styles from './App.module.scss'
import { export default class App extends Component {
nodeHost,
nodePort,
nodeScheme,
faucetHost,
faucetPort,
faucetScheme
} from './config/config'
const POLL_ACCOUNTS = 1000 // every 1s
const POLL_NETWORK = POLL_ACCOUNTS * 60 // every 1 min
declare global {
interface Window {
web3: Web3
ethereum: any
}
}
interface AppState {
isLogged: boolean
isLoading: boolean
isWeb3: boolean
isNile: boolean
account: string
balance: {
eth: number
ocn: number
}
network: string
web3: Web3
ocean: any
startLogin: () => void
message: string
}
class App extends Component<{}, AppState> {
private accountsInterval: any
private networkInterval: any
public startLogin = (event?: any) => {
if (event) {
event.preventDefault()
}
this.startLoginProcess()
}
private requestFromFaucet = async () => {
if (this.state.account !== '') {
try {
const response = await fetch(
`${faucetScheme}://${faucetHost}:${faucetPort}/faucet`,
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
address: this.state.account,
agent: 'commons'
})
}
)
return response.json()
} catch (error) {
Logger.log('requestFromFaucet', error)
}
} else {
// no account found
}
}
public state = {
isLogged: false,
isLoading: true,
isWeb3: false,
isNile: false,
balance: {
eth: 0,
ocn: 0
},
network: '',
web3: new Web3(
new Web3.providers.HttpProvider(
`${nodeScheme}://${nodeHost}:${nodePort}`
)
),
account: '',
ocean: {} as any,
startLogin: this.startLogin,
requestFromFaucet: this.requestFromFaucet,
message: 'Connecting to Ocean...'
}
public async componentDidMount() {
await this.bootstrap()
this.initAccountsPoll()
this.initNetworkPoll()
}
private bootstrap = async () => {
try {
if (window.web3) {
const web3 = new Web3(window.web3.currentProvider)
const { ocean } = await provideOcean(web3)
const accounts = await ocean.accounts.list()
const network = await ocean.keeper.getNetworkName()
const isNile = network === 'Nile'
if (accounts.length > 0) {
const balance = await accounts[0].getBalance()
this.setState({
isWeb3: true,
isLogged: true,
isNile,
ocean,
web3,
balance,
network,
account: accounts[0].getId(),
isLoading: false
})
} else {
this.setState({
isWeb3: true,
isNile,
ocean,
web3,
network,
isLoading: false
})
}
} else {
const { ocean } = await provideOcean(this.state.web3)
const network = await ocean.keeper.getNetworkName()
const isNile = network === 'Nile'
this.setState({
isNile,
ocean,
network,
isLoading: false
})
}
} catch (e) {
// error in bootstrap process
// show error connecting to ocean
Logger.log('web3 error', e)
this.setState({
isLoading: false
})
}
}
private initAccountsPoll() {
if (!this.accountsInterval) {
this.accountsInterval = setInterval(
this.fetchAccounts,
POLL_ACCOUNTS
)
}
}
private initNetworkPoll() {
if (!this.networkInterval) {
this.networkInterval = setInterval(this.fetchNetwork, POLL_NETWORK)
}
}
private fetchAccounts = async () => {
const { web3 } = window
const { ocean } = this.state
if (web3) {
const accounts = await ocean.accounts.list()
if (accounts.length > 0) {
const account = accounts[0].getId()
if (account !== this.state.account) {
const balance = await accounts[0].getBalance()
this.setState({ account, balance, isLogged: true })
}
} else {
this.state.isLogged !== false &&
this.setState({ isLogged: false, account: '' })
}
} else {
this.state.isWeb3 !== false &&
this.setState({
isWeb3: false,
isLogged: false
})
}
}
private fetchNetwork = async () => {
const { web3 } = window
const { ocean } = this.state
if (web3) {
const network = await ocean.keeper.getNetworkName()
const isNile = network === 'Nile'
network !== this.state.network && this.setState({ isNile, network })
}
}
private startLoginProcess = async () => {
try {
if (this.state.isWeb3 && window.ethereum) {
await window.ethereum.enable()
const accounts = await this.state.ocean.accounts.list()
if (accounts.length > 0) {
const balance = await accounts[0].getBalance()
this.setState({
isLogged: true,
balance,
account: accounts[0].getId()
})
} else {
// not unlocked
}
} else {
// no metamask/mist, show installation guide!
}
} catch (e) {
Logger.log('error logging', e)
// error in logging process
// show error
// rerun bootstrap process?
}
}
public render() { public render() {
return ( return (
<div className={styles.app}> <UserProvider>
<User.Provider value={this.state}> <div className={styles.app}>
<Router> <Router>
<> <>
<Header /> <Header />
<main className={styles.main}> <main className={styles.main}>
{this.state.isLoading ? ( <User.Consumer>
<div className={styles.loader}> {states =>
<Spinner message={this.state.message} /> states.isLoading ? (
</div> <div className={styles.loader}>
) : ( <Spinner
<Routes /> message={states.message}
)} />
</div>
) : (
<Routes />
)
}
</User.Consumer>
</main> </main>
<Footer /> <Footer />
</> </>
</Router> </Router>
</User.Provider> </div>
</div> </UserProvider>
) )
} }
} }
export default App

View File

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import { Route, Switch } from 'react-router-dom' import { Route, Switch } from 'react-router-dom'
import withTracker from './withTracker' import withTracker from './hoc/withTracker'
import About from './routes/About' import About from './routes/About'
import Details from './routes/Details/' import Details from './routes/Details/'

View File

@ -1,16 +1,54 @@
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' import { render } from 'react-testing-library'
import { BrowserRouter as Router } from 'react-router-dom'
import Button from './Button' import Button from './Button'
it('Button renders without crashing', () => { describe('Button', () => {
const div = document.createElement('div') it('default renders correctly without crashing', () => {
ReactDOM.render( const { getByTestId } = render(
<> <Button data-testid="button-default">I am a default button</Button>
<Button>I am a button</Button> )
<Button primary>I am a primary button</Button> expect(getByTestId('button-default')).toHaveTextContent('default')
<Button href="https://hello.com">I am a button</Button> })
</>,
div it('primary renders correctly without crashing', () => {
) const { getByTestId } = render(
ReactDOM.unmountComponentAtNode(div) <Button data-testid="button-primary" primary>
I am a primary button
</Button>
)
expect(getByTestId('button-primary')).toHaveTextContent('primary')
expect(getByTestId('button-primary').className).toMatch(/buttonPrimary/)
})
it('Link renders correctly without crashing', () => {
const { getByTestId } = render(
<Router>
<Button data-testid="button-to" to="https://hello.com">
I am a Link button
</Button>
</Router>
)
expect(getByTestId('button-to')).toHaveTextContent('Link')
})
it('href renders correctly without crashing', () => {
const { getByTestId } = render(
<Button data-testid="button-href" href="https://hello.com">
I am a href button
</Button>
)
expect(getByTestId('button-href')).toHaveTextContent('href')
expect(getByTestId('button-href').nodeName).toBe('A')
})
it('link renders correctly without crashing', () => {
const { getByTestId } = render(
<Button data-testid="button-link" link>
I am a link button
</Button>
)
expect(getByTestId('button-link')).toHaveTextContent('link')
expect(getByTestId('button-link').className).toMatch(/link/)
})
}) })

View File

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import cx from 'classnames' import cx from 'classnames'
import { User } from '../../../context/User' import { User } from '../../../context'
import styles from './Indicator.module.scss' import styles from './Indicator.module.scss'
const Indicator = ({ const Indicator = ({

View File

@ -1,6 +1,6 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import Dotdotdot from 'react-dotdotdot' import Dotdotdot from 'react-dotdotdot'
import { User } from '../../../context/User' import { User } from '../../../context'
import styles from './Popover.module.scss' import styles from './Popover.module.scss'
export default class Popover extends PureComponent<{ export default class Popover extends PureComponent<{

View File

@ -1,10 +1,9 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { Logger } from '@oceanprotocol/squid' import { Logger } from '@oceanprotocol/squid'
import { User } from '../../context/User' import { User } from '../../context'
import Spinner from '../atoms/Spinner' import Spinner from '../atoms/Spinner'
import Asset from '../molecules/Asset' import Asset from '../molecules/Asset'
import Web3message from './Web3message'
import styles from './AssetsUser.module.scss' import styles from './AssetsUser.module.scss'
export default class AssetsUser extends PureComponent< export default class AssetsUser extends PureComponent<
@ -13,32 +12,42 @@ export default class AssetsUser extends PureComponent<
> { > {
public state = { results: [], isLoading: true } public state = { results: [], isLoading: true }
public _isMounted: boolean = false
public componentDidMount() { public componentDidMount() {
this.searchOcean() this._isMounted = true
this._isMounted && this.searchOcean()
}
public componentWillUnmount() {
this._isMounted = false
} }
private async searchOcean() { private async searchOcean() {
if (this.context.account) { const { account, ocean } = this.context
this.context.ocean.keeper.didRegistry.contract.getPastEvents(
if (account) {
ocean.keeper.didRegistry.contract.getPastEvents(
'DIDAttributeRegistered', 'DIDAttributeRegistered',
{ {
filter: { _owner: this.context.account }, filter: { _owner: account },
fromBlock: 0, fromBlock: 0,
toBlock: 'latest' toBlock: 'latest'
}, },
async (error: any, events: any) => { async (error: any, events: any) => {
if (error) { if (error) {
Logger.log('error retrieving', error) Logger.log('error retrieving', error)
this.setState({ isLoading: false }) this._isMounted && this.setState({ isLoading: false })
} else { } else {
const results = [] const results = []
for (const event of events) { for (const event of events) {
const ddo = await this.context.ocean.assets.resolve( const ddo = await ocean.assets.resolve(
`did:op:${event.returnValues._did.substring(2)}` `did:op:${event.returnValues._did.substring(2)}`
) )
results.push(ddo) results.push(ddo)
} }
this.setState({ results, isLoading: false }) this._isMounted &&
this.setState({ results, isLoading: false })
} }
} }
) )
@ -48,48 +57,51 @@ export default class AssetsUser extends PureComponent<
} }
public render() { public render() {
return this.context.isNile && this.context.account ? ( const { account, isNile } = this.context
<div className={styles.assetsUser}>
{this.props.recent && (
<h2 className={styles.subTitle}>
Your Latest Published Data Sets
</h2>
)}
{this.state.isLoading ? ( return (
<Spinner /> isNile &&
) : this.state.results.length ? ( account && (
<> <div className={styles.assetsUser}>
{this.state.results {this.props.recent && (
.slice( <h2 className={styles.subTitle}>
0, Your Latest Published Data Sets
this.props.recent </h2>
? this.props.recent )}
: undefined
) {this.state.isLoading ? (
.filter(asset => !!asset) <Spinner />
.map((asset: any) => ( ) : this.state.results.length ? (
<Asset <>
list={this.props.list} {this.state.results
key={asset.id} .slice(
asset={asset} 0,
/> this.props.recent
))} ? this.props.recent
{this.props.recent && ( : undefined
<Link className={styles.link} to={'/history'}> )
All Data Sets .filter(asset => !!asset)
</Link> .map((asset: any) => (
)} <Asset
</> list={this.props.list}
) : ( key={asset.id}
<div className={styles.empty}> asset={asset}
<p>No Data Sets Yet.</p> />
<Link to="/publish">+ Publish A Data Set</Link> ))}
</div> {this.props.recent && (
)} <Link className={styles.link} to={'/history'}>
</div> All Data Sets
) : ( </Link>
<Web3message /> )}
</>
) : (
<div className={styles.empty}>
<p>No Data Sets Yet.</p>
<Link to="/publish">+ Publish A Data Set</Link>
</div>
)}
</div>
)
) )
} }
} }

View File

@ -1,7 +1,7 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import { NavLink } from 'react-router-dom' import { NavLink } from 'react-router-dom'
import { ReactComponent as Logo } from '@oceanprotocol/art/logo/logo.svg' import { ReactComponent as Logo } from '@oceanprotocol/art/logo/logo.svg'
import { User } from '../../context/User' import { User } from '../../context'
import AccountStatus from '../molecules/AccountStatus' import AccountStatus from '../molecules/AccountStatus'
import styles from './Header.module.scss' import styles from './Header.module.scss'

View File

@ -0,0 +1,11 @@
import React from 'react'
import { render } from 'react-testing-library'
import Web3message from './Web3message'
describe('Web3message', () => {
it('default renders without crashing', () => {
const { container } = render(<Web3message />)
expect(container.firstChild).toBeInTheDocument()
})
})

View File

@ -3,69 +3,54 @@ import Dotdotdot from 'react-dotdotdot'
import Button from '../atoms/Button' import Button from '../atoms/Button'
import AccountStatus from '../molecules/AccountStatus' import AccountStatus from '../molecules/AccountStatus'
import styles from './Web3message.module.scss' import styles from './Web3message.module.scss'
import { User } from '../../context/User' import { User } from '../../context'
import content from '../../data/web3message.json'
export default class Web3message extends PureComponent { export default class Web3message extends PureComponent {
private noWeb3 = () => ( private message = (
message: string,
account?: string,
unlockAccounts?: () => any
) => (
<div className={styles.message}> <div className={styles.message}>
<AccountStatus className={styles.status} /> Not a Web3 Browser. For <AccountStatus className={styles.status} />{' '}
publishing or downloading an asset you need to{' '} {account ? (
<a <Dotdotdot clamp={1}>
href="https://docs.oceanprotocol.com/tutorials/metamask-setup/" {message}
target="_blank" <code className={styles.account}>{account}</code>
rel="noopener noreferrer" </Dotdotdot>
> ) : (
setup MetaMask <>
</a>{' '} <span dangerouslySetInnerHTML={{ __html: message }} />{' '}
or use any other Web3-capable plugin or browser. {unlockAccounts && (
</div> <Button onClick={() => unlockAccounts()} link>
) Unlock Account
</Button>
private unlockAccount = (states: any) => ( )}
<div className={styles.message}> </>
<AccountStatus className={styles.status} /> Account locked. For )}
publishing and downloading an asset you need to unlock your Web3
account.{' '}
<Button link onClick={states.startLogin}>
Unlock account
</Button>
</div>
)
private haveAccount = (account: string) => (
<div className={styles.message}>
<AccountStatus className={styles.status} />
<Dotdotdot clamp={1}>
Connected with account
<code className={styles.account}>{account}</code>
</Dotdotdot>
</div>
)
private wrongNetwork = (network: string) => (
<div className={styles.message}>
<AccountStatus className={styles.status} /> Not connected to Nile
network, but to {network}.<br />
Please connect in MetaMask with Custom RPC{' '}
<code>{`https://nile.dev-ocean.com`}</code>
</div> </div>
) )
public render() { public render() {
return ( const {
<User.Consumer> isWeb3,
{states => isNile,
!states.isWeb3 isLogged,
? this.noWeb3() account,
: !states.isNile unlockAccounts
? this.wrongNetwork(states.network) } = this.context
: !states.isLogged
? this.unlockAccount(states) return !isWeb3
: states.isLogged ? this.message(content.noweb3)
? this.haveAccount(states.account) : !isNile
: null ? this.message(content.wrongNetwork)
} : !isLogged
</User.Consumer> ? this.message(content.noAccount, '', unlockAccounts)
) : isLogged
? this.message(content.hasAccount, account)
: null
} }
} }
Web3message.contextType = User

View File

@ -0,0 +1,244 @@
import React, { PureComponent } from 'react'
import Web3 from 'web3'
import { Logger } from '@oceanprotocol/squid'
import { User } from '.'
import { provideOcean, requestFromFaucet, FaucetResponse } from '../ocean'
import { nodeHost, nodePort, nodeScheme } from '../config/config'
const POLL_ACCOUNTS = 1000 // every 1s
const POLL_NETWORK = POLL_ACCOUNTS * 60 // every 1 min
declare global {
interface Window {
web3: Web3
ethereum: {
enable(): void
host: string
supportsSubscriptions(): boolean
send(method: string, parameters: any[]): Promise<any[]>
sendBatch(methods: any[], moduleInstance: any): Promise<any[]>
}
}
}
interface UserProviderState {
isLogged: boolean
isLoading: boolean
isWeb3: boolean
isNile: boolean
account: string
balance: {
eth: number
ocn: number
}
network: string
web3: Web3
ocean: any
requestFromFaucet(account: string): Promise<FaucetResponse>
unlockAccounts(): Promise<any>
message: string
}
export default class UserProvider extends PureComponent<{}, UserProviderState> {
private unlockAccounts = async () => {
try {
await window.ethereum.enable()
} catch (error) {
// User denied account access...
return null
}
}
public state = {
isLogged: false,
isLoading: true,
isWeb3: false,
isNile: false,
balance: {
eth: 0,
ocn: 0
},
network: '',
web3: new Web3(
new Web3.providers.HttpProvider(
`${nodeScheme}://${nodeHost}:${nodePort}`
)
),
account: '',
ocean: {} as any,
requestFromFaucet: () => requestFromFaucet(''),
unlockAccounts: () => this.unlockAccounts(),
message: 'Connecting to Ocean...'
}
private accountsInterval: any = null
private networkInterval: any = null
public async componentDidMount() {
await this.bootstrap()
this.initAccountsPoll()
this.initNetworkPoll()
}
private initAccountsPoll() {
if (!this.accountsInterval) {
this.accountsInterval = setInterval(
this.fetchAccounts,
POLL_ACCOUNTS
)
}
}
private initNetworkPoll() {
if (!this.networkInterval) {
this.networkInterval = setInterval(this.fetchNetwork, POLL_NETWORK)
}
}
private getWeb3 = async () => {
// Modern dapp browsers
if (window.ethereum) {
window.web3 = new Web3(window.ethereum)
return window.web3
}
// Legacy dapp browsers
else if (window.web3) {
window.web3 = new Web3(window.web3.currentProvider)
return window.web3
}
// Non-dapp browsers
else {
return null
}
}
private bootstrap = async () => {
try {
//
// Start with Web3 detection only
//
this.setState({ message: 'Setting up Web3...' })
let web3 = await this.getWeb3()
web3
? this.setState({ isWeb3: true })
: this.setState({ isWeb3: false })
// Modern & legacy dapp browsers
if (web3 && this.state.isWeb3) {
//
// Detecting network with window.web3
//
let isNile
await window.web3.eth.net.getId((err, netId) => {
if (err) return
isNile = netId === 8995
const network = isNile ? 'Nile' : netId.toString()
if (
isNile !== this.state.isNile ||
network !== this.state.network
) {
this.setState({ isNile, network })
}
})
if (!isNile) {
web3 = this.state.web3 // eslint-disable-line
}
//
// Provide the Ocean
//
this.setState({ message: 'Connecting to Ocean...' })
const { ocean } = await provideOcean(web3)
this.setState({ ocean, message: 'Getting accounts...' })
// Get accounts
await this.fetchAccounts()
this.setState({ isLoading: false, message: '' })
}
// Non-dapp browsers
else {
this.setState({ message: 'Connecting to Ocean...' })
const { ocean } = await provideOcean(this.state.web3)
this.setState({ ocean, isLoading: false })
this.fetchNetwork()
}
} catch (e) {
// error in bootstrap process
// show error connecting to ocean
Logger.log('web3 error', e)
this.setState({ isLoading: false })
}
}
private fetchAccounts = async () => {
const { ocean, isWeb3, isLogged, isNile } = this.state
if (isWeb3) {
let accounts
// Modern dapp browsers
if (window.ethereum && !isLogged && isNile) {
// simply set to empty, and have user click a button somewhere
// to initiate account unlocking
accounts = []
// alternatively, automatically prompt for account unlocking
// await this.unlockAccounts()
}
accounts = await ocean.accounts.list()
if (accounts.length > 0) {
const account = await accounts[0].getId()
if (account !== this.state.account) {
this.setState({
account,
isLogged: true,
requestFromFaucet: () => requestFromFaucet(account)
})
await this.fetchBalance(accounts[0])
}
} else {
!isLogged && this.setState({ isLogged: false, account: '' })
}
}
}
private fetchBalance = async (account: any) => {
const balance = await account.getBalance()
const { eth, ocn } = balance
if (eth !== this.state.balance.eth || ocn !== this.state.balance.ocn) {
this.setState({ balance: { eth, ocn } })
}
}
private fetchNetwork = async () => {
const { ocean, isWeb3 } = this.state
if (isWeb3) {
const network = await ocean.keeper.getNetworkName()
const isNile = network === 'Nile'
network !== this.state.network && this.setState({ isNile, network })
}
}
public render() {
return (
<User.Provider value={this.state}>
{this.props.children}
</User.Provider>
)
}
}

View File

@ -13,10 +13,11 @@ export const User = React.createContext({
ocn: 0 ocn: 0
}, },
network: '', network: '',
startLogin: () => {
/* empty */
},
requestFromFaucet: () => { requestFromFaucet: () => {
/* empty */ /* empty */
} },
unlockAccounts: () => {
/* empty */
},
message: ''
}) })

View File

@ -0,0 +1,6 @@
{
"noweb3": "Not a Web3 Browser. For publishing and downloading an asset you need to <a href='https://docs.oceanprotocol.com/tutorials/metamask-setup/' target='_blank' rel='noopener noreferrer'>setup MetaMask</a> or use any other Web3-capable plugin or browser.",
"noAccount": "No accounts detected. For publishing and downloading an asset you need to unlock your Web3 account.",
"hasAccount": "Connected with account ",
"wrongNetwork": "Not connected to Nile network.<br />Please connect in MetaMask with Custom RPC <code>https://nile.dev-ocean.com</code>"
}

View File

@ -0,0 +1,33 @@
import React, { useEffect } from 'react'
import ReactGA, { FieldsObject } from 'react-ga'
import { RouteComponentProps } from 'react-router-dom'
import { analyticsId } from '../config/config'
const withTracker = <P extends RouteComponentProps>(
WrappedComponent: any,
options: FieldsObject = {}
) => {
ReactGA.initialize(analyticsId, {
testMode: process.env.NODE_ENV === 'development',
debug: false
})
const trackPage = (page: string) => {
options.isWeb3 = window.web3 !== undefined
ReactGA.set({ page, ...options })
ReactGA.pageview(page)
}
const HOC = (props: P) => {
useEffect(() => trackPage(props.location.pathname), [
props.location.pathname
])
return <WrappedComponent {...props} />
}
return HOC
}
export default withTracker

View File

@ -1,4 +1,4 @@
import { Ocean } from '@oceanprotocol/squid' import { Ocean, Logger } from '@oceanprotocol/squid'
import Web3 from 'web3' import Web3 from 'web3'
import { import {
@ -9,6 +9,9 @@ import {
brizoPort, brizoPort,
brizoScheme, brizoScheme,
brizoAddress, brizoAddress,
faucetHost,
faucetPort,
faucetScheme,
nodeHost, nodeHost,
nodePort, nodePort,
nodeScheme, nodeScheme,
@ -43,3 +46,32 @@ export async function provideOcean(web3provider: Web3) {
return { ocean } return { ocean }
} }
//
// Faucet
//
export interface FaucetResponse {
success: boolean
message: string
trxHash?: string
}
export async function requestFromFaucet(account: string) {
try {
const url = `${faucetScheme}://${faucetHost}:${faucetPort}/faucet`
const response = await fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
address: account,
agent: 'commons'
})
})
return response.json()
} catch (error) {
Logger.log('requestFromFaucet', error)
}
}

View File

@ -3,7 +3,7 @@ import { Logger } from '@oceanprotocol/squid'
import filesize from 'filesize' import filesize from 'filesize'
import Button from '../../components/atoms/Button' import Button from '../../components/atoms/Button'
import Spinner from '../../components/atoms/Spinner' import Spinner from '../../components/atoms/Spinner'
import { User } from '../../context/User' import { User } from '../../context'
import styles from './AssetFile.module.scss' import styles from './AssetFile.module.scss'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
@ -76,7 +76,7 @@ export default class AssetFile extends PureComponent<
public render() { public render() {
const { ddo, file } = this.props const { ddo, file } = this.props
const { isLoading, message, error } = this.state const { isLoading, message, error } = this.state
const { isLogged } = this.context const { isLogged, isNile } = this.context
return ( return (
<div className={styles.fileWrap}> <div className={styles.fileWrap}>
@ -98,7 +98,7 @@ export default class AssetFile extends PureComponent<
primary primary
className={styles.buttonMain} className={styles.buttonMain}
onClick={() => this.purchaseAsset(ddo, file.index)} onClick={() => this.purchaseAsset(ddo, file.index)}
disabled={!isLogged} disabled={!isLogged || !isNile}
> >
Get file Get file
</Button> </Button>

View File

@ -1,6 +1,6 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import AssetFile from './AssetFile' import AssetFile from './AssetFile'
import { User } from '../../context/User' import { User } from '../../context'
import Web3message from '../../components/organisms/Web3message' import Web3message from '../../components/organisms/Web3message'
import styles from './AssetFilesDetails.module.scss' import styles from './AssetFilesDetails.module.scss'

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import Route from '../../components/templates/Route' import Route from '../../components/templates/Route'
import Spinner from '../../components/atoms/Spinner' import Spinner from '../../components/atoms/Spinner'
import { User } from '../../context/User' import { User } from '../../context'
import AssetDetails from './AssetDetails' import AssetDetails from './AssetDetails'
import stylesApp from '../../App.module.scss' import stylesApp from '../../App.module.scss'
@ -29,18 +29,14 @@ export default class Details extends Component<DetailsProps, DetailsState> {
public render() { public render() {
const { metadata, ddo } = this.state const { metadata, ddo } = this.state
return ( return metadata.base.name !== '' ? (
<Route <Route title={metadata.base.name}>
title={metadata.base ? metadata.base.name : 'Loading Details'} <AssetDetails metadata={metadata} ddo={ddo} />
>
{metadata && metadata.base.name ? (
<AssetDetails metadata={metadata} ddo={ddo} />
) : (
<div className={stylesApp.loader}>
<Spinner message={'Loading asset...'} />
</div>
)}
</Route> </Route>
) : (
<div className={stylesApp.loader}>
<Spinner message={'Loading asset...'} />
</div>
) )
} }
} }

View File

@ -1,8 +1,9 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import { FaucetResponse } from '../ocean'
import Route from '../components/templates/Route' import Route from '../components/templates/Route'
import Button from '../components/atoms/Button' import Button from '../components/atoms/Button'
import Spinner from '../components/atoms/Spinner' import Spinner from '../components/atoms/Spinner'
import { User } from '../context/User' import { User } from '../context'
import Web3message from '../components/organisms/Web3message' import Web3message from '../components/organisms/Web3message'
import styles from './Faucet.module.scss' import styles from './Faucet.module.scss'
@ -21,7 +22,9 @@ export default class Faucet extends PureComponent<{}, FaucetState> {
trxHash: undefined trxHash: undefined
} }
private getTokens = async (requestFromFaucet: () => any) => { private getTokens = async (
requestFromFaucet: () => Promise<FaucetResponse>
) => {
this.setState({ isLoading: true }) this.setState({ isLoading: true })
try { try {
@ -77,7 +80,7 @@ export default class Faucet extends PureComponent<{}, FaucetState> {
<Button <Button
primary primary
onClick={() => this.getTokens(this.context.requestFromFaucet)} onClick={() => this.getTokens(this.context.requestFromFaucet)}
disabled={!this.context.isLogged} disabled={!this.context.isLogged || !this.context.isNile}
> >
Request Ether Request Ether
</Button> </Button>

View File

@ -1,13 +1,21 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import Route from '../components/templates/Route' import Route from '../components/templates/Route'
import AssetsUser from '../components/organisms/AssetsUser' import AssetsUser from '../components/organisms/AssetsUser'
import Web3message from '../components/organisms/Web3message'
import { User } from '../context'
export default class History extends Component { export default class History extends Component {
public render() { public render() {
return ( return (
<Route title="History"> <Route title="History">
{(!this.context.isLogged || !this.context.isNile) && (
<Web3message />
)}
<AssetsUser list /> <AssetsUser list />
</Route> </Route>
) )
} }
} }
History.contextType = User

View File

@ -3,7 +3,7 @@ import Input from '../../components/atoms/Form/Input'
import Label from '../../components/atoms/Form/Label' import Label from '../../components/atoms/Form/Label'
import Row from '../../components/atoms/Form/Row' import Row from '../../components/atoms/Form/Row'
import Button from '../../components/atoms/Button' import Button from '../../components/atoms/Button'
import { User } from '../../context/User' import { User } from '../../context'
import Files from './Files/' import Files from './Files/'
import StepRegisterContent from './StepRegisterContent' import StepRegisterContent from './StepRegisterContent'
import styles from './Step.module.scss' import styles from './Step.module.scss'
@ -154,29 +154,9 @@ export default class Step extends PureComponent<StepProps, {}> {
{this.nextButton()} {this.nextButton()}
{lastStep && ( {lastStep && (
<User.Consumer> <Button disabled={!this.context.isLogged} primary>
{states => Register asset
states.isLogged ? ( </Button>
<Button primary>Register asset</Button>
) : states.isWeb3 ? (
<Button onClick={states.startLogin}>
Register asset (unlock Metamask)
</Button>
) : (
<Button
onClick={(e: Event) => {
e.preventDefault()
window.open(
'https://docs.oceanprotocol.com/tutorials/metamask-setup/',
'_blank'
)
}}
>
Register asset (install Metamask)
</Button>
)
}
</User.Consumer>
)} )}
</div> </div>
</> </>

View File

@ -3,7 +3,8 @@ import { Logger } from '@oceanprotocol/squid'
import Route from '../../components/templates/Route' import Route from '../../components/templates/Route'
import Form from '../../components/atoms/Form/Form' import Form from '../../components/atoms/Form/Form'
import AssetModel from '../../models/AssetModel' import AssetModel from '../../models/AssetModel'
import { User } from '../../context/User' import { User } from '../../context'
import Web3message from '../../components/organisms/Web3message'
import Step from './Step' import Step from './Step'
import Progress from './Progress' import Progress from './Progress'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
@ -318,6 +319,10 @@ class Publish extends Component<{}, PublishState> {
title="Publish" title="Publish"
description="Publish a new data set into the Ocean Protocol Network." description="Publish a new data set into the Ocean Protocol Network."
> >
{(!this.context.isLogged || !this.context.isNile) && (
<Web3message />
)}
<Progress steps={steps} currentStep={this.state.currentStep} /> <Progress steps={steps} currentStep={this.state.currentStep} />
<Form onSubmit={this.registerAsset}> <Form onSubmit={this.registerAsset}>

View File

@ -3,7 +3,7 @@ import queryString from 'query-string'
import { Logger } from '@oceanprotocol/squid' import { Logger } from '@oceanprotocol/squid'
import Spinner from '../components/atoms/Spinner' import Spinner from '../components/atoms/Spinner'
import Route from '../components/templates/Route' import Route from '../components/templates/Route'
import { User } from '../context/User' import { User } from '../context'
import Asset from '../components/molecules/Asset' import Asset from '../components/molecules/Asset'
import Pagination from '../components/molecules/Pagination' import Pagination from '../components/molecules/Pagination'
import styles from './Search.module.scss' import styles from './Search.module.scss'

2
client/src/setupTests.js Normal file
View File

@ -0,0 +1,2 @@
import 'jest-dom/extend-expect'
import 'react-testing-library/cleanup-after-each'

View File

@ -1,46 +0,0 @@
import React, { PureComponent } from 'react'
import ReactGA from 'react-ga'
import { analyticsId } from './config/config'
interface TrackerProps {
location: Location
}
ReactGA.initialize(analyticsId, {
testMode: process.env.NODE_ENV === 'test'
})
export default function withTracker(WrappedComponent: any, options: any = {}) {
const trackPage = (page: string) => {
options.isWeb3 = window.web3 !== undefined
ReactGA.set({
page,
...options
})
ReactGA.pageview(page)
}
return class HOC extends PureComponent<TrackerProps, {}> {
public componentDidMount() {
const page =
this.props.location.pathname + this.props.location.search
trackPage(page)
}
public componentWillReceiveProps(nextProps: any) {
const currentPage = this.props.location.pathname
const nextPage = nextProps.location.pathname
if (currentPage !== nextPage) {
trackPage(
nextProps.location.pathname + nextProps.location.search
)
}
}
public render() {
return <WrappedComponent {...this.props} />
}
}
}