From e615fe8a8eb3c0dac6c0298c3e4a12c3b81f9341 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Mon, 4 Sep 2023 11:59:12 -0300 Subject: [PATCH 01/42] feat: integrate push notifications sdk --- gatsby-node.js | 1 + package-lock.json | 2382 ++++++++++++++++++++++++++++--- package.json | 1 + src/back/routes/notification.ts | 78 + src/clients/Governance.ts | 12 + src/pages/notifications.tsx | 99 ++ src/server.ts | 2 + src/utils/notifications.ts | 3 + 8 files changed, 2369 insertions(+), 209 deletions(-) create mode 100644 src/back/routes/notification.ts create mode 100644 src/pages/notifications.tsx create mode 100644 src/utils/notifications.ts diff --git a/gatsby-node.js b/gatsby-node.js index f9892f901..e19f93c6f 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -19,6 +19,7 @@ exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => { os: false, stream: false, util: false, + zlib: false, }, }, }) diff --git a/package-lock.json b/package-lock.json index bf0a27879..47f327373 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@dcl/ui-env": "1.2.1", "@jparnaudo/react-crypto-icons": "^1.0.5", "@otterspace-xyz/contracts": "^2.7.3", + "@pushprotocol/restapi": "^1.4.16", "@snapshot-labs/snapshot.js": "0.5.5", "@tanstack/react-query": "^4.29.7", "autoprefixer": "^10.4.4", @@ -222,6 +223,16 @@ "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.5.tgz", "integrity": "sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==" }, + "node_modules/@ambire/signature-validator": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ambire/signature-validator/-/signature-validator-1.3.1.tgz", + "integrity": "sha512-kR6Se3nhAGf1VMeun7V2Lml9KRXB5oz64vO2zGSg+dNaGq4BPDEjsNdr0PIKXZ8651sDlRCN7V9SzL5E2ddBYQ==", + "dependencies": { + "ethers": "^5.6.5", + "tap-spec": "^5.0.0", + "tape": "^5.5.3" + } + }, "node_modules/@ampproject/remapping": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", @@ -2869,6 +2880,55 @@ "node": ">=10.0.0" } }, + "node_modules/@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util/node_modules/@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "dependencies": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "dependencies": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + }, "node_modules/@ethersproject/abi": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", @@ -4956,6 +5016,80 @@ "@lit-labs/ssr-dom-shim": "^1.0.0" } }, + "node_modules/@livepeer/core": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@livepeer/core/-/core-1.8.2.tgz", + "integrity": "sha512-p1MdN0aiboChl/Ptp6Ord93NYML23kCRVEFCM3to3bujIUMNVTXbL01bHUnPNx2t4vuVl1LDJchioy/qwHJnYw==", + "dependencies": { + "cross-fetch": "^4.0.0", + "ms": "^3.0.0-canary.1", + "multiformats": "9.9.0", + "tus-js-client": "^3.1.0", + "zustand": "^4.3.9" + }, + "peerDependencies": { + "react": ">=17.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/@livepeer/core/node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/@livepeer/core/node_modules/ms": { + "version": "3.0.0-canary.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-3.0.0-canary.1.tgz", + "integrity": "sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==", + "engines": { + "node": ">=12.13" + } + }, + "node_modules/@livepeer/core/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@ljharb/resumer": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@ljharb/resumer/-/resumer-0.0.1.tgz", + "integrity": "sha512-skQiAOrCfO7vRTq53cxznMpks7wS1va95UCidALlOVWqvBAzwPVErwizDwoMqNVMEn1mDq0utxZd02eIrvF1lw==", + "dependencies": { + "@ljharb/through": "^2.3.9" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@ljharb/through": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.9.tgz", + "integrity": "sha512-yN599ZBuMPPK4tdoToLlvgJB4CLK8fGl7ntfy0Wn7U6ttNvHYurd81bfUiK/6sMkiIwm65R6ck4L6+Y3DfVbNQ==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/@lmdb/lmdb-darwin-arm64": { "version": "2.5.3", "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.3.tgz", @@ -4968,6 +5102,63 @@ "darwin" ] }, + "node_modules/@metamask/eth-sig-util": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-5.1.0.tgz", + "integrity": "sha512-mlgziIHYlA9pi/XZerChqg4NocdOgBPB9NmxgXWQO2U2hH8RGOJQrz6j/AIKkYxgCMIE2PY000+joOwXfzeTDQ==", + "dependencies": { + "@ethereumjs/util": "^8.0.6", + "bn.js": "^4.12.0", + "ethereum-cryptography": "^2.0.0", + "ethjs-util": "^0.1.6", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "dependencies": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "dependencies": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, "node_modules/@metamask/safe-event-emitter": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz", @@ -6379,6 +6570,74 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, + "node_modules/@pushprotocol/restapi": { + "version": "1.4.16", + "resolved": "https://registry.npmjs.org/@pushprotocol/restapi/-/restapi-1.4.16.tgz", + "integrity": "sha512-G1Yh7aJRXUTXBjiV6uNHeEWprgp+zmZqdVIofU3KF/OVcIuZOW3kV4QJt0gd9Tw5Kddm6xMTa8ZrGAHNOyrl0A==", + "dependencies": { + "@ambire/signature-validator": "^1.3.1", + "@metamask/eth-sig-util": "^5.0.2", + "axios": "^0.27.2", + "buffer": "^6.0.3", + "crypto-js": "^4.1.1", + "immer": "^10.0.2", + "joi": "^17.9.2", + "livepeer": "^2.5.8", + "openpgp": "^5.5.0", + "simple-peer": "^9.11.1", + "tslib": "^2.3.0", + "unique-names-generator": "^4.7.1", + "uuid": "^9.0.0", + "video-stream-merger": "^4.0.1" + }, + "peerDependencies": { + "ethers": "^5.6.8" + } + }, + "node_modules/@pushprotocol/restapi/node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/@pushprotocol/restapi/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@pushprotocol/restapi/node_modules/immer": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.0.2.tgz", + "integrity": "sha512-Rx3CqeqQ19sxUtYV9CU911Vhy8/721wRFnJv3REVGWUmoAcIwzifTsdmJte/MV+0/XpM35LZdQMBGkRIoLPwQA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/@pushprotocol/restapi/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@pushprotocol/restapi/node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@redux-saga/core": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.2.3.tgz", @@ -6562,9 +6821,9 @@ } }, "node_modules/@sideway/formula": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", - "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" }, "node_modules/@sideway/pinpoint": { "version": "2.0.0", @@ -6904,6 +7163,11 @@ "@stablelib/wipe": "^1.0.1" } }, + "node_modules/@stitches/core": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@stitches/core/-/core-1.2.8.tgz", + "integrity": "sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg==" + }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", @@ -10695,6 +10959,18 @@ "node": ">=0.10.0" } }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -10735,6 +11011,23 @@ "node": ">=0.10.0" } }, + "node_modules/array.prototype.every": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/array.prototype.every/-/array.prototype.every-1.1.5.tgz", + "integrity": "sha512-FfMQJ+/joFGXpRCltbzV3znaP5QxIhLFySo0fEPn3GuoYlud9LhknMCIxdYKC2qsM/6VHoSp6YGwe3EZXrEcwQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array.prototype.flat": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", @@ -10768,6 +11061,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", + "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/arrify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", @@ -12226,6 +12538,11 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g==" + }, "node_modules/buffer-to-arraybuffer": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", @@ -13587,6 +13904,15 @@ "node": ">=0.1.90" } }, + "node_modules/combine-errors": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/combine-errors/-/combine-errors-3.0.3.tgz", + "integrity": "sha512-C8ikRNRMygCwaTx+Ek3Yr+OuZzgZjduCOfSQBjbM8V3MfgcjSTeto/GXP6PAwKvJz/v15b7GHZvx5rOlczFw/Q==", + "dependencies": { + "custom-error-instance": "2.1.1", + "lodash.uniqby": "4.5.0" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -14158,9 +14484,9 @@ } }, "node_modules/core-js": { - "version": "3.25.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.25.2.tgz", - "integrity": "sha512-YB4IAT1bjEfxTJ1XYy11hJAKskO+qmhuDBM8/guIfMz4JvdsAQAqvyb97zXX7JgSrfPLG5mRGFWJwJD39ruq2A==", + "version": "3.32.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.1.tgz", + "integrity": "sha512-lqufgNn9NLnESg5mQeYsxQP5w7wrViSj0jr/kv6ECQiByzQkrn1MKvV0L3acttpDqfQrHLwr2KCMgX5b8X+lyQ==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -14377,6 +14703,11 @@ "node": "*" } }, + "node_modules/crypto-js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", + "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + }, "node_modules/crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -15547,6 +15878,11 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" }, + "node_modules/custom-error-instance": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/custom-error-instance/-/custom-error-instance-2.1.1.tgz", + "integrity": "sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg==" + }, "node_modules/d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", @@ -16543,25 +16879,28 @@ } }, "node_modules/deep-equal": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz", - "integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.2.tgz", + "integrity": "sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==", "dependencies": { - "call-bind": "^1.0.0", - "es-get-iterator": "^1.1.1", - "get-intrinsic": "^1.0.1", - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.2", - "is-regex": "^1.1.1", + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.1", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", "isarray": "^2.0.5", - "object-is": "^1.1.4", + "object-is": "^1.1.5", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.3", - "which-boxed-primitive": "^1.0.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.0", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", "which-collection": "^1.0.1", - "which-typed-array": "^1.1.2" + "which-typed-array": "^1.1.9" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -16631,9 +16970,9 @@ } }, "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", "dependencies": { "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" @@ -16658,6 +16997,14 @@ "node": ">=0.10.0" } }, + "node_modules/defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delay": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", @@ -17052,6 +17399,17 @@ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" }, + "node_modules/dotignore": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz", + "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==", + "dependencies": { + "minimatch": "^3.0.4" + }, + "bin": { + "ignored": "bin/ignored" + } + }, "node_modules/dprop": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dprop/-/dprop-1.0.0.tgz", @@ -17445,34 +17803,49 @@ } }, "node_modules/es-abstract": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.3.tgz", - "integrity": "sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", + "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.1", + "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.1", "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", "has": "^1.0.3", "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.6", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", + "object-inspect": "^1.12.3", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", + "regexp.prototype.flags": "^1.5.0", + "safe-array-concat": "^1.0.0", "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.10" }, "engines": { "node": ">= 0.4" @@ -17482,18 +17855,19 @@ } }, "node_modules/es-get-iterator": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz", - "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.0", - "has-symbols": "^1.0.1", - "is-arguments": "^1.1.0", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", "is-map": "^2.0.2", "is-set": "^2.0.2", - "is-string": "^1.0.5", - "isarray": "^2.0.5" + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -17509,6 +17883,19 @@ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-shim-unscopables": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", @@ -20050,6 +20437,14 @@ } } }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -20059,11 +20454,6 @@ "node": ">=0.10.0" } }, - "node_modules/foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" - }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -22792,6 +23182,11 @@ "node": ">=6.9.0" } }, + "node_modules/get-browser-rtc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-browser-rtc/-/get-browser-rtc-1.1.0.tgz", + "integrity": "sha512-MghbMJ61EJrRsDe7w1Bvqt3ZsBuqhce5nrn/XAwgwOXhcsz53/ltdxOse1h/8eKXj5slzxdsz56g5rzOFSGwfQ==" + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -22801,12 +23196,13 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3" }, "funding": { @@ -22827,7 +23223,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, "engines": { "node": ">=8.0.0" } @@ -23027,6 +23422,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -23046,6 +23455,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/got": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", @@ -23219,6 +23639,25 @@ "node": ">= 0.4.0" } }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -23232,6 +23671,18 @@ "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" }, + "node_modules/has-dynamic-import": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-dynamic-import/-/has-dynamic-import-2.0.1.tgz", + "integrity": "sha512-X3fbtsZmwb6W7fJGR9o7x65fZoodygCrZ3TVycvghP62yYQfS0t4RS0Qcz+j5tQYUKeSWS09tHkWW6WhFV3XhQ==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -23251,6 +23702,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbol-support-x": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", @@ -23520,6 +23982,11 @@ "value-equal": "^1.0.1" } }, + "node_modules/hls.js": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.4.10.tgz", + "integrity": "sha512-wAVSj4Fm2MqOHy5+BlYnlKxXvJlv5IuZHjlzHu18QmjRzSDFQiUDWdHs5+NsFMQrgKEBwuWDcyvaMC9dUzJ5Uw==" + }, "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -24175,11 +24642,11 @@ "integrity": "sha512-rScRlhDcz6k199EkHqT8NpM87ebN89ICOzILoBHgaG36/WX50N32BnU/kpZgCGPLhARRAWUUX5/cyaIjt7Kipg==" }, "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", "dependencies": { - "get-intrinsic": "^1.1.0", + "get-intrinsic": "^1.2.0", "has": "^1.0.3", "side-channel": "^1.0.4" }, @@ -24645,6 +25112,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -24710,9 +25190,9 @@ } }, "node_modules/is-callable": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.6.tgz", - "integrity": "sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "engines": { "node": ">= 0.4" }, @@ -24745,9 +25225,9 @@ } }, "node_modules/is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", "dependencies": { "has": "^1.0.3" }, @@ -24851,6 +25331,17 @@ "node": ">=0.10.0" } }, + "node_modules/is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-fn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fn/-/is-fn-1.0.0.tgz", @@ -25267,15 +25758,11 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", - "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.11" }, "engines": { "node": ">= 0.4" @@ -27403,14 +27890,14 @@ } }, "node_modules/joi": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.6.0.tgz", - "integrity": "sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw==", + "version": "17.10.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.10.1.tgz", + "integrity": "sha512-vIiDxQKmRidUVp8KngT8MZSOcmRVm2zV7jbMjNYWuHcJWI0bUck3nRTGQjhpPlQenIQIBC5Vp9AhcnHbWQqafw==", "dependencies": { "@hapi/hoek": "^9.0.0", "@hapi/topo": "^5.0.0", "@sideway/address": "^4.1.3", - "@sideway/formula": "^3.0.0", + "@sideway/formula": "^3.0.1", "@sideway/pinpoint": "^2.0.0" } }, @@ -28284,6 +28771,64 @@ "@types/trusted-types": "^2.0.2" } }, + "node_modules/livepeer": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/livepeer/-/livepeer-2.8.2.tgz", + "integrity": "sha512-gV0hyvQQWeIjwZ/p/PJ61w+FeebkKFzTRgdUJqk35E+H5avgp5+kZw+HfnrWtTaU3/06bbyoHK91EjmBHoeCRQ==", + "dependencies": { + "@livepeer/core": "^1.8.2", + "@stitches/core": "^1.2.8", + "core-js": "^3.31.1", + "cross-fetch": "^4.0.0", + "hls.js": "^1.4.9", + "ms": "^3.0.0-canary.1", + "tus-js-client": "^3.1.0", + "zustand": "^4.3.9" + }, + "peerDependencies": { + "react": ">=17.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/livepeer/node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/livepeer/node_modules/ms": { + "version": "3.0.0-canary.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-3.0.0-canary.1.tgz", + "integrity": "sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==", + "engines": { + "node": ">=12.13" + } + }, + "node_modules/livepeer/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/lmdb": { "version": "2.5.3", "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.5.3.tgz", @@ -28358,11 +28903,51 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, + "node_modules/lodash._baseiteratee": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash._baseiteratee/-/lodash._baseiteratee-4.7.0.tgz", + "integrity": "sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ==", + "dependencies": { + "lodash._stringtopath": "~4.8.0" + } + }, + "node_modules/lodash._basetostring": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-4.12.0.tgz", + "integrity": "sha512-SwcRIbyxnN6CFEEK4K1y+zuApvWdpQdBHM/swxP962s8HIxPO3alBH5t3m/dl+f4CMUug6sJb7Pww8d13/9WSw==" + }, + "node_modules/lodash._baseuniq": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz", + "integrity": "sha512-Ja1YevpHZctlI5beLA7oc5KNDhGcPixFhcqSiORHNsp/1QTv7amAXzw+gu4YOvErqVlMVyIJGgtzeepCnnur0A==", + "dependencies": { + "lodash._createset": "~4.0.0", + "lodash._root": "~3.0.0" + } + }, + "node_modules/lodash._createset": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz", + "integrity": "sha512-GTkC6YMprrJZCYU3zcqZj+jkXkrXzq3IPBcF/fIPpNEAB4hZEtXU8zp/RwKOvZl43NUmwDbyRk3+ZTbeRdEBXA==" + }, "node_modules/lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" }, + "node_modules/lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ==" + }, + "node_modules/lodash._stringtopath": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/lodash._stringtopath/-/lodash._stringtopath-4.8.0.tgz", + "integrity": "sha512-SXL66C731p0xPDC5LZg4wI5H+dJo/EO4KTqOMwLYCH3+FmmfAKJEZCm6ohGpI+T1xwsDsJCfL4OnhorllvlTPQ==", + "dependencies": { + "lodash._basetostring": "~4.12.0" + } + }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -28481,6 +29066,11 @@ "lodash._reinterpolate": "^3.0.0" } }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" + }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -28491,6 +29081,15 @@ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" }, + "node_modules/lodash.uniqby": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.5.0.tgz", + "integrity": "sha512-IRt7cfTtHy6f1aRVA5n7kT8rgN3N1nH6MOWLcHfpWG2SH19E3JksLK38MktLxZDhlAjCP9jpIXkOnRXlu6oByQ==", + "dependencies": { + "lodash._baseiteratee": "~4.7.0", + "lodash._baseuniq": "~4.6.0" + } + }, "node_modules/lodash.unset": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/lodash.unset/-/lodash.unset-4.5.2.tgz", @@ -29319,6 +29918,11 @@ "node": ">= 0.6" } }, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==" + }, "node_modules/micromark": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.0.10.tgz", @@ -30030,9 +30634,12 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/minimist-options": { "version": "4.1.0", @@ -31596,9 +32203,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -31814,6 +32421,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openpgp": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/openpgp/-/openpgp-5.10.1.tgz", + "integrity": "sha512-SR5Ft+ej51d0+p53ld5Ney0Yiz0y8Mh1YYLJrvpRMbTaNhvS1QcDX0Oq1rW9sjBnQXtgrpWw2Zve3rm7K5C/pw==", + "dependencies": { + "asn1.js": "^5.0.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/opentracing": { "version": "0.14.7", "resolved": "https://registry.npmjs.org/opentracing/-/opentracing-0.14.7.tgz", @@ -32267,6 +32885,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-ms": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz", + "integrity": "sha512-LpH1Cf5EYuVjkBvCDBYvkUPh+iv2bk3FHflxHkpCYT0/FZ1d3N3uJaLiHr4yGuMcFUhv6eAivitTvWZI4B/chg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/parse-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-5.0.0.tgz", @@ -32912,6 +33538,14 @@ "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" }, + "node_modules/plur": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-1.0.0.tgz", + "integrity": "sha512-qSnKBSZeDY8ApxwhfVIwKwF36KVJqb1/9nzYYq3j3vdwocULCXT8f8fQGkiw1Nk9BGfxiDagEe/pwakA+bOBqw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pngjs": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", @@ -34626,6 +35260,19 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, + "node_modules/pretty-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-2.1.0.tgz", + "integrity": "sha512-H2enpsxzDhuzRl3zeSQpQMirn8dB0Z/gxW96j06tMfTviUWvX14gjKb7qd1gtkUyYhDPuoNe00K5PqNvy2oQNg==", + "dependencies": { + "is-finite": "^1.0.1", + "parse-ms": "^1.0.0", + "plur": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/probe-image-size": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/probe-image-size/-/probe-image-size-7.2.3.tgz", @@ -34921,8 +35568,7 @@ "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" }, "node_modules/queue-microtask": { "version": "1.2.3", @@ -35158,6 +35804,11 @@ "node": ">=0.10.0" } }, + "node_modules/re-emitter": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/re-emitter/-/re-emitter-1.1.3.tgz", + "integrity": "sha512-bHJul9CWcocrS+w5e5QrKYXV9NkbSA9hxSEyhYuctwm6keY9NXR2Xt/4A0vbMP0QvuwyfEyb4bkowYXv1ziEbg==" + }, "node_modules/react": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", @@ -36222,13 +36873,13 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" }, "engines": { "node": ">= 0.4" @@ -37211,6 +37862,28 @@ "node": ">=6" } }, + "node_modules/safe-array-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", + "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -38128,6 +38801,55 @@ "simple-concat": "^1.0.0" } }, + "node_modules/simple-peer": { + "version": "9.11.1", + "resolved": "https://registry.npmjs.org/simple-peer/-/simple-peer-9.11.1.tgz", + "integrity": "sha512-D1SaWpOW8afq1CZGWB8xTfrT3FekjQmPValrqncJMX7QFl8YwhrPTZvMCANLtgBwwdS+7zURyqxDDEmY558tTw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "buffer": "^6.0.3", + "debug": "^4.3.2", + "err-code": "^3.0.1", + "get-browser-rtc": "^1.1.0", + "queue-microtask": "^1.2.3", + "randombytes": "^2.1.0", + "readable-stream": "^3.6.0" + } + }, + "node_modules/simple-peer/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/simple-peer/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -38669,6 +39391,17 @@ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==" }, + "node_modules/split": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.0.tgz", + "integrity": "sha512-3SVfJe2A0WZg3D+ZEtXqYkvpSGAVaZ1MgufNCeHioBESCqQFsuT1VcQufiopBfJZqh92ZwQ6ddL378iUSbqVNQ==", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, "node_modules/split-on-first": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", @@ -38922,6 +39655,17 @@ "node": ">= 0.6" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/stream-browserify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", @@ -39054,27 +39798,43 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "es-abstract": "^1.20.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "es-abstract": "^1.20.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -39627,6 +40387,133 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/tap-out": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tap-out/-/tap-out-2.1.0.tgz", + "integrity": "sha512-LJE+TBoVbOWhwdz4+FQk40nmbIuxJLqaGvj3WauQw3NYYU5TdjoV3C0x/yq37YAvVyi+oeBXmWnxWSjJ7IEyUw==", + "dependencies": { + "re-emitter": "1.1.3", + "readable-stream": "2.2.9", + "split": "1.0.0", + "trim": "0.0.1" + }, + "bin": { + "tap-out": "bin/cmd.js" + } + }, + "node_modules/tap-out/node_modules/process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw==" + }, + "node_modules/tap-out/node_modules/readable-stream": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", + "integrity": "sha512-iuxqX7b7FYt08AriYECxUsK9KTXE3A/FenxIa3IPmvANHxaTP/wGIwwf+IidvvIDk/MsCp/oEV6A8CXo4SDcCg==", + "dependencies": { + "buffer-shims": "~1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~1.0.0", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/tap-out/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/tap-out/node_modules/string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/tap-spec": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tap-spec/-/tap-spec-5.0.0.tgz", + "integrity": "sha512-zMDVJiE5I6Y4XGjlueGXJIX2YIkbDN44broZlnypT38Hj/czfOXrszHNNJBF/DXR8n+x6gbfSx68x04kIEHdrw==", + "dependencies": { + "chalk": "^1.0.0", + "duplexer": "^0.1.1", + "figures": "^1.4.0", + "lodash": "^4.17.10", + "pretty-ms": "^2.1.0", + "repeat-string": "^1.5.2", + "tap-out": "^2.1.0", + "through2": "^2.0.0" + }, + "bin": { + "tap-spec": "bin/cmd.js", + "tspec": "bin/cmd.js" + } + }, + "node_modules/tap-spec/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap-spec/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap-spec/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap-spec/node_modules/figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ==", + "dependencies": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap-spec/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tap-spec/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -39635,6 +40522,56 @@ "node": ">=6" } }, + "node_modules/tape": { + "version": "5.6.6", + "resolved": "https://registry.npmjs.org/tape/-/tape-5.6.6.tgz", + "integrity": "sha512-rGp2cZ3rfZ6QfTBm6yvohf8aXmDqPyzMKZwTMV12w4i+b/N2Adwlg8PlW8jLqWzlJUZhglyYaLOSrMt/ZlZkAA==", + "dependencies": { + "@ljharb/resumer": "^0.0.1", + "@ljharb/through": "^2.3.9", + "array.prototype.every": "^1.1.4", + "call-bind": "^1.0.2", + "deep-equal": "^2.2.2", + "defined": "^1.0.1", + "dotignore": "^0.1.2", + "for-each": "^0.3.3", + "get-package-type": "^0.1.0", + "glob": "^7.2.3", + "has": "^1.0.3", + "has-dynamic-import": "^2.0.1", + "inherits": "^2.0.4", + "is-regex": "^1.1.4", + "minimist": "^1.2.8", + "object-inspect": "^1.12.3", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "resolve": "^2.0.0-next.4", + "string.prototype.trim": "^1.2.7" + }, + "bin": { + "tape": "bin/tape" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tape/node_modules/resolve": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tar": { "version": "4.4.19", "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", @@ -39897,6 +40834,42 @@ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/through2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", @@ -40160,6 +41133,12 @@ "tree-kill": "cli.js" } }, + "node_modules/trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==", + "deprecated": "Use String.prototype.trim() instead" + }, "node_modules/trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -40351,6 +41330,36 @@ "node": "*" } }, + "node_modules/tus-js-client": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tus-js-client/-/tus-js-client-3.1.1.tgz", + "integrity": "sha512-SZzWP62jEFLmROSRZx+uoGLKqsYWMGK/m+PiNehPVWbCm7/S9zRIMaDxiaOcKdMnFno4luaqP5E+Y1iXXPjP0A==", + "dependencies": { + "buffer-from": "^1.1.2", + "combine-errors": "^3.0.3", + "is-stream": "^2.0.0", + "js-base64": "^3.7.2", + "lodash.throttle": "^4.1.1", + "proper-lockfile": "^4.1.2", + "url-parse": "^1.5.7" + } + }, + "node_modules/tus-js-client/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tus-js-client/node_modules/js-base64": { + "version": "3.7.5", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.5.tgz", + "integrity": "sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==" + }, "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", @@ -40416,6 +41425,67 @@ "resolved": "https://registry.npmjs.org/type-of/-/type-of-2.0.1.tgz", "integrity": "sha1-5yoXQYllaOn2KDeNgW1pEvfyOXI=" }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typed-url-params": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/typed-url-params/-/typed-url-params-1.0.1.tgz", @@ -40766,6 +41836,14 @@ "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" }, + "node_modules/unique-names-generator": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/unique-names-generator/-/unique-names-generator-4.7.1.tgz", + "integrity": "sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow==", + "engines": { + "node": ">=8" + } + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -41158,7 +42236,6 @@ "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -41507,6 +42584,11 @@ "d3-timer": "^3.0.1" } }, + "node_modules/video-stream-merger": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/video-stream-merger/-/video-stream-merger-4.0.1.tgz", + "integrity": "sha512-VazYSr8tk6S/zkOq5jpR/ryy1HnGxm5XCw+d2Ejpqy1m6d71oZpyFG82dUkgAo7dg/lk3k4TqvJPtuRUtR8URA==" + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -43967,16 +45049,15 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, "node_modules/which-typed-array": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", - "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.7" + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -44868,6 +45949,33 @@ "node": ">=8" } }, + "node_modules/zustand": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.1.tgz", + "integrity": "sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/zwitch": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.2.tgz", @@ -44980,6 +46088,16 @@ } } }, + "@ambire/signature-validator": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ambire/signature-validator/-/signature-validator-1.3.1.tgz", + "integrity": "sha512-kR6Se3nhAGf1VMeun7V2Lml9KRXB5oz64vO2zGSg+dNaGq4BPDEjsNdr0PIKXZ8651sDlRCN7V9SzL5E2ddBYQ==", + "requires": { + "ethers": "^5.6.5", + "tap-spec": "^5.0.0", + "tape": "^5.5.3" + } + }, "@ampproject/remapping": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", @@ -46864,6 +47982,48 @@ } } }, + "@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "requires": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "dependencies": { + "@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "requires": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + } + }, + "@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "requires": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + } + }, + "ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "requires": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + } + } + }, "@ethersproject/abi": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", @@ -48445,12 +49605,110 @@ "@lit-labs/ssr-dom-shim": "^1.0.0" } }, + "@livepeer/core": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@livepeer/core/-/core-1.8.2.tgz", + "integrity": "sha512-p1MdN0aiboChl/Ptp6Ord93NYML23kCRVEFCM3to3bujIUMNVTXbL01bHUnPNx2t4vuVl1LDJchioy/qwHJnYw==", + "requires": { + "cross-fetch": "^4.0.0", + "ms": "^3.0.0-canary.1", + "multiformats": "9.9.0", + "tus-js-client": "^3.1.0", + "zustand": "^4.3.9" + }, + "dependencies": { + "cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "requires": { + "node-fetch": "^2.6.12" + } + }, + "ms": { + "version": "3.0.0-canary.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-3.0.0-canary.1.tgz", + "integrity": "sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==" + }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + } + } + } + }, + "@ljharb/resumer": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@ljharb/resumer/-/resumer-0.0.1.tgz", + "integrity": "sha512-skQiAOrCfO7vRTq53cxznMpks7wS1va95UCidALlOVWqvBAzwPVErwizDwoMqNVMEn1mDq0utxZd02eIrvF1lw==", + "requires": { + "@ljharb/through": "^2.3.9" + } + }, + "@ljharb/through": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.9.tgz", + "integrity": "sha512-yN599ZBuMPPK4tdoToLlvgJB4CLK8fGl7ntfy0Wn7U6ttNvHYurd81bfUiK/6sMkiIwm65R6ck4L6+Y3DfVbNQ==" + }, "@lmdb/lmdb-darwin-arm64": { "version": "2.5.3", "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.3.tgz", "integrity": "sha512-RXwGZ/0eCqtCY8FLTM/koR60w+MXyvBUpToXiIyjOcBnC81tAlTUHrRUavCEWPI9zc9VgvpK3+cbumPyR8BSuA==", "optional": true }, + "@metamask/eth-sig-util": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-5.1.0.tgz", + "integrity": "sha512-mlgziIHYlA9pi/XZerChqg4NocdOgBPB9NmxgXWQO2U2hH8RGOJQrz6j/AIKkYxgCMIE2PY000+joOwXfzeTDQ==", + "requires": { + "@ethereumjs/util": "^8.0.6", + "bn.js": "^4.12.0", + "ethereum-cryptography": "^2.0.0", + "ethjs-util": "^0.1.6", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + }, + "dependencies": { + "@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "requires": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + } + }, + "@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "requires": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + } + }, + "ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "requires": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + }, + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + } + } + }, "@metamask/safe-event-emitter": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz", @@ -49428,6 +50686,63 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, + "@pushprotocol/restapi": { + "version": "1.4.16", + "resolved": "https://registry.npmjs.org/@pushprotocol/restapi/-/restapi-1.4.16.tgz", + "integrity": "sha512-G1Yh7aJRXUTXBjiV6uNHeEWprgp+zmZqdVIofU3KF/OVcIuZOW3kV4QJt0gd9Tw5Kddm6xMTa8ZrGAHNOyrl0A==", + "requires": { + "@ambire/signature-validator": "^1.3.1", + "@metamask/eth-sig-util": "^5.0.2", + "axios": "^0.27.2", + "buffer": "^6.0.3", + "crypto-js": "^4.1.1", + "immer": "^10.0.2", + "joi": "^17.9.2", + "livepeer": "^2.5.8", + "openpgp": "^5.5.0", + "simple-peer": "^9.11.1", + "tslib": "^2.3.0", + "unique-names-generator": "^4.7.1", + "uuid": "^9.0.0", + "video-stream-merger": "^4.0.1" + }, + "dependencies": { + "axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "requires": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "immer": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.0.2.tgz", + "integrity": "sha512-Rx3CqeqQ19sxUtYV9CU911Vhy8/721wRFnJv3REVGWUmoAcIwzifTsdmJte/MV+0/XpM35LZdQMBGkRIoLPwQA==" + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" + } + } + }, "@redux-saga/core": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.2.3.tgz", @@ -49565,9 +50880,9 @@ } }, "@sideway/formula": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", - "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" }, "@sideway/pinpoint": { "version": "2.0.0", @@ -49867,6 +51182,11 @@ "@stablelib/wipe": "^1.0.1" } }, + "@stitches/core": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@stitches/core/-/core-1.2.8.tgz", + "integrity": "sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg==" + }, "@svgr/babel-plugin-add-jsx-attribute": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", @@ -52995,6 +54315,15 @@ "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", "dev": true }, + "array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "requires": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + } + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -53023,6 +54352,17 @@ "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", "dev": true }, + "array.prototype.every": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/array.prototype.every/-/array.prototype.every-1.1.5.tgz", + "integrity": "sha512-FfMQJ+/joFGXpRCltbzV3znaP5QxIhLFySo0fEPn3GuoYlud9LhknMCIxdYKC2qsM/6VHoSp6YGwe3EZXrEcwQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "is-string": "^1.0.7" + } + }, "array.prototype.flat": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", @@ -53044,6 +54384,19 @@ "es-shim-unscopables": "^1.0.0" } }, + "arraybuffer.prototype.slice": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", + "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "requires": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + } + }, "arrify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", @@ -54213,6 +55566,11 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g==" + }, "buffer-to-arraybuffer": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", @@ -55224,6 +56582,15 @@ "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" }, + "combine-errors": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/combine-errors/-/combine-errors-3.0.3.tgz", + "integrity": "sha512-C8ikRNRMygCwaTx+Ek3Yr+OuZzgZjduCOfSQBjbM8V3MfgcjSTeto/GXP6PAwKvJz/v15b7GHZvx5rOlczFw/Q==", + "requires": { + "custom-error-instance": "2.1.1", + "lodash.uniqby": "4.5.0" + } + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -55686,9 +57053,9 @@ } }, "core-js": { - "version": "3.25.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.25.2.tgz", - "integrity": "sha512-YB4IAT1bjEfxTJ1XYy11hJAKskO+qmhuDBM8/guIfMz4JvdsAQAqvyb97zXX7JgSrfPLG5mRGFWJwJD39ruq2A==" + "version": "3.32.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.1.tgz", + "integrity": "sha512-lqufgNn9NLnESg5mQeYsxQP5w7wrViSj0jr/kv6ECQiByzQkrn1MKvV0L3acttpDqfQrHLwr2KCMgX5b8X+lyQ==" }, "core-js-compat": { "version": "3.21.1", @@ -55856,6 +57223,11 @@ "randomfill": "^1.0.3" } }, + "crypto-js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", + "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + }, "crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -56647,6 +58019,11 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" }, + "custom-error-instance": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/custom-error-instance/-/custom-error-instance-2.1.1.tgz", + "integrity": "sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg==" + }, "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", @@ -57468,25 +58845,28 @@ } }, "deep-equal": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz", - "integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.2.tgz", + "integrity": "sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==", "requires": { - "call-bind": "^1.0.0", - "es-get-iterator": "^1.1.1", - "get-intrinsic": "^1.0.1", - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.2", - "is-regex": "^1.1.1", + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.1", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", "isarray": "^2.0.5", - "object-is": "^1.1.4", + "object-is": "^1.1.5", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.3", - "which-boxed-primitive": "^1.0.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.0", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", "which-collection": "^1.0.1", - "which-typed-array": "^1.1.2" + "which-typed-array": "^1.1.9" }, "dependencies": { "isarray": { @@ -57545,9 +58925,9 @@ "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" }, "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", "requires": { "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" @@ -57563,6 +58943,11 @@ "isobject": "^3.0.1" } }, + "defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==" + }, "delay": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", @@ -57871,6 +59256,14 @@ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" }, + "dotignore": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz", + "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==", + "requires": { + "minimatch": "^3.0.4" + } + }, "dprop": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dprop/-/dprop-1.0.0.tgz", @@ -58197,49 +59590,65 @@ } }, "es-abstract": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.3.tgz", - "integrity": "sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", + "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", "requires": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.1", + "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.1", "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", "has": "^1.0.3", "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.6", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", + "object-inspect": "^1.12.3", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", + "regexp.prototype.flags": "^1.5.0", + "safe-array-concat": "^1.0.0", "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.10" } }, "es-get-iterator": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz", - "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "requires": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.0", - "has-symbols": "^1.0.1", - "is-arguments": "^1.1.0", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", "is-map": "^2.0.2", "is-set": "^2.0.2", - "is-string": "^1.0.5", - "isarray": "^2.0.5" + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" }, "dependencies": { "isarray": { @@ -58254,6 +59663,16 @@ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" }, + "es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "requires": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + } + }, "es-shim-unscopables": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", @@ -60314,17 +61733,20 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", "dev": true }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" - }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -62320,18 +63742,24 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" }, + "get-browser-rtc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-browser-rtc/-/get-browser-rtc-1.1.0.tgz", + "integrity": "sha512-MghbMJ61EJrRsDe7w1Bvqt3ZsBuqhce5nrn/XAwgwOXhcsz53/ltdxOse1h/8eKXj5slzxdsz56g5rzOFSGwfQ==" + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3" } }, @@ -62348,8 +63776,7 @@ "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==" }, "get-port": { "version": "3.2.0", @@ -62499,6 +63926,14 @@ } } }, + "globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "requires": { + "define-properties": "^1.1.3" + } + }, "globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -62512,6 +63947,14 @@ "slash": "^3.0.0" } }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, "got": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", @@ -62644,6 +64087,21 @@ "function-bind": "^1.1.1" } }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" + } + } + }, "has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -62654,6 +64112,15 @@ "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" }, + "has-dynamic-import": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-dynamic-import/-/has-dynamic-import-2.0.1.tgz", + "integrity": "sha512-X3fbtsZmwb6W7fJGR9o7x65fZoodygCrZ3TVycvghP62yYQfS0t4RS0Qcz+j5tQYUKeSWS09tHkWW6WhFV3XhQ==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -62667,6 +64134,11 @@ "get-intrinsic": "^1.1.1" } }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + }, "has-symbol-support-x": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", @@ -62878,6 +64350,11 @@ "value-equal": "^1.0.1" } }, + "hls.js": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.4.10.tgz", + "integrity": "sha512-wAVSj4Fm2MqOHy5+BlYnlKxXvJlv5IuZHjlzHu18QmjRzSDFQiUDWdHs5+NsFMQrgKEBwuWDcyvaMC9dUzJ5Uw==" + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -63384,11 +64861,11 @@ "integrity": "sha512-rScRlhDcz6k199EkHqT8NpM87ebN89ICOzILoBHgaG36/WX50N32BnU/kpZgCGPLhARRAWUUX5/cyaIjt7Kipg==" }, "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", "requires": { - "get-intrinsic": "^1.1.0", + "get-intrinsic": "^1.2.0", "has": "^1.0.3", "side-channel": "^1.0.4" } @@ -63771,6 +65248,16 @@ "has-tostringtag": "^1.0.0" } }, + "is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -63807,9 +65294,9 @@ "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" }, "is-callable": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.6.tgz", - "integrity": "sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q==" + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" }, "is-ci": { "version": "2.0.0", @@ -63833,9 +65320,9 @@ } }, "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", "requires": { "has": "^1.0.3" } @@ -63902,6 +65389,11 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==" + }, "is-fn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fn/-/is-fn-1.0.0.tgz", @@ -64193,15 +65685,11 @@ } }, "is-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", - "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.11" } }, "is-typedarray": { @@ -65824,14 +67312,14 @@ "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" }, "joi": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.6.0.tgz", - "integrity": "sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw==", + "version": "17.10.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.10.1.tgz", + "integrity": "sha512-vIiDxQKmRidUVp8KngT8MZSOcmRVm2zV7jbMjNYWuHcJWI0bUck3nRTGQjhpPlQenIQIBC5Vp9AhcnHbWQqafw==", "requires": { "@hapi/hoek": "^9.0.0", "@hapi/topo": "^5.0.0", "@sideway/address": "^4.1.3", - "@sideway/formula": "^3.0.0", + "@sideway/formula": "^3.0.1", "@sideway/pinpoint": "^2.0.0" } }, @@ -66541,6 +68029,44 @@ "@types/trusted-types": "^2.0.2" } }, + "livepeer": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/livepeer/-/livepeer-2.8.2.tgz", + "integrity": "sha512-gV0hyvQQWeIjwZ/p/PJ61w+FeebkKFzTRgdUJqk35E+H5avgp5+kZw+HfnrWtTaU3/06bbyoHK91EjmBHoeCRQ==", + "requires": { + "@livepeer/core": "^1.8.2", + "@stitches/core": "^1.2.8", + "core-js": "^3.31.1", + "cross-fetch": "^4.0.0", + "hls.js": "^1.4.9", + "ms": "^3.0.0-canary.1", + "tus-js-client": "^3.1.0", + "zustand": "^4.3.9" + }, + "dependencies": { + "cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "requires": { + "node-fetch": "^2.6.12" + } + }, + "ms": { + "version": "3.0.0-canary.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-3.0.0-canary.1.tgz", + "integrity": "sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==" + }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + } + } + } + }, "lmdb": { "version": "2.5.3", "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.5.3.tgz", @@ -66605,11 +68131,51 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, + "lodash._baseiteratee": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash._baseiteratee/-/lodash._baseiteratee-4.7.0.tgz", + "integrity": "sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ==", + "requires": { + "lodash._stringtopath": "~4.8.0" + } + }, + "lodash._basetostring": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-4.12.0.tgz", + "integrity": "sha512-SwcRIbyxnN6CFEEK4K1y+zuApvWdpQdBHM/swxP962s8HIxPO3alBH5t3m/dl+f4CMUug6sJb7Pww8d13/9WSw==" + }, + "lodash._baseuniq": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz", + "integrity": "sha512-Ja1YevpHZctlI5beLA7oc5KNDhGcPixFhcqSiORHNsp/1QTv7amAXzw+gu4YOvErqVlMVyIJGgtzeepCnnur0A==", + "requires": { + "lodash._createset": "~4.0.0", + "lodash._root": "~3.0.0" + } + }, + "lodash._createset": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz", + "integrity": "sha512-GTkC6YMprrJZCYU3zcqZj+jkXkrXzq3IPBcF/fIPpNEAB4hZEtXU8zp/RwKOvZl43NUmwDbyRk3+ZTbeRdEBXA==" + }, "lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ==" + }, + "lodash._stringtopath": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/lodash._stringtopath/-/lodash._stringtopath-4.8.0.tgz", + "integrity": "sha512-SXL66C731p0xPDC5LZg4wI5H+dJo/EO4KTqOMwLYCH3+FmmfAKJEZCm6ohGpI+T1xwsDsJCfL4OnhorllvlTPQ==", + "requires": { + "lodash._basetostring": "~4.12.0" + } + }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -66728,6 +68294,11 @@ "lodash._reinterpolate": "^3.0.0" } }, + "lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" + }, "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -66738,6 +68309,15 @@ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" }, + "lodash.uniqby": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.5.0.tgz", + "integrity": "sha512-IRt7cfTtHy6f1aRVA5n7kT8rgN3N1nH6MOWLcHfpWG2SH19E3JksLK38MktLxZDhlAjCP9jpIXkOnRXlu6oByQ==", + "requires": { + "lodash._baseiteratee": "~4.7.0", + "lodash._baseuniq": "~4.6.0" + } + }, "lodash.unset": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/lodash.unset/-/lodash.unset-4.5.2.tgz", @@ -67407,6 +68987,11 @@ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, + "micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==" + }, "micromark": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.0.10.tgz", @@ -67829,9 +69414,9 @@ } }, "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" }, "minimist-options": { "version": "4.1.0", @@ -69052,9 +70637,9 @@ } }, "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" }, "object-is": { "version": "1.1.5", @@ -69204,6 +70789,14 @@ "is-wsl": "^2.1.1" } }, + "openpgp": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/openpgp/-/openpgp-5.10.1.tgz", + "integrity": "sha512-SR5Ft+ej51d0+p53ld5Ney0Yiz0y8Mh1YYLJrvpRMbTaNhvS1QcDX0Oq1rW9sjBnQXtgrpWw2Zve3rm7K5C/pw==", + "requires": { + "asn1.js": "^5.0.0" + } + }, "opentracing": { "version": "0.14.7", "resolved": "https://registry.npmjs.org/opentracing/-/opentracing-0.14.7.tgz", @@ -69547,6 +71140,11 @@ "lines-and-columns": "^1.1.6" } }, + "parse-ms": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz", + "integrity": "sha512-LpH1Cf5EYuVjkBvCDBYvkUPh+iv2bk3FHflxHkpCYT0/FZ1d3N3uJaLiHr4yGuMcFUhv6eAivitTvWZI4B/chg==" + }, "parse-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-5.0.0.tgz", @@ -70048,6 +71646,11 @@ "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" }, + "plur": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-1.0.0.tgz", + "integrity": "sha512-qSnKBSZeDY8ApxwhfVIwKwF36KVJqb1/9nzYYq3j3vdwocULCXT8f8fQGkiw1Nk9BGfxiDagEe/pwakA+bOBqw==" + }, "pngjs": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", @@ -71306,6 +72909,16 @@ } } }, + "pretty-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-2.1.0.tgz", + "integrity": "sha512-H2enpsxzDhuzRl3zeSQpQMirn8dB0Z/gxW96j06tMfTviUWvX14gjKb7qd1gtkUyYhDPuoNe00K5PqNvy2oQNg==", + "requires": { + "is-finite": "^1.0.1", + "parse-ms": "^1.0.0", + "plur": "^1.0.0" + } + }, "probe-image-size": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/probe-image-size/-/probe-image-size-7.2.3.tgz", @@ -71547,8 +73160,7 @@ "querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" }, "queue-microtask": { "version": "1.2.3", @@ -71720,6 +73332,11 @@ } } }, + "re-emitter": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/re-emitter/-/re-emitter-1.1.3.tgz", + "integrity": "sha512-bHJul9CWcocrS+w5e5QrKYXV9NkbSA9hxSEyhYuctwm6keY9NXR2Xt/4A0vbMP0QvuwyfEyb4bkowYXv1ziEbg==" + }, "react": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", @@ -72540,13 +74157,13 @@ } }, "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" } }, "regexpp": { @@ -73262,6 +74879,24 @@ "mri": "^1.1.0" } }, + "safe-array-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", + "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + } + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -73977,6 +75612,35 @@ "simple-concat": "^1.0.0" } }, + "simple-peer": { + "version": "9.11.1", + "resolved": "https://registry.npmjs.org/simple-peer/-/simple-peer-9.11.1.tgz", + "integrity": "sha512-D1SaWpOW8afq1CZGWB8xTfrT3FekjQmPValrqncJMX7QFl8YwhrPTZvMCANLtgBwwdS+7zURyqxDDEmY558tTw==", + "requires": { + "buffer": "^6.0.3", + "debug": "^4.3.2", + "err-code": "^3.0.1", + "get-browser-rtc": "^1.1.0", + "queue-microtask": "^1.2.3", + "randombytes": "^2.1.0", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -74425,6 +76089,14 @@ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==" }, + "split": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.0.tgz", + "integrity": "sha512-3SVfJe2A0WZg3D+ZEtXqYkvpSGAVaZ1MgufNCeHioBESCqQFsuT1VcQufiopBfJZqh92ZwQ6ddL378iUSbqVNQ==", + "requires": { + "through": "2" + } + }, "split-on-first": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", @@ -74626,6 +76298,14 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "requires": { + "internal-slot": "^1.0.4" + } + }, "stream-browserify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", @@ -74737,24 +76417,34 @@ "side-channel": "^1.0.4" } }, + "string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, "string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "es-abstract": "^1.20.4" } }, "string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "es-abstract": "^1.20.4" } }, "stringify-object": { @@ -75173,11 +76863,157 @@ } } }, + "tap-out": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tap-out/-/tap-out-2.1.0.tgz", + "integrity": "sha512-LJE+TBoVbOWhwdz4+FQk40nmbIuxJLqaGvj3WauQw3NYYU5TdjoV3C0x/yq37YAvVyi+oeBXmWnxWSjJ7IEyUw==", + "requires": { + "re-emitter": "1.1.3", + "readable-stream": "2.2.9", + "split": "1.0.0", + "trim": "0.0.1" + }, + "dependencies": { + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw==" + }, + "readable-stream": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", + "integrity": "sha512-iuxqX7b7FYt08AriYECxUsK9KTXE3A/FenxIa3IPmvANHxaTP/wGIwwf+IidvvIDk/MsCp/oEV6A8CXo4SDcCg==", + "requires": { + "buffer-shims": "~1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~1.0.0", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "tap-spec": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tap-spec/-/tap-spec-5.0.0.tgz", + "integrity": "sha512-zMDVJiE5I6Y4XGjlueGXJIX2YIkbDN44broZlnypT38Hj/czfOXrszHNNJBF/DXR8n+x6gbfSx68x04kIEHdrw==", + "requires": { + "chalk": "^1.0.0", + "duplexer": "^0.1.1", + "figures": "^1.4.0", + "lodash": "^4.17.10", + "pretty-ms": "^2.1.0", + "repeat-string": "^1.5.2", + "tap-out": "^2.1.0", + "through2": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ==", + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==" + } + } + }, "tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" }, + "tape": { + "version": "5.6.6", + "resolved": "https://registry.npmjs.org/tape/-/tape-5.6.6.tgz", + "integrity": "sha512-rGp2cZ3rfZ6QfTBm6yvohf8aXmDqPyzMKZwTMV12w4i+b/N2Adwlg8PlW8jLqWzlJUZhglyYaLOSrMt/ZlZkAA==", + "requires": { + "@ljharb/resumer": "^0.0.1", + "@ljharb/through": "^2.3.9", + "array.prototype.every": "^1.1.4", + "call-bind": "^1.0.2", + "deep-equal": "^2.2.2", + "defined": "^1.0.1", + "dotignore": "^0.1.2", + "for-each": "^0.3.3", + "get-package-type": "^0.1.0", + "glob": "^7.2.3", + "has": "^1.0.3", + "has-dynamic-import": "^2.0.1", + "inherits": "^2.0.4", + "is-regex": "^1.1.4", + "minimist": "^1.2.8", + "object-inspect": "^1.12.3", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "resolve": "^2.0.0-next.4", + "string.prototype.trim": "^1.2.7" + }, + "dependencies": { + "resolve": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + } + } + }, "tar": { "version": "4.4.19", "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", @@ -75371,6 +77207,44 @@ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", @@ -75583,6 +77457,11 @@ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true }, + "trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==" + }, "trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -75714,6 +77593,32 @@ "safe-buffer": "^5.0.1" } }, + "tus-js-client": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tus-js-client/-/tus-js-client-3.1.1.tgz", + "integrity": "sha512-SZzWP62jEFLmROSRZx+uoGLKqsYWMGK/m+PiNehPVWbCm7/S9zRIMaDxiaOcKdMnFno4luaqP5E+Y1iXXPjP0A==", + "requires": { + "buffer-from": "^1.1.2", + "combine-errors": "^3.0.3", + "is-stream": "^2.0.0", + "js-base64": "^3.7.2", + "lodash.throttle": "^4.1.1", + "proper-lockfile": "^4.1.2", + "url-parse": "^1.5.7" + }, + "dependencies": { + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "js-base64": { + "version": "3.7.5", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.5.tgz", + "integrity": "sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==" + } + } + }, "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", @@ -75764,6 +77669,49 @@ "resolved": "https://registry.npmjs.org/type-of/-/type-of-2.0.1.tgz", "integrity": "sha1-5yoXQYllaOn2KDeNgW1pEvfyOXI=" }, + "typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + } + }, + "typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "requires": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + } + }, + "typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + } + }, + "typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "requires": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + } + }, "typed-url-params": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/typed-url-params/-/typed-url-params-1.0.1.tgz", @@ -76034,6 +77982,11 @@ "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" }, + "unique-names-generator": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/unique-names-generator/-/unique-names-generator-4.7.1.tgz", + "integrity": "sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow==" + }, "unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -76336,7 +78289,6 @@ "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, "requires": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -76601,6 +78553,11 @@ "d3-timer": "^3.0.1" } }, + "video-stream-merger": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/video-stream-merger/-/video-stream-merger-4.0.1.tgz", + "integrity": "sha512-VazYSr8tk6S/zkOq5jpR/ryy1HnGxm5XCw+d2Ejpqy1m6d71oZpyFG82dUkgAo7dg/lk3k4TqvJPtuRUtR8URA==" + }, "w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -78522,16 +80479,15 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, "which-typed-array": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", - "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", "requires": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", - "es-abstract": "^1.18.5", - "foreach": "^2.0.5", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.7" + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" } }, "widest-line": { @@ -79277,6 +81233,14 @@ } } }, + "zustand": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.1.tgz", + "integrity": "sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==", + "requires": { + "use-sync-external-store": "1.2.0" + } + }, "zwitch": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.2.tgz", diff --git a/package.json b/package.json index 6bfdba469..b7bd2ad41 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "@dcl/ui-env": "1.2.1", "@jparnaudo/react-crypto-icons": "^1.0.5", "@otterspace-xyz/contracts": "^2.7.3", + "@pushprotocol/restapi": "^1.4.16", "@snapshot-labs/snapshot.js": "0.5.5", "@tanstack/react-query": "^4.29.7", "autoprefixer": "^10.4.4", diff --git a/src/back/routes/notification.ts b/src/back/routes/notification.ts new file mode 100644 index 000000000..e3576bdff --- /dev/null +++ b/src/back/routes/notification.ts @@ -0,0 +1,78 @@ +import * as PushAPI from '@pushprotocol/restapi' +import RequestError from 'decentraland-gatsby/dist/entities/Route/error' +import handleAPI from 'decentraland-gatsby/dist/entities/Route/handle' +import routes from 'decentraland-gatsby/dist/entities/Route/routes' +import { ethers } from 'ethers' +import { Request } from 'express' + +import { getCaipAddress } from '../../utils/notifications' + +enum ENV { + PROD = 'prod', + STAGING = 'staging', +} + +const NotificationType = { + TARGET: 3, + SUBSET: 4, + BROADCAST: 1, +} + +const NotificationIdentityType = { + DIRECT_PAYLOAD: 2, +} + +const CHAIN_ID = 5 +const CHANNEL_ADDRESS = '0xBf363AeDd082Ddd8DB2D6457609B03f9ee74a2F1' + +export default routes((router) => { + router.post('/notifications/send', handleAPI(handleSendNotification)) + router.get('/notifications/user/:address', handleAPI(handleGetNotifications)) +}) + +const PUSH_CHANNEL_OWNER_PK = process.env.PUSH_CHANNEL_OWNER_PK +const pkAddress = `0x${PUSH_CHANNEL_OWNER_PK}` +const _signer = new ethers.Wallet(pkAddress) + +async function handleSendNotification(req: Request) { + const { title, body, recipient } = req.body + + if (!recipient || !title || !body) { + throw new RequestError('Invalid data', RequestError.BadRequest) + } + + const response = await PushAPI.payloads.sendNotification({ + signer: _signer, + type: NotificationType.TARGET, + identityType: NotificationIdentityType.DIRECT_PAYLOAD, + notification: { + title, + body, + }, + payload: { + title, + body, + cta: '', + img: '', + }, + recipients: getCaipAddress(recipient, CHAIN_ID), + channel: getCaipAddress(CHANNEL_ADDRESS, CHAIN_ID), + env: ENV.STAGING, + }) + + return response.data +} + +async function handleGetNotifications(req: Request) { + const address = req.params.address + if (!address) { + throw new RequestError('Missing user', RequestError.BadRequest) + } + + const notifications = await PushAPI.user.getFeeds({ + user: getCaipAddress(address, CHAIN_ID), + env: ENV.STAGING, + }) + + return notifications +} diff --git a/src/clients/Governance.ts b/src/clients/Governance.ts index 8874ce426..cb694a387 100644 --- a/src/clients/Governance.ts +++ b/src/clients/Governance.ts @@ -651,4 +651,16 @@ export class Governance extends API { ) return response.data } + + async sendNotification(recipient: string, title: string, body: string) { + const response = await this.fetch>( + `/notifications/send`, + this.options().method('POST').json({ + recipient, + title, + body, + }) + ) + return response.data + } } diff --git a/src/pages/notifications.tsx b/src/pages/notifications.tsx new file mode 100644 index 000000000..e02f2a4ae --- /dev/null +++ b/src/pages/notifications.tsx @@ -0,0 +1,99 @@ +import React, { useState } from 'react' + +import { Web3Provider } from '@ethersproject/providers' +import * as PushAPI from '@pushprotocol/restapi' +import { useQuery } from '@tanstack/react-query' +import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext' +import { Button } from 'decentraland-ui/dist/components/Button/Button' +import { Container } from 'decentraland-ui/dist/components/Container/Container' +import { Field } from 'decentraland-ui/dist/components/Field/Field' + +import { Governance } from '../clients/Governance' +import Heading from '../components/Common/Typography/Heading' +import Label from '../components/Common/Typography/Label' +import LogIn from '../components/User/LogIn' +import useIsDebugAddress from '../hooks/useIsDebugAddress' +import { getCaipAddress } from '../utils/notifications' + +import './debug.css' + +enum ENV { + PROD = 'prod', + STAGING = 'staging', +} + +const CHAIN_ID = 5 +const CHANNEL_ADDRESS = '0xBf363AeDd082Ddd8DB2D6457609B03f9ee74a2F1' + +export default function DebugPage() { + const [user, userState] = useAuthContext() + const { isDebugAddress } = useIsDebugAddress(user) + const [notificationAddress, setNotificationAddress] = useState('') + const [notificationTitle, setNotificationTitle] = useState('') + const [notificationBody, setNotificationBody] = useState('') + const [isSendingNotification, setIsSendingNotification] = useState(false) + + const { data: subscriptions } = useQuery({ + queryKey: [`subscriptions#${user}`], + queryFn: () => + user ? PushAPI.user.getSubscriptions({ user: getCaipAddress(user, CHAIN_ID), env: ENV.STAGING }) : null, + enabled: !!user, + }) + + const handleSubscribeUserToChannel = async () => { + if (!user || !userState.provider) { + return + } + + const signer = new Web3Provider(userState.provider).getSigner() + + await PushAPI.channels.subscribe({ + signer, + channelAddress: getCaipAddress(CHANNEL_ADDRESS, CHAIN_ID), + userAddress: getCaipAddress(user, CHAIN_ID), + onSuccess: () => { + console.log('opt in success') + }, + onError: () => { + console.error('opt in error') + }, + env: ENV.STAGING, + }) + } + + const handleNotificationSend = async (e: any) => { + e.preventDefault() + setIsSendingNotification(true) + try { + await Governance.get().sendNotification(notificationAddress, notificationTitle, notificationBody) + setNotificationAddress('') + setNotificationTitle('') + setNotificationBody('') + } catch (error) { + console.log('Error sending notification', error) + } + setIsSendingNotification(false) + } + + if (!user || !isDebugAddress) { + return + } + + const isSubscribed = !!subscriptions?.find((item: any) => item.channel === CHANNEL_ADDRESS) + + return ( + + Notifications + +
+ + setNotificationAddress(e.target.value)} /> + setNotificationTitle(e.target.value)} /> + setNotificationBody(e.target.value)} /> + + +
+ ) +} diff --git a/src/server.ts b/src/server.ts index 6814b0c47..8827ce457 100644 --- a/src/server.ts +++ b/src/server.ts @@ -24,6 +24,7 @@ import committee from './back/routes/committee' import common from './back/routes/common' import debug from './back/routes/debug' import newsletter from './back/routes/newsletter' +import notification from './back/routes/notification' import project from './back/routes/project' import proposal from './back/routes/proposal' import sitemap from './back/routes/sitemap' @@ -77,6 +78,7 @@ app.use('/api', [ vestings, project, newsletter, + notification, handle(async () => { throw new RequestError('NotFound', RequestError.NotFound) }), diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts new file mode 100644 index 000000000..35c0c86a1 --- /dev/null +++ b/src/utils/notifications.ts @@ -0,0 +1,3 @@ +export function getCaipAddress(address: string, chainId: number) { + return `eip155:${chainId}:${address}` +} From 220b6b1dddd5512e2c46439c223610d204ff363e Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Mon, 4 Sep 2023 15:53:43 -0300 Subject: [PATCH 02/42] feat: get user notifications --- src/back/routes/notification.ts | 25 +++++++++++++++---------- src/clients/Governance.ts | 9 +++++++++ src/pages/notifications.tsx | 31 +++++++++++++++++++++++++++++-- src/utils/notifications.ts | 11 +++++++++++ 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/src/back/routes/notification.ts b/src/back/routes/notification.ts index e3576bdff..38571a6a8 100644 --- a/src/back/routes/notification.ts +++ b/src/back/routes/notification.ts @@ -4,7 +4,9 @@ import handleAPI from 'decentraland-gatsby/dist/entities/Route/handle' import routes from 'decentraland-gatsby/dist/entities/Route/routes' import { ethers } from 'ethers' import { Request } from 'express' +import fetch from 'isomorphic-fetch' +import { isProdEnv } from '../../utils/governanceEnvs' import { getCaipAddress } from '../../utils/notifications' enum ENV { @@ -27,12 +29,13 @@ const CHANNEL_ADDRESS = '0xBf363AeDd082Ddd8DB2D6457609B03f9ee74a2F1' export default routes((router) => { router.post('/notifications/send', handleAPI(handleSendNotification)) - router.get('/notifications/user/:address', handleAPI(handleGetNotifications)) + router.get('/notifications/user/:address', handleAPI(handleUserNotifications)) }) const PUSH_CHANNEL_OWNER_PK = process.env.PUSH_CHANNEL_OWNER_PK +const PUSH_API_URL = process.env.PUSH_API_URL const pkAddress = `0x${PUSH_CHANNEL_OWNER_PK}` -const _signer = new ethers.Wallet(pkAddress) +const signer = new ethers.Wallet(pkAddress) async function handleSendNotification(req: Request) { const { title, body, recipient } = req.body @@ -42,7 +45,7 @@ async function handleSendNotification(req: Request) { } const response = await PushAPI.payloads.sendNotification({ - signer: _signer, + signer, type: NotificationType.TARGET, identityType: NotificationIdentityType.DIRECT_PAYLOAD, notification: { @@ -57,22 +60,24 @@ async function handleSendNotification(req: Request) { }, recipients: getCaipAddress(recipient, CHAIN_ID), channel: getCaipAddress(CHANNEL_ADDRESS, CHAIN_ID), - env: ENV.STAGING, + env: isProdEnv() ? ENV.PROD : ENV.STAGING, }) return response.data } -async function handleGetNotifications(req: Request) { +async function handleUserNotifications(req: Request) { const address = req.params.address if (!address) { throw new RequestError('Missing user', RequestError.BadRequest) } - const notifications = await PushAPI.user.getFeeds({ - user: getCaipAddress(address, CHAIN_ID), - env: ENV.STAGING, - }) + const response = await fetch( + `${PUSH_API_URL}/apis/v1/users/${getCaipAddress(address, CHAIN_ID)}/channels/${getCaipAddress( + CHANNEL_ADDRESS, + CHAIN_ID + )}/feeds` + ) - return notifications + return (await response.json()).feeds } diff --git a/src/clients/Governance.ts b/src/clients/Governance.ts index a398d7cc9..17cd9e991 100644 --- a/src/clients/Governance.ts +++ b/src/clients/Governance.ts @@ -36,6 +36,7 @@ import { Topic } from '../entities/SurveyTopic/types' import { ProjectHealth, UpdateAttributes, UpdateResponse } from '../entities/Updates/types' import { Vote, VotedProposal, Voter } from '../entities/Votes/types' import Time from '../utils/date/Time' +import { Notification } from '../utils/notifications' import { TransparencyBudget } from './DclData' import { @@ -652,6 +653,14 @@ export class Governance extends API { return response.data } + async getUserNotifications(address: string) { + const response = await this.fetch>( + `/notifications/user/${address}`, + this.options().method('GET') + ) + return response.data + } + async sendNotification(recipient: string, title: string, body: string) { const response = await this.fetch>( `/notifications/send`, diff --git a/src/pages/notifications.tsx b/src/pages/notifications.tsx index e02f2a4ae..5821cb5d1 100644 --- a/src/pages/notifications.tsx +++ b/src/pages/notifications.tsx @@ -10,7 +10,7 @@ import { Field } from 'decentraland-ui/dist/components/Field/Field' import { Governance } from '../clients/Governance' import Heading from '../components/Common/Typography/Heading' -import Label from '../components/Common/Typography/Label' +import Text from '../components/Common/Typography/Text' import LogIn from '../components/User/LogIn' import useIsDebugAddress from '../hooks/useIsDebugAddress' import { getCaipAddress } from '../utils/notifications' @@ -40,6 +40,12 @@ export default function DebugPage() { enabled: !!user, }) + const { data: userNotifications } = useQuery({ + queryKey: [`notifications#${user}`], + queryFn: () => (user ? Governance.get().getUserNotifications(user) : null), + enabled: !!user, + }) + const handleSubscribeUserToChannel = async () => { if (!user || !userState.provider) { return @@ -81,14 +87,35 @@ export default function DebugPage() { const isSubscribed = !!subscriptions?.find((item: any) => item.channel === CHANNEL_ADDRESS) + console.log('u', userNotifications) + return ( Notifications + {userNotifications && ( +
+ Your notifications + {userNotifications?.map((notification) => ( +
+ {notification.payload.data.asub} + {notification.payload.data.amsg} +
+ ))} +
+ )}
- + Send notification to user setNotificationAddress(e.target.value)} /> setNotificationTitle(e.target.value)} /> setNotificationBody(e.target.value)} /> diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 35c0c86a1..dd2bb628f 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -1,3 +1,14 @@ +// TODO: Move to notification types file +export type Notification = { + payload_id: number + payload: { + data: { + asub: string + amsg: string + } + } +} + export function getCaipAddress(address: string, chainId: number) { return `eip155:${chainId}:${address}` } From b0effeb1acf5a0cbe99e555887312c93b50d5980 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Thu, 14 Sep 2023 16:14:33 -0300 Subject: [PATCH 03/42] feat: send broadcast notification --- src/back/routes/notification.ts | 34 +++++++++-------- src/clients/Governance.ts | 6 ++- src/components/Layout/Layout.tsx | 5 ++- src/config/env/dev.json | 2 +- src/config/env/local.json | 2 +- src/pages/notifications.tsx | 63 ++++++++++++++++++++++++++------ src/utils/notifications.ts | 6 +++ 7 files changed, 85 insertions(+), 33 deletions(-) diff --git a/src/back/routes/notification.ts b/src/back/routes/notification.ts index 38571a6a8..004712066 100644 --- a/src/back/routes/notification.ts +++ b/src/back/routes/notification.ts @@ -1,4 +1,5 @@ import * as PushAPI from '@pushprotocol/restapi' +import { WithAuth, auth } from 'decentraland-gatsby/dist/entities/Auth/middleware' import RequestError from 'decentraland-gatsby/dist/entities/Route/error' import handleAPI from 'decentraland-gatsby/dist/entities/Route/handle' import routes from 'decentraland-gatsby/dist/entities/Route/routes' @@ -7,19 +8,14 @@ import { Request } from 'express' import fetch from 'isomorphic-fetch' import { isProdEnv } from '../../utils/governanceEnvs' -import { getCaipAddress } from '../../utils/notifications' +import { NotificationType, getCaipAddress } from '../../utils/notifications' +import { validateDebugAddress } from '../utils/validations' enum ENV { PROD = 'prod', STAGING = 'staging', } -const NotificationType = { - TARGET: 3, - SUBSET: 4, - BROADCAST: 1, -} - const NotificationIdentityType = { DIRECT_PAYLOAD: 2, } @@ -28,8 +24,9 @@ const CHAIN_ID = 5 const CHANNEL_ADDRESS = '0xBf363AeDd082Ddd8DB2D6457609B03f9ee74a2F1' export default routes((router) => { - router.post('/notifications/send', handleAPI(handleSendNotification)) - router.get('/notifications/user/:address', handleAPI(handleUserNotifications)) + const withAuth = auth() + router.post('/notifications/send', withAuth, handleAPI(sendNotification)) + router.get('/notifications/user/:address', handleAPI(getUserFeed)) }) const PUSH_CHANNEL_OWNER_PK = process.env.PUSH_CHANNEL_OWNER_PK @@ -37,16 +34,21 @@ const PUSH_API_URL = process.env.PUSH_API_URL const pkAddress = `0x${PUSH_CHANNEL_OWNER_PK}` const signer = new ethers.Wallet(pkAddress) -async function handleSendNotification(req: Request) { - const { title, body, recipient } = req.body +async function sendNotification(req: WithAuth) { + validateDebugAddress(req.auth) + const { title, body, recipient, cta, type } = req.body + + if (type === NotificationType.TARGET && !recipient) { + throw new RequestError('Target type needs recipient', RequestError.BadRequest) + } - if (!recipient || !title || !body) { + if (!title || !body) { throw new RequestError('Invalid data', RequestError.BadRequest) } const response = await PushAPI.payloads.sendNotification({ signer, - type: NotificationType.TARGET, + type, identityType: NotificationIdentityType.DIRECT_PAYLOAD, notification: { title, @@ -55,10 +57,10 @@ async function handleSendNotification(req: Request) { payload: { title, body, - cta: '', + cta, img: '', }, - recipients: getCaipAddress(recipient, CHAIN_ID), + recipients: recipient ? getCaipAddress(recipient, CHAIN_ID) : undefined, channel: getCaipAddress(CHANNEL_ADDRESS, CHAIN_ID), env: isProdEnv() ? ENV.PROD : ENV.STAGING, }) @@ -66,7 +68,7 @@ async function handleSendNotification(req: Request) { return response.data } -async function handleUserNotifications(req: Request) { +async function getUserFeed(req: Request) { const address = req.params.address if (!address) { throw new RequestError('Missing user', RequestError.BadRequest) diff --git a/src/clients/Governance.ts b/src/clients/Governance.ts index 17cd9e991..e8ce12b39 100644 --- a/src/clients/Governance.ts +++ b/src/clients/Governance.ts @@ -661,13 +661,15 @@ export class Governance extends API { return response.data } - async sendNotification(recipient: string, title: string, body: string) { + async sendNotification(recipient: string, title: string, body: string, type: number, url: string) { const response = await this.fetch>( `/notifications/send`, - this.options().method('POST').json({ + this.options().method('POST').authorization({ sign: true }).json({ recipient, title, body, + type, + cta: url, }) ) return response.data diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx index 2a1d7d41e..cb469294c 100644 --- a/src/components/Layout/Layout.tsx +++ b/src/components/Layout/Layout.tsx @@ -3,18 +3,19 @@ import React from 'react' import { ChainId } from '@dcl/schemas/dist/dapps/chain-id' import { useLocation } from '@reach/router' import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext' -import env from 'decentraland-gatsby/dist/utils/env' import { Footer } from 'decentraland-ui/dist/components/Footer/Footer' import { Navbar, NavbarProps } from 'decentraland-ui/dist/components/Navbar/Navbar' import type { PageProps } from 'gatsby' +import { config } from '../../config' import { isProjectPath } from '../../utils/locations' import WalletSelectorModal from '../Modal/WalletSelectorModal' import WrongNetworkModal from '../Modal/WrongNetworkModal' import './Layout.css' -const CHAIN_ID: ChainId[] = env('GATSBY_DEFAULT_CHAIN_ID', String(ChainId.ETHEREUM_MAINNET)) +const CHAIN_ID: ChainId[] = config + .get('GATSBY_DEFAULT_CHAIN_ID', String(ChainId.ETHEREUM_MAINNET)) .split(',') .filter(Boolean) .map((chainId) => Number(chainId)) diff --git a/src/config/env/dev.json b/src/config/env/dev.json index 25c5e2509..2454c5fb0 100644 --- a/src/config/env/dev.json +++ b/src/config/env/dev.json @@ -5,7 +5,7 @@ "GATSBY_DECENTRALAND_API": "https://subscription.decentraland.org", "GATSBY_VESTING_DASHBOARD_URL": "https://vesting.decentraland.org/%23/", "GATSBY_DCL_DATA_API": "https://data.decentraland.vote/", - "GATSBY_DEFAULT_CHAIN_ID": "11155111", + "GATSBY_DEFAULT_CHAIN_ID": "5,11155111", "GATSBY_DISCOURSE_API": "https://forum.decentraland.vote", "GATSBY_DISCOURSE_USER": "dao", "GATSBY_DISCOURSE_CONNECT_THREAD": "1190", diff --git a/src/config/env/local.json b/src/config/env/local.json index 1807bf930..bb5b99560 100644 --- a/src/config/env/local.json +++ b/src/config/env/local.json @@ -1,5 +1,5 @@ { - "GATSBY_DEFAULT_CHAIN_ID": "11155111", + "GATSBY_DEFAULT_CHAIN_ID": "5,11155111", "GATSBY_GOVERNANCE_API": "https://localhost:8000/api", "GATSBY_BUY_MANA_URL": "https://kyberswap.com/swap/eth-mana", "GATSBY_BUY_LAND_URL": "https://market.decentraland.org/browse?section=land", diff --git a/src/pages/notifications.tsx b/src/pages/notifications.tsx index 5821cb5d1..707efd286 100644 --- a/src/pages/notifications.tsx +++ b/src/pages/notifications.tsx @@ -7,13 +7,14 @@ import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext import { Button } from 'decentraland-ui/dist/components/Button/Button' import { Container } from 'decentraland-ui/dist/components/Container/Container' import { Field } from 'decentraland-ui/dist/components/Field/Field' +import { SelectField } from 'decentraland-ui/dist/components/SelectField/SelectField' import { Governance } from '../clients/Governance' import Heading from '../components/Common/Typography/Heading' import Text from '../components/Common/Typography/Text' import LogIn from '../components/User/LogIn' import useIsDebugAddress from '../hooks/useIsDebugAddress' -import { getCaipAddress } from '../utils/notifications' +import { NotificationType, getCaipAddress } from '../utils/notifications' import './debug.css' @@ -28,9 +29,11 @@ const CHANNEL_ADDRESS = '0xBf363AeDd082Ddd8DB2D6457609B03f9ee74a2F1' export default function DebugPage() { const [user, userState] = useAuthContext() const { isDebugAddress } = useIsDebugAddress(user) + const [notificationType, setNotificationType] = useState(NotificationType.TARGET) const [notificationAddress, setNotificationAddress] = useState('') const [notificationTitle, setNotificationTitle] = useState('') const [notificationBody, setNotificationBody] = useState('') + const [notificationURL, setNotificationURL] = useState('') const [isSendingNotification, setIsSendingNotification] = useState(false) const { data: subscriptions } = useQuery({ @@ -67,17 +70,26 @@ export default function DebugPage() { }) } - const handleNotificationSend = async (e: any) => { + const handleSendNotification = async (e: any) => { e.preventDefault() setIsSendingNotification(true) + try { - await Governance.get().sendNotification(notificationAddress, notificationTitle, notificationBody) + await Governance.get().sendNotification( + notificationAddress, + notificationTitle, + notificationBody, + notificationType, + notificationURL + ) setNotificationAddress('') setNotificationTitle('') setNotificationBody('') + setNotificationURL('') } catch (error) { console.log('Error sending notification', error) } + setIsSendingNotification(false) } @@ -87,8 +99,6 @@ export default function DebugPage() { const isSubscribed = !!subscriptions?.find((item: any) => item.channel === CHANNEL_ADDRESS) - console.log('u', userNotifications) - return ( Notifications @@ -114,12 +124,43 @@ export default function DebugPage() { ))} )} - - Send notification to user - setNotificationAddress(e.target.value)} /> - setNotificationTitle(e.target.value)} /> - setNotificationBody(e.target.value)} /> - + + Send notification + setNotificationType(Number(value))} + options={[ + { text: 'Target', value: NotificationType.TARGET }, + { text: 'Broadcast', value: NotificationType.BROADCAST }, + ]} + /> + {notificationType === NotificationType.TARGET && ( + setNotificationAddress(e.target.value)} + /> + )} + setNotificationTitle(e.target.value)} + /> + setNotificationBody(e.target.value)} + /> + setNotificationURL(e.target.value)} + /> + ) diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index dd2bb628f..66ac052de 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -9,6 +9,12 @@ export type Notification = { } } +export const NotificationType = { + BROADCAST: 1, + TARGET: 3, + SUBSET: 4, +} + export function getCaipAddress(address: string, chainId: number) { return `eip155:${chainId}:${address}` } From 4faacacd9bd659716dc91b4431e4d5d14ae97d19 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Thu, 14 Sep 2023 18:15:25 -0300 Subject: [PATCH 04/42] fix: login component import --- src/pages/notifications.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/notifications.tsx b/src/pages/notifications.tsx index 707efd286..a2314a47d 100644 --- a/src/pages/notifications.tsx +++ b/src/pages/notifications.tsx @@ -12,7 +12,7 @@ import { SelectField } from 'decentraland-ui/dist/components/SelectField/SelectF import { Governance } from '../clients/Governance' import Heading from '../components/Common/Typography/Heading' import Text from '../components/Common/Typography/Text' -import LogIn from '../components/User/LogIn' +import LogIn from '../components/Layout/LogIn' import useIsDebugAddress from '../hooks/useIsDebugAddress' import { NotificationType, getCaipAddress } from '../utils/notifications' From 1d9f3ccd60b8fee683c690e73e07114c6bf22674 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Mon, 18 Sep 2023 10:32:34 -0300 Subject: [PATCH 05/42] feat: notification service with mock functions --- src/back/routes/notification.ts | 37 +----------- src/back/routes/proposal.ts | 7 ++- src/back/routes/votes.ts | 7 ++- src/back/services/notification.ts | 94 +++++++++++++++++++++++++++++++ src/entities/Proposal/jobs.ts | 2 + src/services/ProposalService.ts | 10 ++++ 6 files changed, 120 insertions(+), 37 deletions(-) create mode 100644 src/back/services/notification.ts diff --git a/src/back/routes/notification.ts b/src/back/routes/notification.ts index 004712066..d492b77b7 100644 --- a/src/back/routes/notification.ts +++ b/src/back/routes/notification.ts @@ -1,25 +1,14 @@ -import * as PushAPI from '@pushprotocol/restapi' import { WithAuth, auth } from 'decentraland-gatsby/dist/entities/Auth/middleware' import RequestError from 'decentraland-gatsby/dist/entities/Route/error' import handleAPI from 'decentraland-gatsby/dist/entities/Route/handle' import routes from 'decentraland-gatsby/dist/entities/Route/routes' -import { ethers } from 'ethers' import { Request } from 'express' import fetch from 'isomorphic-fetch' -import { isProdEnv } from '../../utils/governanceEnvs' import { NotificationType, getCaipAddress } from '../../utils/notifications' +import { NotificationService } from '../services/notification' import { validateDebugAddress } from '../utils/validations' -enum ENV { - PROD = 'prod', - STAGING = 'staging', -} - -const NotificationIdentityType = { - DIRECT_PAYLOAD: 2, -} - const CHAIN_ID = 5 const CHANNEL_ADDRESS = '0xBf363AeDd082Ddd8DB2D6457609B03f9ee74a2F1' @@ -29,10 +18,7 @@ export default routes((router) => { router.get('/notifications/user/:address', handleAPI(getUserFeed)) }) -const PUSH_CHANNEL_OWNER_PK = process.env.PUSH_CHANNEL_OWNER_PK const PUSH_API_URL = process.env.PUSH_API_URL -const pkAddress = `0x${PUSH_CHANNEL_OWNER_PK}` -const signer = new ethers.Wallet(pkAddress) async function sendNotification(req: WithAuth) { validateDebugAddress(req.auth) @@ -46,26 +32,7 @@ async function sendNotification(req: WithAuth) { throw new RequestError('Invalid data', RequestError.BadRequest) } - const response = await PushAPI.payloads.sendNotification({ - signer, - type, - identityType: NotificationIdentityType.DIRECT_PAYLOAD, - notification: { - title, - body, - }, - payload: { - title, - body, - cta, - img: '', - }, - recipients: recipient ? getCaipAddress(recipient, CHAIN_ID) : undefined, - channel: getCaipAddress(CHANNEL_ADDRESS, CHAIN_ID), - env: isProdEnv() ? ENV.PROD : ENV.STAGING, - }) - - return response.data + return await NotificationService.sendNotification(type, title, body, recipient, cta) } async function getUserFeed(req: Request) { diff --git a/src/back/routes/proposal.ts b/src/back/routes/proposal.ts index d2d2a7bd0..ee523e1fc 100644 --- a/src/back/routes/proposal.ts +++ b/src/back/routes/proposal.ts @@ -84,6 +84,7 @@ import { ProposalInCreation, ProposalService } from '../../services/ProposalServ import { getProfile } from '../../utils/Catalyst' import Time from '../../utils/date/Time' import { ErrorCategory } from '../../utils/errorCategories' +import { NotificationService } from '../services/notification' import { validateAddress, validateProposalId } from '../utils/validations' export default routes((route) => { @@ -521,7 +522,8 @@ export async function updateProposalStatus(req: WithAuth(update, { id }) + if (isEnactedStatus) { + NotificationService.proposalEnacted(proposal) + } ProposalService.commentProposalUpdateInDiscourse(id) diff --git a/src/back/routes/votes.ts b/src/back/routes/votes.ts index c5e9e3550..175fc11d8 100644 --- a/src/back/routes/votes.ts +++ b/src/back/routes/votes.ts @@ -1,3 +1,4 @@ +import RequestError from 'decentraland-gatsby/dist/entities/Route/error' import handleAPI from 'decentraland-gatsby/dist/entities/Route/handle' import routes from 'decentraland-gatsby/dist/entities/Route/routes' import { Request } from 'express' @@ -28,7 +29,7 @@ export async function getProposalVotes(req: Request<{ proposal: string }>) { const id = validateProposalId(req.params.proposal) const proposal = await ProposalService.getProposal(id) - const latestVotes = await VoteService.getVotes(id) + const latestVotes = await VoteService.getVotes(proposal.id) if (!!latestVotes.hash && Time.date(proposal.finish_at).getTime() + Time.Hour < Date.now() && !refresh) { return latestVotes.votes @@ -66,6 +67,10 @@ export async function updateSnapshotProposalVotes(proposal: ProposalAttributes, } export async function getCachedVotes(req: Request) { + if (!req.query.id) { + throw new RequestError('Missing proposal IDs', RequestError.BadRequest) + } + const list = toProposalIds(req.query.id as string[]) const scores = await VotesModel.findAny(list) return scores.reduce((result, vote) => { diff --git a/src/back/services/notification.ts b/src/back/services/notification.ts new file mode 100644 index 000000000..2af64c51c --- /dev/null +++ b/src/back/services/notification.ts @@ -0,0 +1,94 @@ +import * as PushAPI from '@pushprotocol/restapi' +import { ethers } from 'ethers' + +import { ProposalAttributes } from '../../entities/Proposal/types' +import { isProdEnv } from '../../utils/governanceEnvs' +import { getCaipAddress } from '../../utils/notifications' + +enum ENV { + PROD = 'prod', + STAGING = 'staging', +} + +const CHAIN_ID = 5 +const CHANNEL_ADDRESS = '0xBf363AeDd082Ddd8DB2D6457609B03f9ee74a2F1' +const PUSH_CHANNEL_OWNER_PK = process.env.PUSH_CHANNEL_OWNER_PK +const pkAddress = `0x${PUSH_CHANNEL_OWNER_PK}` +const signer = new ethers.Wallet(pkAddress) +const NotificationIdentityType = { + DIRECT_PAYLOAD: 2, +} + +export class NotificationService { + static async sendNotification(type: number, title: string, body: string, recipient: string, url: string) { + const response = await PushAPI.payloads.sendNotification({ + signer, + type, + identityType: NotificationIdentityType.DIRECT_PAYLOAD, + notification: { + title, + body, + }, + payload: { + title, + body, + cta: url, + img: '', + }, + recipients: recipient ? getCaipAddress(recipient, CHAIN_ID) : undefined, + channel: getCaipAddress(CHANNEL_ADDRESS, CHAIN_ID), + env: isProdEnv() ? ENV.PROD : ENV.STAGING, + }) + + return response.data + } + + static async proposalEnacted(proposal: ProposalAttributes) { + /* + Target recipient: Author / Coauthors of the Grant Proposal + Copy: Title: "Grant Proposal Enacted" + Description: "Congratulations! Your Grant Proposal has been successfully enacted and a Vesting Contract was added" + Target URL: Grant Proposal URL + */ + + return true + } + + static async coAuthorRequested(proposal: ProposalAttributes, coAuthors: string[]) { + /* + Trigger: A proposal got published and the user has been added as a coauthor. + Target recipient: Proposed co author + Copy: Title: "Co-author Request Received" + Description: "You've been invited to collaborate as a co-author on a published proposal. Accept it or reject it here" + Target URL: Proposal URL + */ + + return true + } + + static async votingEndedAuthors(proposal: ProposalAttributes, authors: string[]) { + /* + TYPE SUBSET + Trigger: A proposal ended + Target recipient: Authors & Co authors of the proposal + Copy: Title: "Voting Ended on Your Proposal [TITLE]" + Description: "The votes are in! Find out the outcome of the voting on your proposal now." + Target URL: Proposal URL + */ + + return true + } + + static async votingEndedVoters(proposal: ProposalAttributes, voters: string[]) { + /* + TYPE SUBSET + Trigger: A proposal ended + Target recipient: Users who voted on the proposal + Copy: Title: "Voting Ended on a Proposal You Voted On" + Description: "Discover the results of the proposal you participated in as a voter. Your input matters!" + Target URL: Proposal URL + */ + + return true + } +} diff --git a/src/entities/Proposal/jobs.ts b/src/entities/Proposal/jobs.ts index 5a492a6ef..d8c63f7fb 100644 --- a/src/entities/Proposal/jobs.ts +++ b/src/entities/Proposal/jobs.ts @@ -91,6 +91,8 @@ async function getProposalsWithOutcome(proposals: ProposalAttributes[], context: const pendingProposalsWithOutcome = [] for (const proposal of proposals) { + // TODO: New proposal status could be calculated here and added to outcome. + // E.g: OutcomeObject: { ...outcome, newProposalStatus: OutOfBudget } const outcome = await calculateOutcome(proposal, context) if (!outcome) { continue diff --git a/src/services/ProposalService.ts b/src/services/ProposalService.ts index 80a6a73e9..1e68a7599 100644 --- a/src/services/ProposalService.ts +++ b/src/services/ProposalService.ts @@ -2,12 +2,14 @@ import logger from 'decentraland-gatsby/dist/entities/Development/logger' import RequestError from 'decentraland-gatsby/dist/entities/Route/error' import { v1 as uuid } from 'uuid' +import { NotificationService } from '../back/services/notification' import { VoteService } from '../back/services/vote' import { Discourse, DiscourseComment, DiscoursePost } from '../clients/Discourse' import { SnapshotProposalContent } from '../clients/SnapshotGraphqlTypes' import CoauthorModel from '../entities/Coauthor/model' import isDAOCommittee from '../entities/Committee/isDAOCommittee' import ProposalModel from '../entities/Proposal/model' +import { ProposalWithOutcome } from '../entities/Proposal/outcome' import * as templates from '../entities/Proposal/templates' import { getUpdateMessage } from '../entities/Proposal/templates/messages' import { ProposalAttributes, ProposalStatus, ProposalType } from '../entities/Proposal/types' @@ -194,6 +196,7 @@ export class ProposalService { await VotesModel.createEmpty(id) if (coAuthors) { await CoauthorModel.createMultiple(id, coAuthors) + NotificationService.coAuthorRequested(newProposal, coAuthors) } } catch (err: any) { DiscourseService.dropDiscourseTopic(discourseProposal.topic_id) @@ -229,4 +232,11 @@ export class ProposalService { await Discourse.get().commentOnPost(discourseComment) }) } + + static async finishProposals(proposals: ProposalWithOutcome[]) { + // TODO: ProposalModel.finishProposal, update to finishProposals + // TODO: NotificationService.votingEndedAuthors(proposal) + // TODO: Get voters from proposalIds + // TODO: NotificationService.votingEndedVoters(proposal) + } } From 8ec5694a9acdd10a1d6c3b98e06efd58cb4ff708 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Mon, 18 Sep 2023 14:28:39 -0300 Subject: [PATCH 06/42] feat: add notifications feed component in navigation --- .../Icon/NotificationBellActive.tsx | 30 ++++ .../Icon/NotificationBellInactive.tsx | 22 +++ src/components/Layout/Navigation.css | 2 + src/components/Layout/Navigation.tsx | 2 + src/components/NotificationFeed.css | 72 +++++++++ src/components/NotificationFeed.tsx | 143 ++++++++++++++++++ src/components/Sidebar/GovernanceSidebar.tsx | 25 +-- src/hooks/useClickOutside.ts | 24 +++ src/pages/notifications.tsx | 75 +-------- src/utils/notifications.ts | 9 ++ 10 files changed, 310 insertions(+), 94 deletions(-) create mode 100644 src/components/Icon/NotificationBellActive.tsx create mode 100644 src/components/Icon/NotificationBellInactive.tsx create mode 100644 src/components/NotificationFeed.css create mode 100644 src/components/NotificationFeed.tsx create mode 100644 src/hooks/useClickOutside.ts diff --git a/src/components/Icon/NotificationBellActive.tsx b/src/components/Icon/NotificationBellActive.tsx new file mode 100644 index 000000000..990295141 --- /dev/null +++ b/src/components/Icon/NotificationBellActive.tsx @@ -0,0 +1,30 @@ +import React from 'react' + +function NotificationBellActive({ className, size = 32 }: { className?: string; size?: number }) { + return ( + + + + + + + + + + + + + ) +} + +export default NotificationBellActive diff --git a/src/components/Icon/NotificationBellInactive.tsx b/src/components/Icon/NotificationBellInactive.tsx new file mode 100644 index 000000000..4914a9a0b --- /dev/null +++ b/src/components/Icon/NotificationBellInactive.tsx @@ -0,0 +1,22 @@ +import React from 'react' + +function NotificationBellInactive({ size = 32 }) { + return ( + + + + + + + + + + + + ) +} + +export default NotificationBellInactive diff --git a/src/components/Layout/Navigation.css b/src/components/Layout/Navigation.css index e146f8ba5..bb1a8a96b 100644 --- a/src/components/Layout/Navigation.css +++ b/src/components/Layout/Navigation.css @@ -25,6 +25,8 @@ .dcl.tabs-right { display: flex; align-items: center; + gap: 10px; + position: relative; } .NavigationProfilePopUp { diff --git a/src/components/Layout/Navigation.tsx b/src/components/Layout/Navigation.tsx index 48b2194f1..0f84ceef8 100644 --- a/src/components/Layout/Navigation.tsx +++ b/src/components/Layout/Navigation.tsx @@ -14,6 +14,7 @@ import useIsProfileValidated from '../../hooks/useIsProfileValidated' import locations, { isProjectPath } from '../../utils/locations' import Link from '../Common/Typography/Link' import Dot from '../Icon/Dot' +import NotificationFeed from '../NotificationFeed' import SearchInput from '../Search/SearchInput' import './Navigation.css' @@ -121,6 +122,7 @@ const Navigation = ({ activeTab }: NavigationProps) => { + {user && } diff --git a/src/components/NotificationFeed.css b/src/components/NotificationFeed.css new file mode 100644 index 000000000..92de13281 --- /dev/null +++ b/src/components/NotificationFeed.css @@ -0,0 +1,72 @@ +.NotificationFeed__Button { + background: none; + border: none; + padding: 0; + margin: 0; + transition: filter 0.2s ease; + cursor: pointer; +} + +.NotificationFeed__Button--active { + filter: drop-shadow(0 1px 8px var(--alpha-black-300)); +} + +.NotificationFeed__Content { + min-width: 346px; + min-height: 370px; + max-height: 385px; + overflow-y: auto; + pointer-events: none; + opacity: 0; + position: absolute; + top: 60px; + right: -15px; + z-index: 100; + border-radius: 8px; + border: 1px solid var(--alpha-black-300); + background: #fff; + box-shadow: 0px 1px 24px 0px var(--alpha-black-300); + transition: opacity 0.3s ease; + overscroll-behavior-y: contain; +} + +.NotificationFeed__Content--visible { + pointer-events: visible; + opacity: 1; +} + +.NotificationFeed__Header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px; +} + +.NotificationFeed__Title { + margin: 0; + text-transform: uppercase; +} + +.NotificationFeed__OptOut { + margin: 0; + background: none; + border: none; + font-size: 13px; + line-height: 18px; + text-transform: uppercase; + color: var(--dcl-primary); + cursor: pointer; +} + +.NotificationFeed__UnsubscribedView { + padding: 0 16px; + text-align: center; +} + +.NotificationFeed__Item { + padding: 0 16px; +} + +.NotificationFeed__LoadMoreButtonContainer { + padding: 16px; +} diff --git a/src/components/NotificationFeed.tsx b/src/components/NotificationFeed.tsx new file mode 100644 index 000000000..bcaa3af62 --- /dev/null +++ b/src/components/NotificationFeed.tsx @@ -0,0 +1,143 @@ +import React, { useMemo, useState } from 'react' + +import { Web3Provider } from '@ethersproject/providers' +import * as PushAPI from '@pushprotocol/restapi' +import { useQuery } from '@tanstack/react-query' +import classNames from 'classnames' +import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext' +import { Button } from 'decentraland-ui/dist/components/Button/Button' +import { Loader } from 'decentraland-ui/dist/components/Loader/Loader' + +import { Governance } from '../clients/Governance' +import { DEFAULT_QUERY_STALE_TIME } from '../hooks/constants' +import { useClickOutside } from '../hooks/useClickOutside' +import { CHAIN_ID, CHANNEL_ADDRESS, ENV, getCaipAddress } from '../utils/notifications' + +import FullWidthButton from './Common/FullWidthButton' +import Heading from './Common/Typography/Heading' +import Text from './Common/Typography/Text' +import NotificationBellActive from './Icon/NotificationBellActive' +import NotificationBellInactive from './Icon/NotificationBellInactive' + +import './NotificationFeed.css' + +export default function NotificationFeed() { + const [isOpen, setOpen] = useState(false) + const [user, userState] = useAuthContext() + + useClickOutside('.NotificationFeed__Content', isOpen, () => setOpen(false)) + + const { + data: subscriptions, + refetch: refetchSubscriptions, + isLoading: isLoadingSubscriptions, + isRefetching: isRefetchingSubscriptions, + } = useQuery({ + queryKey: [`subscriptions#${user}`], + queryFn: () => + user ? PushAPI.user.getSubscriptions({ user: getCaipAddress(user, CHAIN_ID), env: ENV.STAGING }) : null, + enabled: !!user, + staleTime: DEFAULT_QUERY_STALE_TIME, + }) + + // TODO: Type subscriptions query + const isSubscribed = useMemo( + () => !!subscriptions?.find((item: any) => item.channel === CHANNEL_ADDRESS), + [subscriptions] + ) + const { + data: userNotifications, + isLoading: isLoadingNotifications, + isRefetching: isRefetchingNotifications, + } = useQuery({ + queryKey: [`notifications#${user}`], + queryFn: () => (user ? Governance.get().getUserNotifications(user) : null), + enabled: !!user && isSubscribed, + staleTime: DEFAULT_QUERY_STALE_TIME, + }) + + const handleSubscribeUserToChannel = async () => { + if (!user || !userState.provider) { + return + } + + const signer = new Web3Provider(userState.provider).getSigner() + + await PushAPI.channels.subscribe({ + signer, + channelAddress: getCaipAddress(CHANNEL_ADDRESS, CHAIN_ID), + userAddress: getCaipAddress(user, CHAIN_ID), + onSuccess: () => { + refetchSubscriptions() + }, + env: ENV.STAGING, + }) + } + + const handleUnsubscribeUserToChannel = async () => { + if (!user || !userState.provider) { + return + } + + const signer = new Web3Provider(userState.provider).getSigner() + + await PushAPI.channels.unsubscribe({ + signer, + channelAddress: getCaipAddress(CHANNEL_ADDRESS, CHAIN_ID), + userAddress: getCaipAddress(user, CHAIN_ID), + onSuccess: () => refetchSubscriptions(), + env: ENV.STAGING, + }) + } + + return ( + <> + +
+
+ + Notifications + + {isSubscribed && ( + + )} +
+ {!isSubscribed && ( +
+ + {`Don't miss a beat`} + We rely on a 3rd party service that you’ll have to opt-in to in order to proceed. + +
+ )} + {!isLoadingNotifications && isSubscribed && userNotifications && ( +
+ {userNotifications?.map((notification) => ( +
+ {notification.payload.data.asub} + {notification.payload.data.amsg} +
+ ))} +
+ Load more +
+
+ )} + {(isLoadingSubscriptions || + isRefetchingSubscriptions || + isRefetchingNotifications || + (isSubscribed && isLoadingNotifications)) && } +
+ + ) +} diff --git a/src/components/Sidebar/GovernanceSidebar.tsx b/src/components/Sidebar/GovernanceSidebar.tsx index 579c52ea1..8289b1190 100644 --- a/src/components/Sidebar/GovernanceSidebar.tsx +++ b/src/components/Sidebar/GovernanceSidebar.tsx @@ -1,7 +1,9 @@ -import React, { useEffect } from 'react' +import React from 'react' import Sidebar from 'semantic-ui-react/dist/commonjs/modules/Sidebar/Sidebar' +import { useClickOutside } from '../../hooks/useClickOutside' + import './GovernanceSidebar.css' type Props = { @@ -13,26 +15,7 @@ type Props = { } export default function GovernanceSidebar({ visible, onShow, onHide, onClose, children }: Props) { - useEffect(() => { - function handleClickOutside(event: MouseEvent) { - const sidebar = document.querySelector('.GovernanceSidebar') - if (sidebar && !sidebar.contains(event.target as Node) && !!onClose) { - event.preventDefault() - event.stopPropagation() - onClose() - } - } - - if (visible) { - document.addEventListener('mousedown', handleClickOutside) - } else { - document.removeEventListener('mousedown', handleClickOutside) - } - - return () => { - document.removeEventListener('mousedown', handleClickOutside) - } - }, [visible, onClose]) + useClickOutside('.GovernanceSidebar', !!visible, onClose) return ( void) { + return useEffect(() => { + function handleClickOutside(event: MouseEvent) { + const element = document.querySelector(selector) + if (element && !element.contains(event.target as Node) && !!onClickOutside) { + event.preventDefault() + event.stopPropagation() + onClickOutside() + } + } + + if (shouldListen) { + document.addEventListener('mousedown', handleClickOutside) + } else { + document.removeEventListener('mousedown', handleClickOutside) + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside) + } + }, [selector, shouldListen, onClickOutside]) +} diff --git a/src/pages/notifications.tsx b/src/pages/notifications.tsx index a2314a47d..681d122a5 100644 --- a/src/pages/notifications.tsx +++ b/src/pages/notifications.tsx @@ -1,8 +1,5 @@ import React, { useState } from 'react' -import { Web3Provider } from '@ethersproject/providers' -import * as PushAPI from '@pushprotocol/restapi' -import { useQuery } from '@tanstack/react-query' import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext' import { Button } from 'decentraland-ui/dist/components/Button/Button' import { Container } from 'decentraland-ui/dist/components/Container/Container' @@ -11,23 +8,14 @@ import { SelectField } from 'decentraland-ui/dist/components/SelectField/SelectF import { Governance } from '../clients/Governance' import Heading from '../components/Common/Typography/Heading' -import Text from '../components/Common/Typography/Text' import LogIn from '../components/Layout/LogIn' import useIsDebugAddress from '../hooks/useIsDebugAddress' -import { NotificationType, getCaipAddress } from '../utils/notifications' +import { NotificationType } from '../utils/notifications' import './debug.css' -enum ENV { - PROD = 'prod', - STAGING = 'staging', -} - -const CHAIN_ID = 5 -const CHANNEL_ADDRESS = '0xBf363AeDd082Ddd8DB2D6457609B03f9ee74a2F1' - export default function DebugPage() { - const [user, userState] = useAuthContext() + const [user] = useAuthContext() const { isDebugAddress } = useIsDebugAddress(user) const [notificationType, setNotificationType] = useState(NotificationType.TARGET) const [notificationAddress, setNotificationAddress] = useState('') @@ -36,40 +24,6 @@ export default function DebugPage() { const [notificationURL, setNotificationURL] = useState('') const [isSendingNotification, setIsSendingNotification] = useState(false) - const { data: subscriptions } = useQuery({ - queryKey: [`subscriptions#${user}`], - queryFn: () => - user ? PushAPI.user.getSubscriptions({ user: getCaipAddress(user, CHAIN_ID), env: ENV.STAGING }) : null, - enabled: !!user, - }) - - const { data: userNotifications } = useQuery({ - queryKey: [`notifications#${user}`], - queryFn: () => (user ? Governance.get().getUserNotifications(user) : null), - enabled: !!user, - }) - - const handleSubscribeUserToChannel = async () => { - if (!user || !userState.provider) { - return - } - - const signer = new Web3Provider(userState.provider).getSigner() - - await PushAPI.channels.subscribe({ - signer, - channelAddress: getCaipAddress(CHANNEL_ADDRESS, CHAIN_ID), - userAddress: getCaipAddress(user, CHAIN_ID), - onSuccess: () => { - console.log('opt in success') - }, - onError: () => { - console.error('opt in error') - }, - env: ENV.STAGING, - }) - } - const handleSendNotification = async (e: any) => { e.preventDefault() setIsSendingNotification(true) @@ -97,33 +51,8 @@ export default function DebugPage() { return } - const isSubscribed = !!subscriptions?.find((item: any) => item.channel === CHANNEL_ADDRESS) - return ( - Notifications - - {userNotifications && ( -
- Your notifications - {userNotifications?.map((notification) => ( -
- {notification.payload.data.asub} - {notification.payload.data.amsg} -
- ))} -
- )}
Send notification Date: Tue, 19 Sep 2023 16:43:18 -0300 Subject: [PATCH 07/42] fix: tests crypto not found error with pushapi import --- .env.example | 3 +++ src/back/services/notification.ts | 6 ++++-- src/constants.ts | 1 + src/services/BudgetService.test.ts | 1 + 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 1e58a356c..b2352cc1d 100644 --- a/.env.example +++ b/.env.example @@ -105,3 +105,6 @@ DAO_ROLLBAR_TOKEN="" # Newsletter BEEHIIV_API_KEY= BEEHIIV_PUBLICATION_ID= + +# Notifications +NOTIFICATIONS_SERVICE_ENABLED=false \ No newline at end of file diff --git a/src/back/services/notification.ts b/src/back/services/notification.ts index 2af64c51c..600d89a5f 100644 --- a/src/back/services/notification.ts +++ b/src/back/services/notification.ts @@ -1,10 +1,12 @@ -import * as PushAPI from '@pushprotocol/restapi' import { ethers } from 'ethers' +import { NOTIFICATIONS_SERVICE_ENABLED } from '../../constants' import { ProposalAttributes } from '../../entities/Proposal/types' import { isProdEnv } from '../../utils/governanceEnvs' import { getCaipAddress } from '../../utils/notifications' +const PushAPI = NOTIFICATIONS_SERVICE_ENABLED ? require('@pushprotocol/restapi') : null + enum ENV { PROD = 'prod', STAGING = 'staging', @@ -14,7 +16,7 @@ const CHAIN_ID = 5 const CHANNEL_ADDRESS = '0xBf363AeDd082Ddd8DB2D6457609B03f9ee74a2F1' const PUSH_CHANNEL_OWNER_PK = process.env.PUSH_CHANNEL_OWNER_PK const pkAddress = `0x${PUSH_CHANNEL_OWNER_PK}` -const signer = new ethers.Wallet(pkAddress) +const signer = NOTIFICATIONS_SERVICE_ENABLED ? new ethers.Wallet(pkAddress) : undefined const NotificationIdentityType = { DIRECT_PAYLOAD: 2, } diff --git a/src/constants.ts b/src/constants.ts index ac3560ba9..99e1b6c1f 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -23,6 +23,7 @@ export const LOCAL_ENV_VAR = env('GATSBY_LOCAL_ENV_VAR') || '' export const TEST_ENV_VAR = env('GATSBY_TEST_ENV_VAR') || '' export const PROD_ENV_VAR = env('GATSBY_PROD_ENV_VAR') || '' export const DISCORD_SERVICE_ENABLED = getBooleanStringVar('DISCORD_SERVICE_ENABLED', true) +export const NOTIFICATIONS_SERVICE_ENABLED = getBooleanStringVar('NOTIFICATIONS_SERVICE_ENABLED', true) export const VOTES_VP_THRESHOLD = 5 export const SSO_URL = env('GATSBY_SSO_URL') ?? undefined export const RAFT_OWNER_PK = process.env.RAFT_OWNER_PK || '' diff --git a/src/services/BudgetService.test.ts b/src/services/BudgetService.test.ts index 09bb7dfdf..8a9ed24ce 100644 --- a/src/services/BudgetService.test.ts +++ b/src/services/BudgetService.test.ts @@ -109,6 +109,7 @@ function asserErrorLogging() { jest.mock('../constants', () => ({ DISCORD_SERVICE_ENABLED: false, + NOTIFICATIONS_SERVICE_ENABLED: false, })) describe('BudgetService', () => { From 1209d174ff9399a188c5170a0a7158455ac26fc1 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Tue, 19 Sep 2023 21:59:13 -0300 Subject: [PATCH 08/42] feat: add empty state, pagination and more styles --- src/components/Icon/PeaceCircle.tsx | 13 +++++ src/components/NotificationFeed.css | 24 ++++++-- src/components/NotificationFeed.tsx | 58 ++++++++++++++----- .../Notifications/NotificationItem.css | 11 ++++ .../Notifications/NotificationItem.tsx | 26 +++++++++ src/utils/notifications.ts | 2 + 6 files changed, 117 insertions(+), 17 deletions(-) create mode 100644 src/components/Icon/PeaceCircle.tsx create mode 100644 src/components/Notifications/NotificationItem.css create mode 100644 src/components/Notifications/NotificationItem.tsx diff --git a/src/components/Icon/PeaceCircle.tsx b/src/components/Icon/PeaceCircle.tsx new file mode 100644 index 000000000..c51a0d41b --- /dev/null +++ b/src/components/Icon/PeaceCircle.tsx @@ -0,0 +1,13 @@ +function PeaceCircle() { + return ( + + + + + ) +} + +export default PeaceCircle diff --git a/src/components/NotificationFeed.css b/src/components/NotificationFeed.css index 92de13281..9ce243994 100644 --- a/src/components/NotificationFeed.css +++ b/src/components/NotificationFeed.css @@ -15,9 +15,13 @@ min-width: 346px; min-height: 370px; max-height: 385px; + height: 100; + padding-bottom: 16px; overflow-y: auto; pointer-events: none; opacity: 0; + display: flex; + flex-direction: column; position: absolute; top: 60px; right: -15px; @@ -58,13 +62,25 @@ cursor: pointer; } -.NotificationFeed__UnsubscribedView { - padding: 0 16px; - text-align: center; +.NotificationFeed__EmptyView { + gap: 16px; } -.NotificationFeed__Item { +.NotificationFeed__List { + display: flex; + flex-direction: column; + gap: 10px; +} + +.NotificationFeed__EmptyView, +.NotificationFeed__UnsubscribedView { + display: flex; + flex-direction: column; padding: 0 16px; + align-items: center; + justify-content: center; + text-align: center; + height: 100%; } .NotificationFeed__LoadMoreButtonContainer { diff --git a/src/components/NotificationFeed.tsx b/src/components/NotificationFeed.tsx index bcaa3af62..60d13abcd 100644 --- a/src/components/NotificationFeed.tsx +++ b/src/components/NotificationFeed.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import { Web3Provider } from '@ethersproject/providers' import * as PushAPI from '@pushprotocol/restapi' @@ -9,21 +9,27 @@ import { Button } from 'decentraland-ui/dist/components/Button/Button' import { Loader } from 'decentraland-ui/dist/components/Loader/Loader' import { Governance } from '../clients/Governance' +import { isSameAddress } from '../entities/Snapshot/utils' import { DEFAULT_QUERY_STALE_TIME } from '../hooks/constants' import { useClickOutside } from '../hooks/useClickOutside' -import { CHAIN_ID, CHANNEL_ADDRESS, ENV, getCaipAddress } from '../utils/notifications' +import { CHAIN_ID, CHANNEL_ADDRESS, ENV, Notification, getCaipAddress } from '../utils/notifications' import FullWidthButton from './Common/FullWidthButton' import Heading from './Common/Typography/Heading' import Text from './Common/Typography/Text' import NotificationBellActive from './Icon/NotificationBellActive' import NotificationBellInactive from './Icon/NotificationBellInactive' +import PeaceCircle from './Icon/PeaceCircle' +import NotificationItem from './Notifications/NotificationItem' import './NotificationFeed.css' +const NOTIFICATIONS_PER_PAGE = 5 + export default function NotificationFeed() { const [isOpen, setOpen] = useState(false) const [user, userState] = useAuthContext() + const [filteredNotifications, setFilteredNotifications] = useState([]) useClickOutside('.NotificationFeed__Content', isOpen, () => setOpen(false)) @@ -40,9 +46,8 @@ export default function NotificationFeed() { staleTime: DEFAULT_QUERY_STALE_TIME, }) - // TODO: Type subscriptions query const isSubscribed = useMemo( - () => !!subscriptions?.find((item: any) => item.channel === CHANNEL_ADDRESS), + () => !!subscriptions?.find((item: { channel: string }) => isSameAddress(item.channel, CHANNEL_ADDRESS)), [subscriptions] ) const { @@ -90,6 +95,24 @@ export default function NotificationFeed() { }) } + const handleLoadMoreClick = () => { + const notifications = userNotifications?.slice( + 0, + filteredNotifications.length + NOTIFICATIONS_PER_PAGE + ) as unknown as Notification[] + setFilteredNotifications(notifications) + } + + useEffect(() => { + if (filteredNotifications.length === 0 && userNotifications && userNotifications?.length > 0) { + const notifications = userNotifications.slice(0, NOTIFICATIONS_PER_PAGE) as unknown as Notification[] + setFilteredNotifications(notifications) + } + }, [userNotifications, filteredNotifications.length]) + + const hasNotifications = filteredNotifications && filteredNotifications.length > 0 + const showLoadMoreButton = isSubscribed && !isLoadingNotifications && hasNotifications + return ( <> )} @@ -137,9 +139,9 @@ export default function NotificationFeed() {
{`Don't miss a beat`} - We rely on a 3rd party service that you’ll have to opt-in to in order to proceed. + {t('navigation.notifications.unsubscribed.description')}
)} @@ -147,7 +149,7 @@ export default function NotificationFeed() {
- No notifications at this time + {t('navigation.notifications.empty')}
)} @@ -160,7 +162,9 @@ export default function NotificationFeed() { {filteredNotifications.length !== userNotifications?.length && (
- Load more + + {t('navigation.notifications.load_more')} +
)} diff --git a/src/intl/en.json b/src/intl/en.json index afe413ed2..1239e2f8f 100644 --- a/src/intl/en.json +++ b/src/intl/en.json @@ -51,6 +51,16 @@ "3months": "Last 90 Days" } }, + "notifications": { + "title": "Notifications", + "opt_out": "Opt out", + "unsubscribed": { + "description": "We rely on a 3rd party service that you’ll have to opt-in to in order to proceed.", + "button": "Activate notifications" + }, + "empty": "No notifications at this time", + "load_more": "Load more" + }, "exit": "Are you sure you want to exit? You will lose the changes you have made." }, "mobile_login": { From 69fd5c23e9a1dd76b614dfaf80ed9e62b67f82d8 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Tue, 26 Sep 2023 10:54:26 -0300 Subject: [PATCH 19/42] feat: add signature state --- .../Icon/NotificationBellInactive.tsx | 2 +- src/components/Icon/SignGray.tsx | 13 ++++++++++ src/components/NotificationFeed.tsx | 24 ++++++++++++------- src/intl/en.json | 6 +++++ 4 files changed, 36 insertions(+), 9 deletions(-) create mode 100644 src/components/Icon/SignGray.tsx diff --git a/src/components/Icon/NotificationBellInactive.tsx b/src/components/Icon/NotificationBellInactive.tsx index 4914a9a0b..2475b4b83 100644 --- a/src/components/Icon/NotificationBellInactive.tsx +++ b/src/components/Icon/NotificationBellInactive.tsx @@ -1,6 +1,6 @@ import React from 'react' -function NotificationBellInactive({ size = 32 }) { +function NotificationBellInactive({ size = '32' }) { return ( diff --git a/src/components/Icon/SignGray.tsx b/src/components/Icon/SignGray.tsx new file mode 100644 index 000000000..4775a1031 --- /dev/null +++ b/src/components/Icon/SignGray.tsx @@ -0,0 +1,13 @@ +function SignGray({ size = '124' }) { + return ( + + + + + ) +} + +export default SignGray diff --git a/src/components/NotificationFeed.tsx b/src/components/NotificationFeed.tsx index e58cde8e5..a7858500f 100644 --- a/src/components/NotificationFeed.tsx +++ b/src/components/NotificationFeed.tsx @@ -21,6 +21,7 @@ import Text from './Common/Typography/Text' import NotificationBellActive from './Icon/NotificationBellActive' import NotificationBellInactive from './Icon/NotificationBellInactive' import PeaceCircle from './Icon/PeaceCircle' +import SignGray from './Icon/SignGray' import NotificationItem from './Notifications/NotificationItem' import './NotificationFeed.css' @@ -30,6 +31,7 @@ const NOTIFICATIONS_PER_PAGE = 5 export default function NotificationFeed() { const t = useFormatMessage() const [isOpen, setOpen] = useState(false) + const [isSubscribing, setIsSubscribing] = useState(false) const [user, userState] = useAuthContext() const [filteredNotifications, setFilteredNotifications] = useState([]) @@ -68,6 +70,7 @@ export default function NotificationFeed() { return } + setIsSubscribing(true) const signer = new Web3Provider(userState.provider).getSigner() await PushAPI.channels.subscribe({ @@ -79,6 +82,8 @@ export default function NotificationFeed() { }, env: ENV.STAGING, }) + + setIsSubscribing(false) } const handleUnsubscribeUserToChannel = async () => { @@ -113,7 +118,10 @@ export default function NotificationFeed() { }, [userNotifications, filteredNotifications.length]) const hasNotifications = filteredNotifications && filteredNotifications.length > 0 - const showLoadMoreButton = isSubscribed && !isLoadingNotifications && hasNotifications + const showNotifications = isSubscribed && !isLoadingNotifications && hasNotifications + const showLoadMoreButton = filteredNotifications.length !== userNotifications?.length + const unsubscribedKey = isSubscribing ? 'subscribing' : 'unsubscribed' + const UnsubscribedIcon = isSubscribing ? SignGray : NotificationBellInactive return ( <> @@ -137,11 +145,11 @@ export default function NotificationFeed() { {!isSubscribed && (
- - {`Don't miss a beat`} - {t('navigation.notifications.unsubscribed.description')} -
)} @@ -153,14 +161,14 @@ export default function NotificationFeed() { )} - {showLoadMoreButton && ( + {showNotifications && (
{filteredNotifications?.map((notification) => ( ))}
- {filteredNotifications.length !== userNotifications?.length && ( + {showLoadMoreButton && (
{t('navigation.notifications.load_more')} diff --git a/src/intl/en.json b/src/intl/en.json index 1239e2f8f..b2232f064 100644 --- a/src/intl/en.json +++ b/src/intl/en.json @@ -55,9 +55,15 @@ "title": "Notifications", "opt_out": "Opt out", "unsubscribed": { + "title": "Don't miss a beat", "description": "We rely on a 3rd party service that you’ll have to opt-in to in order to proceed.", "button": "Activate notifications" }, + "subscribing": { + "title": "Setting up notifications", + "description": "Before we begin populating your inbox, we need you to sign a transaction...", + "button": "Signature pending" + }, "empty": "No notifications at this time", "load_more": "Load more" }, From 796522eb88fdcb536ebda443acf278a19e7e898b Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Tue, 26 Sep 2023 16:20:50 -0300 Subject: [PATCH 20/42] feat: add custom types and icons to notification item list --- package-lock.json | 14 +++---- package.json | 2 +- src/back/routes/notification.ts | 11 +++++- src/back/services/notification.ts | 14 ++++++- .../Notifications/NotificationItem.css | 8 ++++ .../Notifications/NotificationItem.tsx | 37 +++++++++++++++---- src/pages/notifications.tsx | 7 +--- src/utils/notifications.ts | 11 +++++- 8 files changed, 79 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index f53fc4e55..b224d0118 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@dcl/ui-env": "1.2.1", "@jparnaudo/react-crypto-icons": "^1.0.5", "@otterspace-xyz/contracts": "^2.7.3", - "@pushprotocol/restapi": "^1.4.16", + "@pushprotocol/restapi": "^1.4.20", "@snapshot-labs/snapshot.js": "0.5.5", "@tanstack/react-query": "^4.29.7", "autoprefixer": "^10.4.4", @@ -6588,9 +6588,9 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@pushprotocol/restapi": { - "version": "1.4.19", - "resolved": "https://registry.npmjs.org/@pushprotocol/restapi/-/restapi-1.4.19.tgz", - "integrity": "sha512-MGeYMX0NB2/sPSOnyvERZL2jKqeBPE6nUgySecEvW/JaUW8DsAX7KSN5IU4xvF9KpQblbUDcdXq4iW55/oZqOA==", + "version": "1.4.20", + "resolved": "https://registry.npmjs.org/@pushprotocol/restapi/-/restapi-1.4.20.tgz", + "integrity": "sha512-JZ+Mz6KvXVS2+dJF3RcK6LZTzPkS8KVL3Mq3GmwMqCiNCzxhcg0Gf0I2hSJGthf3b6GD9diULJFQVdzt3KccwA==", "dependencies": { "@ambire/signature-validator": "^1.3.1", "@metamask/eth-sig-util": "^5.0.2", @@ -50751,9 +50751,9 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "@pushprotocol/restapi": { - "version": "1.4.19", - "resolved": "https://registry.npmjs.org/@pushprotocol/restapi/-/restapi-1.4.19.tgz", - "integrity": "sha512-MGeYMX0NB2/sPSOnyvERZL2jKqeBPE6nUgySecEvW/JaUW8DsAX7KSN5IU4xvF9KpQblbUDcdXq4iW55/oZqOA==", + "version": "1.4.20", + "resolved": "https://registry.npmjs.org/@pushprotocol/restapi/-/restapi-1.4.20.tgz", + "integrity": "sha512-JZ+Mz6KvXVS2+dJF3RcK6LZTzPkS8KVL3Mq3GmwMqCiNCzxhcg0Gf0I2hSJGthf3b6GD9diULJFQVdzt3KccwA==", "requires": { "@ambire/signature-validator": "^1.3.1", "@metamask/eth-sig-util": "^5.0.2", diff --git a/package.json b/package.json index 9b4022987..3e1eebef5 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "@dcl/ui-env": "1.2.1", "@jparnaudo/react-crypto-icons": "^1.0.5", "@otterspace-xyz/contracts": "^2.7.3", - "@pushprotocol/restapi": "^1.4.16", + "@pushprotocol/restapi": "^1.4.20", "@snapshot-labs/snapshot.js": "0.5.5", "@tanstack/react-query": "^4.29.7", "autoprefixer": "^10.4.4", diff --git a/src/back/routes/notification.ts b/src/back/routes/notification.ts index 3274ec752..6fd7f1dca 100644 --- a/src/back/routes/notification.ts +++ b/src/back/routes/notification.ts @@ -4,7 +4,7 @@ import handleAPI from 'decentraland-gatsby/dist/entities/Route/handle' import routes from 'decentraland-gatsby/dist/entities/Route/routes' import { Request } from 'express' -import { NotificationType } from '../../utils/notifications' +import { NotificationCustomType, NotificationType } from '../../utils/notifications' import { NotificationService } from '../services/notification' import { validateDebugAddress } from '../utils/validations' @@ -26,7 +26,14 @@ async function sendNotification(req: WithAuth) { throw new RequestError('Invalid data', RequestError.BadRequest) } - return await NotificationService.sendNotification({ type, title, body, recipient, url: cta }) + return await NotificationService.sendNotification({ + type, + title, + body, + recipient, + url: cta, + customType: NotificationCustomType.Announcement, + }) } async function getUserFeed(req: Request) { diff --git a/src/back/services/notification.ts b/src/back/services/notification.ts index 1cf198c71..d3b1975d5 100644 --- a/src/back/services/notification.ts +++ b/src/back/services/notification.ts @@ -7,7 +7,7 @@ import { proposalUrl } from '../../entities/Proposal/utils' import { ErrorService } from '../../services/ErrorService' import { ErrorCategory } from '../../utils/errorCategories' import { isProdEnv } from '../../utils/governanceEnvs' -import { NotificationType, getCaipAddress } from '../../utils/notifications' +import { NotificationCustomType, NotificationType, getCaipAddress } from '../../utils/notifications' import { CoauthorService } from './coauthor' @@ -38,12 +38,14 @@ export class NotificationService { body, recipient, url, + customType, }: { type?: number title: string body: string recipient: string | string[] | undefined url: string + customType: NotificationCustomType }) { if (!NOTIFICATIONS_SERVICE_ENABLED) { return @@ -62,7 +64,14 @@ export class NotificationService { body, cta: url, img: '', + additionalMeta: { + type: `0+${1}`, // TODO: Check what this means with Push team. + data: JSON.stringify({ + customType, + }), + }, }, + recipients: this.getRecipients(recipient), channel: getCaipAddress(CHANNEL_ADDRESS, CHAIN_ID), env: isProdEnv() ? ENV.PROD : ENV.STAGING, @@ -129,6 +138,7 @@ export class NotificationService { body: 'Congratulations! Your Grant Proposal has been successfully enacted and a Vesting Contract was added', recipient: addresses, url: proposalUrl(proposal.id), + customType: NotificationCustomType.Grant, }) } catch (error) { ErrorService.report('Error sending proposal enacted notification', { @@ -150,6 +160,7 @@ export class NotificationService { body: "You've been invited to collaborate as a co-author on a published proposal. Accept it or reject it here", recipient: coAuthors, url: proposalUrl(proposal.id), + customType: NotificationCustomType.Proposal, }) } catch (error) { ErrorService.report('Error sending co-author request notification', { @@ -175,6 +186,7 @@ export class NotificationService { body: 'The votes are in! Find out the outcome of the voting on your proposal now.', recipient: addresses, url: proposalUrl(proposal.id), + customType: NotificationCustomType.Proposal, }) } catch (error) { ErrorService.report('Error sending voting ended notification to authors', { diff --git a/src/components/Notifications/NotificationItem.css b/src/components/Notifications/NotificationItem.css index 3c8426224..e449ed465 100644 --- a/src/components/Notifications/NotificationItem.css +++ b/src/components/Notifications/NotificationItem.css @@ -1,7 +1,15 @@ .NotificationItem { + display: flex; + flex-direction: row; + gap: 12px; padding: 0 16px; } +.NotificationItem__IconContainer { + width: 24px; + height: 24px; +} + .NotificationItem__Text { margin-bottom: 0; } diff --git a/src/components/Notifications/NotificationItem.tsx b/src/components/Notifications/NotificationItem.tsx index f54d85961..85c066491 100644 --- a/src/components/Notifications/NotificationItem.tsx +++ b/src/components/Notifications/NotificationItem.tsx @@ -1,7 +1,11 @@ import Time from '../../utils/date/Time' -import type { Notification } from '../../utils/notifications' +import { Notification, NotificationCustomType } from '../../utils/notifications' import Link from '../Common/Typography/Link' +import Markdown from '../Common/Typography/Markdown' import Text from '../Common/Typography/Text' +import CheckCircle from '../Icon/CheckCircle' +import CheckCircleOutline from '../Icon/CheckCircleOutline' +import QuestionCircle from '../Icon/QuestionCircle' import './NotificationItem.css' @@ -9,18 +13,37 @@ interface Props { notification: Notification } +function getIcon(metadata: { customType: string }) { + switch (metadata?.customType) { + case NotificationCustomType.Announcement: + return QuestionCircle // TODO: Add Announcement icon + case NotificationCustomType.Grant: + return CheckCircleOutline // TODO: Add Grant icon + case NotificationCustomType.Proposal: + default: + return CheckCircle // TODO: Add Proposal icon + } +} + export default function NotificationItem({ notification }: Props) { const hasLink = !!notification.payload.data.acta const Component = hasLink ? Link : 'div' + const metadata = notification.payload.data.additionalMeta?.data || '' + const Icon = getIcon(metadata ? JSON.parse(metadata) : null) return ( - - {notification.payload.data.amsg} - - - {Time(notification.epoch).fromNow()} - +
+ +
+
+ + {notification.payload.data.amsg} + + + {Time(notification.epoch).fromNow()} + +
) } diff --git a/src/pages/notifications.tsx b/src/pages/notifications.tsx index 681d122a5..9725a0b4a 100644 --- a/src/pages/notifications.tsx +++ b/src/pages/notifications.tsx @@ -83,12 +83,7 @@ export default function DebugPage() { placeholder="Body" onChange={(e) => setNotificationBody(e.target.value)} /> - setNotificationURL(e.target.value)} - /> + setNotificationURL(e.target.value)} /> diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 7df4ed419..c64a4f166 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -1,4 +1,4 @@ -// TODO: Move to notification types file +// TODO: Move types to notification types file export type Notification = { payload_id: number epoch: string @@ -7,6 +7,9 @@ export type Notification = { acta: string asub: string amsg: string + additionalMeta: { + data: string + } } } } @@ -16,6 +19,12 @@ export enum ENV { STAGING = 'staging', } +export enum NotificationCustomType { + Announcement = 'announcement', + Proposal = 'proposal', + Grant = 'grant', +} + export const NotificationType = { BROADCAST: 1, TARGET: 3, From 6c1db531b1d56491ad44abfa8b7590479abf3107 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Wed, 27 Sep 2023 10:23:10 -0300 Subject: [PATCH 21/42] feat: improve notification list styles --- .../Icon/NotificationBellActive.tsx | 2 - .../Icon/NotificationBellInactive.tsx | 2 - src/components/Layout/Navigation.tsx | 4 +- .../Notifications/Notifications.css | 12 ++ .../Notifications/Notifications.tsx | 28 ++++ .../NotificationsFeed.css} | 57 ++++--- .../NotificationsFeed.tsx} | 152 +++++++++--------- src/intl/en.json | 1 + src/pages/notifications.tsx | 2 +- 9 files changed, 152 insertions(+), 108 deletions(-) create mode 100644 src/components/Notifications/Notifications.css create mode 100644 src/components/Notifications/Notifications.tsx rename src/components/{NotificationFeed.css => Notifications/NotificationsFeed.css} (72%) rename src/components/{NotificationFeed.tsx => Notifications/NotificationsFeed.tsx} (52%) diff --git a/src/components/Icon/NotificationBellActive.tsx b/src/components/Icon/NotificationBellActive.tsx index 990295141..8dfdcda86 100644 --- a/src/components/Icon/NotificationBellActive.tsx +++ b/src/components/Icon/NotificationBellActive.tsx @@ -1,5 +1,3 @@ -import React from 'react' - function NotificationBellActive({ className, size = 32 }: { className?: string; size?: number }) { return ( diff --git a/src/components/Layout/Navigation.tsx b/src/components/Layout/Navigation.tsx index 383dfe42c..9a08042f1 100644 --- a/src/components/Layout/Navigation.tsx +++ b/src/components/Layout/Navigation.tsx @@ -12,7 +12,7 @@ import useIsProfileValidated from '../../hooks/useIsProfileValidated' import locations from '../../utils/locations' import Link from '../Common/Typography/Link' import Dot from '../Icon/Dot' -import NotificationFeed from '../NotificationFeed' +import Notifications from '../Notifications/Notifications' import SearchInput from '../Search/SearchInput' import './Navigation.css' @@ -118,7 +118,7 @@ const Navigation = ({ activeTab }: NavigationProps) => { - {user && } + {user && }
diff --git a/src/components/Notifications/Notifications.css b/src/components/Notifications/Notifications.css new file mode 100644 index 000000000..9ab0b4a58 --- /dev/null +++ b/src/components/Notifications/Notifications.css @@ -0,0 +1,12 @@ +.Notifications__Button { + background: none; + border: none; + padding: 0; + margin: 0; + transition: filter 0.2s ease; + cursor: pointer; +} + +.Notifications__Button--active { + filter: drop-shadow(0 1px 8px var(--alpha-black-300)); +} diff --git a/src/components/Notifications/Notifications.tsx b/src/components/Notifications/Notifications.tsx new file mode 100644 index 000000000..89af85e2c --- /dev/null +++ b/src/components/Notifications/Notifications.tsx @@ -0,0 +1,28 @@ +import { useState } from 'react' + +import classNames from 'classnames' + +import useFormatMessage from '../../hooks/useFormatMessage' +import NotificationBellActive from '../Icon/NotificationBellActive' +import NotificationBellInactive from '../Icon/NotificationBellInactive' + +import './Notifications.css' +import NotificationsFeed from './NotificationsFeed' + +export default function Notifications() { + const t = useFormatMessage() + const [isOpen, setOpen] = useState(false) + + return ( + <> + + setOpen(false)} /> + + ) +} diff --git a/src/components/NotificationFeed.css b/src/components/Notifications/NotificationsFeed.css similarity index 72% rename from src/components/NotificationFeed.css rename to src/components/Notifications/NotificationsFeed.css index 9ce243994..ce5326614 100644 --- a/src/components/NotificationFeed.css +++ b/src/components/Notifications/NotificationsFeed.css @@ -1,22 +1,8 @@ -.NotificationFeed__Button { - background: none; - border: none; - padding: 0; - margin: 0; - transition: filter 0.2s ease; - cursor: pointer; -} - -.NotificationFeed__Button--active { - filter: drop-shadow(0 1px 8px var(--alpha-black-300)); -} - -.NotificationFeed__Content { +.NotificationsFeed { min-width: 346px; min-height: 370px; max-height: 385px; height: 100; - padding-bottom: 16px; overflow-y: auto; pointer-events: none; opacity: 0; @@ -34,24 +20,37 @@ overscroll-behavior-y: contain; } -.NotificationFeed__Content--visible { +.NotificationsFeed--visible { pointer-events: visible; opacity: 1; } -.NotificationFeed__Header { +.NotificationsFeed__Button { + background: none; + border: none; + padding: 0; + margin: 0; + transition: filter 0.2s ease; + cursor: pointer; +} + +.NotificationsFeed__Button--active { + filter: drop-shadow(0 1px 8px var(--alpha-black-300)); +} + +.NotificationsFeed__Header { display: flex; align-items: center; justify-content: space-between; padding: 16px; } -.NotificationFeed__Title { +.NotificationsFeed__Title { margin: 0; text-transform: uppercase; } -.NotificationFeed__OptOut { +.NotificationsFeed__OptOut { margin: 0; background: none; border: none; @@ -62,18 +61,26 @@ cursor: pointer; } -.NotificationFeed__EmptyView { +.NotificationsFeed__EmptyView { gap: 16px; } -.NotificationFeed__List { +.NotificationsFeed__ListContainer { display: flex; flex-direction: column; - gap: 10px; + height: 100%; + justify-content: space-between; +} + +.NotificationsFeed__List { + display: flex; + flex-direction: column; + gap: 16px; + padding-bottom: 16px; } -.NotificationFeed__EmptyView, -.NotificationFeed__UnsubscribedView { +.NotificationsFeed__EmptyView, +.NotificationsFeed__UnsubscribedView { display: flex; flex-direction: column; padding: 0 16px; @@ -83,6 +90,6 @@ height: 100%; } -.NotificationFeed__LoadMoreButtonContainer { +.NotificationsFeed__LoadMoreButtonContainer { padding: 16px; } diff --git a/src/components/NotificationFeed.tsx b/src/components/Notifications/NotificationsFeed.tsx similarity index 52% rename from src/components/NotificationFeed.tsx rename to src/components/Notifications/NotificationsFeed.tsx index a7858500f..5d0420c68 100644 --- a/src/components/NotificationFeed.tsx +++ b/src/components/Notifications/NotificationsFeed.tsx @@ -8,34 +8,36 @@ import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext import { Button } from 'decentraland-ui/dist/components/Button/Button' import { Loader } from 'decentraland-ui/dist/components/Loader/Loader' -import { Governance } from '../clients/Governance' -import { isSameAddress } from '../entities/Snapshot/utils' -import { DEFAULT_QUERY_STALE_TIME } from '../hooks/constants' -import { useClickOutside } from '../hooks/useClickOutside' -import useFormatMessage from '../hooks/useFormatMessage' -import { CHAIN_ID, CHANNEL_ADDRESS, ENV, Notification, getCaipAddress } from '../utils/notifications' - -import FullWidthButton from './Common/FullWidthButton' -import Heading from './Common/Typography/Heading' -import Text from './Common/Typography/Text' -import NotificationBellActive from './Icon/NotificationBellActive' -import NotificationBellInactive from './Icon/NotificationBellInactive' -import PeaceCircle from './Icon/PeaceCircle' -import SignGray from './Icon/SignGray' -import NotificationItem from './Notifications/NotificationItem' - -import './NotificationFeed.css' +import { Governance } from '../../clients/Governance' +import { isSameAddress } from '../../entities/Snapshot/utils' +import { DEFAULT_QUERY_STALE_TIME } from '../../hooks/constants' +import { useClickOutside } from '../../hooks/useClickOutside' +import useFormatMessage from '../../hooks/useFormatMessage' +import { CHAIN_ID, CHANNEL_ADDRESS, ENV, Notification, getCaipAddress } from '../../utils/notifications' +import FullWidthButton from '../Common/FullWidthButton' +import Heading from '../Common/Typography/Heading' +import Text from '../Common/Typography/Text' +import NotificationBellInactive from '../Icon/NotificationBellInactive' +import PeaceCircle from '../Icon/PeaceCircle' +import SignGray from '../Icon/SignGray' + +import NotificationItem from './NotificationItem' +import './NotificationsFeed.css' const NOTIFICATIONS_PER_PAGE = 5 -export default function NotificationFeed() { +interface Props { + isOpen: boolean + onClose: () => void +} + +export default function NotificationsFeed({ isOpen, onClose }: Props) { const t = useFormatMessage() - const [isOpen, setOpen] = useState(false) const [isSubscribing, setIsSubscribing] = useState(false) const [user, userState] = useAuthContext() const [filteredNotifications, setFilteredNotifications] = useState([]) - useClickOutside('.NotificationFeed__Content', isOpen, () => setOpen(false)) + useClickOutside('.NotificationsFeed', isOpen, onClose) const { data: subscriptions, @@ -54,6 +56,7 @@ export default function NotificationFeed() { () => !!subscriptions?.find((item: { channel: string }) => isSameAddress(item.channel, CHANNEL_ADDRESS)), [subscriptions] ) + const { data: userNotifications, isLoading: isLoadingNotifications, @@ -122,66 +125,63 @@ export default function NotificationFeed() { const showLoadMoreButton = filteredNotifications.length !== userNotifications?.length const unsubscribedKey = isSubscribing ? 'subscribing' : 'unsubscribed' const UnsubscribedIcon = isSubscribing ? SignGray : NotificationBellInactive + const isLoading = + isLoadingSubscriptions || + isRefetchingSubscriptions || + isRefetchingNotifications || + (isSubscribed && isLoadingNotifications) return ( - <> - -
-
- - {t('navigation.notifications.title')} - - {isSubscribed && ( - - )} -
- {!isSubscribed && ( -
- - {t(`navigation.notifications.${unsubscribedKey}.title`)} - {t(`navigation.notifications.${unsubscribedKey}.description`)} - -
+
+
+ + {t('navigation.notifications.title')} + + {isSubscribed && ( + )} - {isSubscribed && !isLoadingNotifications && !hasNotifications && ( -
- - - {t('navigation.notifications.empty')} - -
- )} - {showNotifications && ( -
-
- {filteredNotifications?.map((notification) => ( - - ))} +
+ {!isLoading && ( + <> + {!isSubscribed && ( +
+ + {t(`navigation.notifications.${unsubscribedKey}.title`)} + {t(`navigation.notifications.${unsubscribedKey}.description`)} +
- {showLoadMoreButton && ( -
- - {t('navigation.notifications.load_more')} - + )} + {isSubscribed && !isLoadingNotifications && !hasNotifications && ( +
+ + + {t('navigation.notifications.empty')} + +
+ )} + {showNotifications && ( +
+
+ {filteredNotifications?.map((notification) => ( + + ))}
- )} -
- )} - {(isLoadingSubscriptions || - isRefetchingSubscriptions || - isRefetchingNotifications || - (isSubscribed && isLoadingNotifications)) && } -
- + {showLoadMoreButton && ( +
+ + {t('navigation.notifications.load_more')} + +
+ )} +
+ )} + + )} + {isLoading && } +
) } diff --git a/src/intl/en.json b/src/intl/en.json index b2232f064..d46b2b236 100644 --- a/src/intl/en.json +++ b/src/intl/en.json @@ -52,6 +52,7 @@ } }, "notifications": { + "button_label": "Notifications menu", "title": "Notifications", "opt_out": "Opt out", "unsubscribed": { diff --git a/src/pages/notifications.tsx b/src/pages/notifications.tsx index 9725a0b4a..c688dea68 100644 --- a/src/pages/notifications.tsx +++ b/src/pages/notifications.tsx @@ -54,7 +54,7 @@ export default function DebugPage() { return (
- Send notification + Send announcement notification setNotificationType(Number(value))} From 6e4b8adbbb3d8bccafb4ea35c140756fbd2f2c35 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Thu, 28 Sep 2023 10:03:01 -0300 Subject: [PATCH 22/42] feat: add notification icons --- src/components/Icon/Notifications/Announcement.tsx | 13 +++++++++++++ src/components/Icon/Notifications/Grant.tsx | 10 ++++++++++ src/components/Icon/Notifications/Proposal.tsx | 13 +++++++++++++ src/components/Notifications/NotificationItem.tsx | 14 +++++++------- 4 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 src/components/Icon/Notifications/Announcement.tsx create mode 100644 src/components/Icon/Notifications/Grant.tsx create mode 100644 src/components/Icon/Notifications/Proposal.tsx diff --git a/src/components/Icon/Notifications/Announcement.tsx b/src/components/Icon/Notifications/Announcement.tsx new file mode 100644 index 000000000..e37bb51cf --- /dev/null +++ b/src/components/Icon/Notifications/Announcement.tsx @@ -0,0 +1,13 @@ +function Announcement() { + return ( + + + + + ) +} + +export default Announcement diff --git a/src/components/Icon/Notifications/Grant.tsx b/src/components/Icon/Notifications/Grant.tsx new file mode 100644 index 000000000..5133683de --- /dev/null +++ b/src/components/Icon/Notifications/Grant.tsx @@ -0,0 +1,10 @@ +function Grant() { + return ( + + + + + ) +} + +export default Grant diff --git a/src/components/Icon/Notifications/Proposal.tsx b/src/components/Icon/Notifications/Proposal.tsx new file mode 100644 index 000000000..6f91282e0 --- /dev/null +++ b/src/components/Icon/Notifications/Proposal.tsx @@ -0,0 +1,13 @@ +function Proposal() { + return ( + + + + + ) +} + +export default Proposal diff --git a/src/components/Notifications/NotificationItem.tsx b/src/components/Notifications/NotificationItem.tsx index 85c066491..94abf971b 100644 --- a/src/components/Notifications/NotificationItem.tsx +++ b/src/components/Notifications/NotificationItem.tsx @@ -3,9 +3,9 @@ import { Notification, NotificationCustomType } from '../../utils/notifications' import Link from '../Common/Typography/Link' import Markdown from '../Common/Typography/Markdown' import Text from '../Common/Typography/Text' -import CheckCircle from '../Icon/CheckCircle' -import CheckCircleOutline from '../Icon/CheckCircleOutline' -import QuestionCircle from '../Icon/QuestionCircle' +import Announcement from '../Icon/Notifications/Announcement' +import Grant from '../Icon/Notifications/Grant' +import Proposal from '../Icon/Notifications/Proposal' import './NotificationItem.css' @@ -16,12 +16,12 @@ interface Props { function getIcon(metadata: { customType: string }) { switch (metadata?.customType) { case NotificationCustomType.Announcement: - return QuestionCircle // TODO: Add Announcement icon + return Announcement case NotificationCustomType.Grant: - return CheckCircleOutline // TODO: Add Grant icon + return Grant case NotificationCustomType.Proposal: default: - return CheckCircle // TODO: Add Proposal icon + return Proposal } } @@ -34,7 +34,7 @@ export default function NotificationItem({ notification }: Props) { return (
- +
From d4230f2b5e38a1075a49a96d6bbd38b3325b5906 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Thu, 28 Sep 2023 13:22:00 -0300 Subject: [PATCH 23/42] feat: send notification to voters --- src/back/routes/snapshot.ts | 2 +- src/back/services/notification.ts | 31 ++++++++++++++++++++----------- src/services/ProposalService.ts | 18 ++++++++++++------ 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/back/routes/snapshot.ts b/src/back/routes/snapshot.ts index 635f9fab0..6423bb5f0 100644 --- a/src/back/routes/snapshot.ts +++ b/src/back/routes/snapshot.ts @@ -25,7 +25,7 @@ export default routes((router) => { router.get('/snapshot/proposal-scores/:proposalSnapshotId', handleAPI(getProposalScores)) }) -async function getStatus(req: Request) { +async function getStatus() { return await SnapshotService.getStatus() } diff --git a/src/back/services/notification.ts b/src/back/services/notification.ts index d3b1975d5..586e0fd23 100644 --- a/src/back/services/notification.ts +++ b/src/back/services/notification.ts @@ -197,16 +197,25 @@ export class NotificationService { } } - static async votingEndedVoters(proposal: ProposalAttributes, voters: string[]) { - /* - TYPE SUBSET - Trigger: A proposal ended - Target recipient: Users who voted on the proposal - Copy: Title: "Voting Ended on a Proposal You Voted On" - Description: "Discover the results of the proposal you participated in as a voter. Your input matters!" - Target URL: Proposal URL - */ - - return true + static async votingEndedVoters(proposal: ProposalAttributes, addresses: string[]) { + try { + if (!areValidAddresses(addresses)) { + throw new Error('Invalid addresses') + } + + return await this.sendNotification({ + title: 'Voting Ended on a Proposal You Voted On', + body: 'Discover the results of the proposal you participated in as a voter. Your input matters!', + recipient: addresses, + url: proposalUrl(proposal.id), + customType: NotificationCustomType.Proposal, + }) + } catch (error) { + ErrorService.report('Error sending voting ended notification to voters', { + error, + category: ErrorCategory.Notifications, + proposal, + }) + } } } diff --git a/src/services/ProposalService.ts b/src/services/ProposalService.ts index 03e9eb2c3..053084d3f 100644 --- a/src/services/ProposalService.ts +++ b/src/services/ProposalService.ts @@ -6,6 +6,7 @@ import { NotificationService } from '../back/services/notification' import { VoteService } from '../back/services/vote' import { Discourse, DiscourseComment, DiscoursePost } from '../clients/Discourse' import { SnapshotProposalContent } from '../clients/SnapshotTypes' +import { NOTIFICATIONS_SERVICE_ENABLED } from '../constants' import CoauthorModel from '../entities/Coauthor/model' import isDAOCommittee from '../entities/Committee/isDAOCommittee' import ProposalModel from '../entities/Proposal/model' @@ -19,12 +20,10 @@ import VotesModel from '../entities/Votes/model' import { inBackground } from '../helpers' import { getProfile } from '../utils/Catalyst' import Time from '../utils/date/Time' -import { ErrorCategory } from '../utils/errorCategories' import { getEnvironmentChainId } from '../utils/votes/utils' import { DiscordService } from './DiscordService' import { DiscourseService } from './DiscourseService' -import { ErrorService } from './ErrorService' import { SnapshotService } from './SnapshotService' export type ProposalInCreation = { @@ -241,10 +240,17 @@ export class ProposalService { status ) - for (const proposal of proposals) { - NotificationService.votingEndedAuthors(proposal) - // TODO: Get voters from proposalIds - // TODO: NotificationService.votingEndedVoters(proposal, voters) + if (NOTIFICATIONS_SERVICE_ENABLED) { + for (const proposal of proposals) { + try { + NotificationService.votingEndedAuthors(proposal) + const votes = await VoteService.getVotes(proposal.id) + const voters = Object.keys(votes) + NotificationService.votingEndedVoters(proposal, voters) + } catch (error) { + logger.log('Error sending notifications on proposal finish', { proposalId: proposal.id }) + } + } } } } From e115d8e90f70ebe78a5261f79c43df5f105546b0 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Thu, 28 Sep 2023 17:23:02 -0300 Subject: [PATCH 24/42] refactor: move types and env vars --- .env.example | 3 +- src/back/routes/notification.ts | 3 +- src/back/services/notification.ts | 23 ++++++++------ src/clients/Governance.ts | 2 +- .../Notifications/NotificationItem.tsx | 2 +- .../Notifications/NotificationsFeed.tsx | 22 ++++++++----- src/config/env/dev.json | 3 +- src/config/env/local.json | 3 +- src/config/env/prd.json | 3 +- src/shared/types/notifications.ts | 25 +++++++++++++++ src/utils/notifications.ts | 31 ++----------------- 11 files changed, 67 insertions(+), 53 deletions(-) create mode 100644 src/shared/types/notifications.ts diff --git a/.env.example b/.env.example index f3fdf3b21..cd8e553c8 100644 --- a/.env.example +++ b/.env.example @@ -109,4 +109,5 @@ BEEHIIV_PUBLICATION_ID= # Notifications NOTIFICATIONS_SERVICE_ENABLED=false PUSH_API_URL=https://backend-staging.epns.io -PUSH_CHANNEL_OWNER_PK= \ No newline at end of file +PUSH_CHANNEL_OWNER_PK= +GATSBY_PUSH_CHANNEL_ID= \ No newline at end of file diff --git a/src/back/routes/notification.ts b/src/back/routes/notification.ts index 6fd7f1dca..81e7fdd7a 100644 --- a/src/back/routes/notification.ts +++ b/src/back/routes/notification.ts @@ -4,7 +4,8 @@ import handleAPI from 'decentraland-gatsby/dist/entities/Route/handle' import routes from 'decentraland-gatsby/dist/entities/Route/routes' import { Request } from 'express' -import { NotificationCustomType, NotificationType } from '../../utils/notifications' +import { NotificationCustomType } from '../../shared/types/notifications' +import { NotificationType } from '../../utils/notifications' import { NotificationService } from '../services/notification' import { validateDebugAddress } from '../utils/validations' diff --git a/src/back/services/notification.ts b/src/back/services/notification.ts index 586e0fd23..131129f84 100644 --- a/src/back/services/notification.ts +++ b/src/back/services/notification.ts @@ -1,3 +1,4 @@ +import { ChainId } from '@dcl/schemas/dist/dapps/chain-id' import { ethers } from 'ethers' import isEthereumAddress from 'validator/lib/isEthereumAddress' @@ -5,9 +6,10 @@ import { NOTIFICATIONS_SERVICE_ENABLED } from '../../constants' import { ProposalAttributes } from '../../entities/Proposal/types' import { proposalUrl } from '../../entities/Proposal/utils' import { ErrorService } from '../../services/ErrorService' +import { NotificationCustomType } from '../../shared/types/notifications' import { ErrorCategory } from '../../utils/errorCategories' import { isProdEnv } from '../../utils/governanceEnvs' -import { NotificationCustomType, NotificationType, getCaipAddress } from '../../utils/notifications' +import { NotificationType, PUSH_CHANNEL_ID, getCaipAddress } from '../../utils/notifications' import { CoauthorService } from './coauthor' @@ -18,8 +20,7 @@ enum ENV { STAGING = 'staging', } -const CHAIN_ID = 5 -const CHANNEL_ADDRESS = '0xBf363AeDd082Ddd8DB2D6457609B03f9ee74a2F1' +const chainId = isProdEnv() ? ChainId.ETHEREUM_MAINNET : ChainId.ETHEREUM_GOERLI const PUSH_CHANNEL_OWNER_PK = process.env.PUSH_CHANNEL_OWNER_PK const PUSH_API_URL = process.env.PUSH_API_URL const pkAddress = `0x${PUSH_CHANNEL_OWNER_PK}` @@ -27,6 +28,8 @@ const signer = NOTIFICATIONS_SERVICE_ENABLED ? new ethers.Wallet(pkAddress) : un const NotificationIdentityType = { DIRECT_PAYLOAD: 2, } +const ADDITIONAL_META_CUSTOM_TYPE = 0 +const ADDITIONAL_META_CUSTOM_TYPE_VERSION = 1 const areValidAddresses = (addresses: string[]) => Array.isArray(addresses) && addresses.every((item) => isEthereumAddress(item)) @@ -65,7 +68,7 @@ export class NotificationService { cta: url, img: '', additionalMeta: { - type: `0+${1}`, // TODO: Check what this means with Push team. + type: `${ADDITIONAL_META_CUSTOM_TYPE}+${ADDITIONAL_META_CUSTOM_TYPE_VERSION}`, data: JSON.stringify({ customType, }), @@ -73,7 +76,7 @@ export class NotificationService { }, recipients: this.getRecipients(recipient), - channel: getCaipAddress(CHANNEL_ADDRESS, CHAIN_ID), + channel: getCaipAddress(PUSH_CHANNEL_ID, chainId), env: isProdEnv() ? ENV.PROD : ENV.STAGING, }) @@ -102,18 +105,18 @@ export class NotificationService { } if (Array.isArray(recipient)) { - return recipient.map((item: string) => getCaipAddress(item, CHAIN_ID)) + return recipient.map((item: string) => getCaipAddress(item, chainId)) } - return getCaipAddress(recipient, CHAIN_ID) + return getCaipAddress(recipient, chainId) } static async getUserFeed(address: string) { try { const response = await fetch( - `${PUSH_API_URL}/apis/v1/users/${getCaipAddress(address, CHAIN_ID)}/channels/${getCaipAddress( - CHANNEL_ADDRESS, - CHAIN_ID + `${PUSH_API_URL}/apis/v1/users/${getCaipAddress(address, chainId)}/channels/${getCaipAddress( + PUSH_CHANNEL_ID, + chainId )}/feeds` ) diff --git a/src/clients/Governance.ts b/src/clients/Governance.ts index 0ffa24f07..dd0ff9377 100644 --- a/src/clients/Governance.ts +++ b/src/clients/Governance.ts @@ -36,8 +36,8 @@ import { Topic } from '../entities/SurveyTopic/types' import { ProjectHealth, UpdateAttributes, UpdateResponse } from '../entities/Updates/types' import { Vote, VotedProposal, Voter } from '../entities/Votes/types' import { NewsletterSubscriptionResult } from '../shared/types/newsletter' +import { Notification } from '../shared/types/notifications' import Time from '../utils/date/Time' -import { Notification } from '../utils/notifications' import { TransparencyBudget } from './DclData' import { diff --git a/src/components/Notifications/NotificationItem.tsx b/src/components/Notifications/NotificationItem.tsx index 94abf971b..ba91341f5 100644 --- a/src/components/Notifications/NotificationItem.tsx +++ b/src/components/Notifications/NotificationItem.tsx @@ -1,5 +1,5 @@ +import { Notification, NotificationCustomType } from '../../shared/types/notifications' import Time from '../../utils/date/Time' -import { Notification, NotificationCustomType } from '../../utils/notifications' import Link from '../Common/Typography/Link' import Markdown from '../Common/Typography/Markdown' import Text from '../Common/Typography/Text' diff --git a/src/components/Notifications/NotificationsFeed.tsx b/src/components/Notifications/NotificationsFeed.tsx index 5d0420c68..d1c92c076 100644 --- a/src/components/Notifications/NotificationsFeed.tsx +++ b/src/components/Notifications/NotificationsFeed.tsx @@ -1,5 +1,6 @@ import { useEffect, useMemo, useState } from 'react' +import { ChainId } from '@dcl/schemas/dist/dapps/chain-id' import { Web3Provider } from '@ethersproject/providers' import * as PushAPI from '@pushprotocol/restapi' import { useQuery } from '@tanstack/react-query' @@ -13,7 +14,8 @@ import { isSameAddress } from '../../entities/Snapshot/utils' import { DEFAULT_QUERY_STALE_TIME } from '../../hooks/constants' import { useClickOutside } from '../../hooks/useClickOutside' import useFormatMessage from '../../hooks/useFormatMessage' -import { CHAIN_ID, CHANNEL_ADDRESS, ENV, Notification, getCaipAddress } from '../../utils/notifications' +import { ENV, Notification } from '../../shared/types/notifications' +import { PUSH_CHANNEL_ID, getCaipAddress } from '../../utils/notifications' import FullWidthButton from '../Common/FullWidthButton' import Heading from '../Common/Typography/Heading' import Text from '../Common/Typography/Text' @@ -38,6 +40,7 @@ export default function NotificationsFeed({ isOpen, onClose }: Props) { const [filteredNotifications, setFilteredNotifications] = useState([]) useClickOutside('.NotificationsFeed', isOpen, onClose) + const chainId = userState.chainId || ChainId.ETHEREUM_GOERLI const { data: subscriptions, @@ -47,13 +50,18 @@ export default function NotificationsFeed({ isOpen, onClose }: Props) { } = useQuery({ queryKey: [`subscriptions#${user}`], queryFn: () => - user ? PushAPI.user.getSubscriptions({ user: getCaipAddress(user, CHAIN_ID), env: ENV.STAGING }) : null, + user + ? PushAPI.user.getSubscriptions({ + user: getCaipAddress(user, chainId), + env: ENV.STAGING, + }) + : null, enabled: !!user, staleTime: DEFAULT_QUERY_STALE_TIME, }) const isSubscribed = useMemo( - () => !!subscriptions?.find((item: { channel: string }) => isSameAddress(item.channel, CHANNEL_ADDRESS)), + () => !!subscriptions?.find((item: { channel: string }) => isSameAddress(item.channel, PUSH_CHANNEL_ID)), [subscriptions] ) @@ -78,8 +86,8 @@ export default function NotificationsFeed({ isOpen, onClose }: Props) { await PushAPI.channels.subscribe({ signer, - channelAddress: getCaipAddress(CHANNEL_ADDRESS, CHAIN_ID), - userAddress: getCaipAddress(user, CHAIN_ID), + channelAddress: getCaipAddress(PUSH_CHANNEL_ID, chainId), + userAddress: getCaipAddress(user, chainId), onSuccess: () => { refetchSubscriptions() }, @@ -98,8 +106,8 @@ export default function NotificationsFeed({ isOpen, onClose }: Props) { await PushAPI.channels.unsubscribe({ signer, - channelAddress: getCaipAddress(CHANNEL_ADDRESS, CHAIN_ID), - userAddress: getCaipAddress(user, CHAIN_ID), + channelAddress: getCaipAddress(PUSH_CHANNEL_ID, chainId), + userAddress: getCaipAddress(user, chainId), onSuccess: () => refetchSubscriptions(), env: ENV.STAGING, }) diff --git a/src/config/env/dev.json b/src/config/env/dev.json index 36866c031..4d65b490a 100644 --- a/src/config/env/dev.json +++ b/src/config/env/dev.json @@ -41,5 +41,6 @@ "GATSBY_VOTING_POWER_TO_PASS_HIRING": "99", "GATSBY_DURATION_HIRING": "1200", "GATSBY_SUBMISSION_THRESHOLD_HIRING": "99", - "GATSBY_SSO_URL": "https://id.decentraland.zone" + "GATSBY_SSO_URL": "https://id.decentraland.zone", + "GATSBY_PUSH_CHANNEL_ID": "0xBf363AeDd082Ddd8DB2D6457609B03f9ee74a2F1" } diff --git a/src/config/env/local.json b/src/config/env/local.json index 3579e9c51..cae065d00 100644 --- a/src/config/env/local.json +++ b/src/config/env/local.json @@ -38,5 +38,6 @@ "GATSBY_VOTING_POWER_TO_PASS_HIRING": "99", "GATSBY_DURATION_HIRING": "1200", "GATSBY_SUBMISSION_THRESHOLD_HIRING": "99", - "GATSBY_SSO_URL": "https://id.decentraland.zone" + "GATSBY_SSO_URL": "https://id.decentraland.zone", + "GATSBY_PUSH_CHANNEL_ID": "0xBf363AeDd082Ddd8DB2D6457609B03f9ee74a2F1" } diff --git a/src/config/env/prd.json b/src/config/env/prd.json index a0d8f4bf5..abdda9bd8 100644 --- a/src/config/env/prd.json +++ b/src/config/env/prd.json @@ -37,5 +37,6 @@ "GATSBY_VOTING_POWER_TO_PASS_PITCH": "2000000", "GATSBY_VOTING_POWER_TO_PASS_TENDER": "4000000", "GATSBY_VOTING_POWER_TO_PASS_HIRING": "6000000", - "GATSBY_SSO_URL": "https://id.decentraland.org" + "GATSBY_SSO_URL": "https://id.decentraland.org", + "GATSBY_PUSH_CHANNEL_ID": "" } diff --git a/src/shared/types/notifications.ts b/src/shared/types/notifications.ts new file mode 100644 index 000000000..82223a654 --- /dev/null +++ b/src/shared/types/notifications.ts @@ -0,0 +1,25 @@ +export type Notification = { + payload_id: number + epoch: string + payload: { + data: { + acta: string + asub: string + amsg: string + additionalMeta: { + data: string + } + } + } +} + +export enum ENV { + PROD = 'prod', + STAGING = 'staging', +} + +export enum NotificationCustomType { + Announcement = 'announcement', + Proposal = 'proposal', + Grant = 'grant', +} diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index c64a4f166..6375b1877 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -1,29 +1,4 @@ -// TODO: Move types to notification types file -export type Notification = { - payload_id: number - epoch: string - payload: { - data: { - acta: string - asub: string - amsg: string - additionalMeta: { - data: string - } - } - } -} - -export enum ENV { - PROD = 'prod', - STAGING = 'staging', -} - -export enum NotificationCustomType { - Announcement = 'announcement', - Proposal = 'proposal', - Grant = 'grant', -} +import { clientEnv } from './clientEnv' export const NotificationType = { BROADCAST: 1, @@ -35,6 +10,4 @@ export function getCaipAddress(address: string, chainId: number) { return `eip155:${chainId}:${address}` } -// TODO: Move to env vars -export const CHAIN_ID = 5 -export const CHANNEL_ADDRESS = '0xBf363AeDd082Ddd8DB2D6457609B03f9ee74a2F1' +export const PUSH_CHANNEL_ID = process.env.GATSBY_PUSH_CHANNEL_ID || clientEnv('GATSBY_PUSH_CHANNEL_ID') From c5d4eba37ce32fcbc5061df05b01306046541cc4 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Fri, 29 Sep 2023 10:31:06 -0300 Subject: [PATCH 25/42] fix: voting power to pass constants names --- src/entities/Proposal/constants.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/entities/Proposal/constants.ts b/src/entities/Proposal/constants.ts index 3882cd93c..180d6bea9 100644 --- a/src/entities/Proposal/constants.ts +++ b/src/entities/Proposal/constants.ts @@ -11,9 +11,9 @@ export const VOTING_POWER_TO_PASS_BAN_NAME = export const VOTING_POWER_TO_PASS_POI = process.env.VOTING_POWER_TO_PASS_POI || clientEnv('GATSBY_VOTING_POWER_TO_PASS_POI') export const VOTING_POWER_TO_PASS_POLL = - process.env.VOTING_POWER_TO_PASS_POLL || clientEnv('GATSBY_VOTING_POWER_TO_PASS_POLL') + process.env.GATSBY_VOTING_POWER_TO_PASS_POLL || clientEnv('GATSBY_VOTING_POWER_TO_PASS_POLL') export const VOTING_POWER_TO_PASS_DRAFT = - process.env.VOTING_POWER_TO_PASS_DRAFT || clientEnv('GATSBY_VOTING_POWER_TO_PASS_DRAFT') + process.env.GATSBY_VOTING_POWER_TO_PASS_DRAFT || clientEnv('GATSBY_VOTING_POWER_TO_PASS_DRAFT') export const VOTING_POWER_TO_PASS_GOVERNANCE = process.env.GATSBY_VOTING_POWER_TO_PASS_GOVERNANCE || clientEnv('GATSBY_VOTING_POWER_TO_PASS_GOVERNANCE') export const VOTING_POWER_TO_PASS_PITCH = @@ -35,4 +35,4 @@ export const SUBMISSION_THRESHOLD_TENDER = export const SUBMISSION_THRESHOLD_HIRING = process.env.GATSBY_SUBMISSION_THRESHOLD_HIRING || clientEnv('GATSBY_SUBMISSION_THRESHOLD_HIRING') export const VOTING_POWER_TO_PASS_HIRING = - process.env.VOTING_POWER_TO_PASS_HIRING || clientEnv('GATSBY_VOTING_POWER_TO_PASS_HIRING') + process.env.GATSBY_VOTING_POWER_TO_PASS_HIRING || clientEnv('GATSBY_VOTING_POWER_TO_PASS_HIRING') From 554c2adf23bc0f0fb6a0abd7c536e625322bfbb2 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Fri, 29 Sep 2023 10:41:25 -0300 Subject: [PATCH 26/42] feat: move notification form to debug --- src/components/Debug/Notifications.tsx | 88 ++++++++++++++++++++++++++ src/pages/debug.tsx | 28 ++++---- 2 files changed, 103 insertions(+), 13 deletions(-) create mode 100644 src/components/Debug/Notifications.tsx diff --git a/src/components/Debug/Notifications.tsx b/src/components/Debug/Notifications.tsx new file mode 100644 index 000000000..f63276969 --- /dev/null +++ b/src/components/Debug/Notifications.tsx @@ -0,0 +1,88 @@ +import { useState } from 'react' + +import { Button } from 'decentraland-ui/dist/components/Button/Button' +import { Field } from 'decentraland-ui/dist/components/Field/Field' +import { SelectField } from 'decentraland-ui/dist/components/SelectField/SelectField' + +import { Governance } from '../../clients/Governance' +import { NotificationType } from '../../utils/notifications' +import Heading from '../Common/Typography/Heading' +import { ContentSection } from '../Layout/ContentLayout' + +interface Props { + className?: string +} + +export default function Notifications({ className }: Props) { + const [notificationType, setNotificationType] = useState(NotificationType.TARGET) + const [notificationAddress, setNotificationAddress] = useState('') + const [notificationTitle, setNotificationTitle] = useState('') + const [notificationBody, setNotificationBody] = useState('') + const [notificationURL, setNotificationURL] = useState('') + const [isSendingNotification, setIsSendingNotification] = useState(false) + + const handleSendNotification = async (e: any) => { + e.preventDefault() + setIsSendingNotification(true) + + try { + await Governance.get().sendNotification( + notificationAddress, + notificationTitle, + notificationBody, + notificationType, + notificationURL + ) + setNotificationAddress('') + setNotificationTitle('') + setNotificationBody('') + setNotificationURL('') + } catch (error) { + console.log('Error sending notification', error) + } + + setIsSendingNotification(false) + } + + return ( +
+ + + Send announcement notification + setNotificationType(Number(value))} + options={[ + { text: 'Target', value: NotificationType.TARGET }, + { text: 'Broadcast', value: NotificationType.BROADCAST }, + ]} + /> + {notificationType === NotificationType.TARGET && ( + setNotificationAddress(e.target.value)} + /> + )} + setNotificationTitle(e.target.value)} + /> + setNotificationBody(e.target.value)} + /> + setNotificationURL(e.target.value)} /> + + + +
+ ) +} diff --git a/src/pages/debug.tsx b/src/pages/debug.tsx index a2df07607..3094df323 100644 --- a/src/pages/debug.tsx +++ b/src/pages/debug.tsx @@ -1,5 +1,3 @@ -import React from 'react' - import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext' import { Container } from 'decentraland-ui/dist/components/Container/Container' @@ -10,6 +8,7 @@ import BudgetsUpdate from '../components/Debug/BudgetsUpdate' import EnvStatus from '../components/Debug/EnvStatus' import ErrorReporting from '../components/Debug/ErrorReporting' import HttpStatus from '../components/Debug/HttpStatus' +import Notifications from '../components/Debug/Notifications' import Snapshot from '../components/Debug/Snapshot' import TriggerFunction from '../components/Debug/TriggerFunction' import LogIn from '../components/Layout/LogIn' @@ -27,17 +26,20 @@ export default function DebugPage() { } return ( - + <> - {'Version'} - {process.env.GATSBY_VERSION_NUMBER} - - - - - - - - + + {'Version'} + {process.env.GATSBY_VERSION_NUMBER} + + + + + + + + + + ) } From 1da431ce97156cfc54cbe42aea3b7b64785d6575 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Fri, 29 Sep 2023 11:43:54 -0300 Subject: [PATCH 27/42] fix: notification button position and search icon color --- src/components/Notifications/Notifications.css | 1 + src/images/icons/magnify.svg | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Notifications/Notifications.css b/src/components/Notifications/Notifications.css index 9ab0b4a58..f21eeee68 100644 --- a/src/components/Notifications/Notifications.css +++ b/src/components/Notifications/Notifications.css @@ -1,4 +1,5 @@ .Notifications__Button { + display: flex; background: none; border: none; padding: 0; diff --git a/src/images/icons/magnify.svg b/src/images/icons/magnify.svg index c1f4129b4..181c39391 100644 --- a/src/images/icons/magnify.svg +++ b/src/images/icons/magnify.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file From 2654b9d53ebbdc5f693bb9462787e6ce64dedf82 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Mon, 2 Oct 2023 11:27:25 -0300 Subject: [PATCH 28/42] fix: center notifications feed states --- .../Notifications/NotificationsFeed.css | 26 ++++++++++++++----- .../Notifications/NotificationsFeed.tsx | 15 ++++++----- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/components/Notifications/NotificationsFeed.css b/src/components/Notifications/NotificationsFeed.css index ce5326614..857aee755 100644 --- a/src/components/Notifications/NotificationsFeed.css +++ b/src/components/Notifications/NotificationsFeed.css @@ -2,7 +2,7 @@ min-width: 346px; min-height: 370px; max-height: 385px; - height: 100; + height: 100%; overflow-y: auto; pointer-events: none; opacity: 0; @@ -42,6 +42,7 @@ display: flex; align-items: center; justify-content: space-between; + height: 48px; padding: 16px; } @@ -61,10 +62,6 @@ cursor: pointer; } -.NotificationsFeed__EmptyView { - gap: 16px; -} - .NotificationsFeed__ListContainer { display: flex; flex-direction: column; @@ -79,15 +76,32 @@ padding-bottom: 16px; } +.NotificationsFeed__Content { + height: 100%; +} + .NotificationsFeed__EmptyView, .NotificationsFeed__UnsubscribedView { display: flex; flex-direction: column; padding: 0 16px; align-items: center; - justify-content: center; text-align: center; +} + +.NotificationsFeed__UnsubscribedView { + margin-top: 8px; + justify-content: flex-start; +} + +.NotificationsFeed__UnsubscribedViewHeading { + margin-top: 20px; +} + +.NotificationsFeed__EmptyView { height: 100%; + justify-content: center; + gap: 16px; } .NotificationsFeed__LoadMoreButtonContainer { diff --git a/src/components/Notifications/NotificationsFeed.tsx b/src/components/Notifications/NotificationsFeed.tsx index d1c92c076..4f55037f1 100644 --- a/src/components/Notifications/NotificationsFeed.tsx +++ b/src/components/Notifications/NotificationsFeed.tsx @@ -41,6 +41,7 @@ export default function NotificationsFeed({ isOpen, onClose }: Props) { useClickOutside('.NotificationsFeed', isOpen, onClose) const chainId = userState.chainId || ChainId.ETHEREUM_GOERLI + const env = ENV.STAGING // TODO: check if it is prod or staging according to chain id const { data: subscriptions, @@ -53,7 +54,7 @@ export default function NotificationsFeed({ isOpen, onClose }: Props) { user ? PushAPI.user.getSubscriptions({ user: getCaipAddress(user, chainId), - env: ENV.STAGING, + env, }) : null, enabled: !!user, @@ -91,7 +92,7 @@ export default function NotificationsFeed({ isOpen, onClose }: Props) { onSuccess: () => { refetchSubscriptions() }, - env: ENV.STAGING, + env, }) setIsSubscribing(false) @@ -109,7 +110,7 @@ export default function NotificationsFeed({ isOpen, onClose }: Props) { channelAddress: getCaipAddress(PUSH_CHANNEL_ID, chainId), userAddress: getCaipAddress(user, chainId), onSuccess: () => refetchSubscriptions(), - env: ENV.STAGING, + env, }) } @@ -152,11 +153,13 @@ export default function NotificationsFeed({ isOpen, onClose }: Props) { )}
{!isLoading && ( - <> +
{!isSubscribed && (
- {t(`navigation.notifications.${unsubscribedKey}.title`)} + + {t(`navigation.notifications.${unsubscribedKey}.title`)} + {t(`navigation.notifications.${unsubscribedKey}.description`)}
)} - +
)} {isLoading && }
From 9e3857d631dafae72c6e68a449c578b333585583 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Mon, 2 Oct 2023 14:07:43 -0300 Subject: [PATCH 29/42] fix: support multiple chain ids env var --- src/clients/SnapshotApi.ts | 2 +- src/components/Layout/Layout.tsx | 13 +------------ src/constants.ts | 3 +++ src/helpers/index.ts | 23 +++++++++++++++++++++++ src/services/ProposalService.ts | 3 +-- src/services/RpcService.ts | 2 +- src/utils/votes/utils.ts | 18 ------------------ 7 files changed, 30 insertions(+), 34 deletions(-) diff --git a/src/clients/SnapshotApi.ts b/src/clients/SnapshotApi.ts index 400e82abd..ad76274a9 100644 --- a/src/clients/SnapshotApi.ts +++ b/src/clients/SnapshotApi.ts @@ -14,9 +14,9 @@ import { SNAPSHOT_SPACE, } from '../entities/Snapshot/constants' import { getChecksumAddress } from '../entities/Snapshot/utils' +import { getEnvironmentChainId } from '../helpers' import { ProposalInCreation, ProposalLifespan } from '../services/ProposalService' import Time from '../utils/date/Time' -import { getEnvironmentChainId } from '../utils/votes/utils' import { SnapshotGraphql } from './SnapshotGraphql' import { trimLastForwardSlash } from './utils' diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx index 34aaf4bd7..36925a83d 100644 --- a/src/components/Layout/Layout.tsx +++ b/src/components/Layout/Layout.tsx @@ -1,29 +1,18 @@ import React from 'react' -import { ChainId } from '@dcl/schemas/dist/dapps/chain-id' import { useLocation } from '@reach/router' import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext' import { Footer } from 'decentraland-ui/dist/components/Footer/Footer' import { Navbar, NavbarProps } from 'decentraland-ui/dist/components/Navbar/Navbar' import type { PageProps } from 'gatsby' -import { config } from '../../config' +import { getSupportedChainIds } from '../../helpers' import { isProjectPath } from '../../utils/locations' import WalletSelectorModal from '../Modal/WalletSelectorModal' import WrongNetworkModal from '../Modal/WrongNetworkModal' import './Layout.css' -const CHAIN_ID: ChainId[] = config - .get('GATSBY_DEFAULT_CHAIN_ID', String(ChainId.ETHEREUM_MAINNET)) - .split(',') - .filter(Boolean) - .map((chainId) => Number(chainId)) - -export function getSupportedChainIds(): ChainId[] { - return CHAIN_ID -} - export type LayoutProps = Omit & { rightMenu: NavbarProps['rightMenu'] children?: React.ReactNode diff --git a/src/constants.ts b/src/constants.ts index f921b732a..2367368b4 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,3 +1,4 @@ +import { ChainId } from '@dcl/schemas' import env from 'decentraland-gatsby/dist/utils/env' import isEthereumAddress from 'validator/lib/isEthereumAddress' @@ -41,3 +42,5 @@ export const DEBUG_ADDRESSES = (process.env.DEBUG_ADDRESSES || '') .filter(isEthereumAddress) .map((address) => address.toLowerCase()) export const SNAPSHOT_STATUS_ENABLED = false +export const DEFAULT_CHAIN_ID = + process.env.GATSBY_DEFAULT_CHAIN_ID || clientEnv('GATSBY_DEFAULT_CHAIN_ID', String(ChainId.ETHEREUM_MAINNET)) diff --git a/src/helpers/index.ts b/src/helpers/index.ts index 5ef4b774d..d968e0b5b 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -1,7 +1,9 @@ +import { ChainId } from '@dcl/schemas/dist/dapps/chain-id' import logger from 'decentraland-gatsby/dist/entities/Development/logger' import isEthereumAddress from 'validator/lib/isEthereumAddress' import isURL from 'validator/lib/isURL' +import { DEFAULT_CHAIN_ID } from '../constants' import { clientEnv } from '../utils/clientEnv' export const CURRENCY_FORMAT_OPTIONS = { @@ -107,3 +109,24 @@ export function splitArray(array: Type[], chunkSize: number): Type[][] { array.slice(index * chunkSize, (index + 1) * chunkSize) ) } + +export function getSupportedChainIds(): ChainId[] { + return (DEFAULT_CHAIN_ID || '') + .split(',') + .filter(Boolean) + .map((chainId) => Number(chainId)) +} + +export function getEnvironmentChainId() { + const chainId = getSupportedChainIds()[0] + switch (chainId) { + case ChainId.ETHEREUM_MAINNET.valueOf(): + return ChainId.ETHEREUM_MAINNET + case ChainId.ETHEREUM_GOERLI: + return ChainId.ETHEREUM_GOERLI + case ChainId.ETHEREUM_SEPOLIA: + return ChainId.ETHEREUM_SEPOLIA + default: + throw new Error(`GATSBY_DEFAULT_CHAIN_ID is not a supported network: ${chainId}`) + } +} diff --git a/src/services/ProposalService.ts b/src/services/ProposalService.ts index 053084d3f..b5d06faef 100644 --- a/src/services/ProposalService.ts +++ b/src/services/ProposalService.ts @@ -17,10 +17,9 @@ import { ProposalAttributes, ProposalStatus, ProposalType } from '../entities/Pr import { isGrantProposalSubmitEnabled } from '../entities/Proposal/utils' import { SNAPSHOT_SPACE } from '../entities/Snapshot/constants' import VotesModel from '../entities/Votes/model' -import { inBackground } from '../helpers' +import { getEnvironmentChainId, inBackground } from '../helpers' import { getProfile } from '../utils/Catalyst' import Time from '../utils/date/Time' -import { getEnvironmentChainId } from '../utils/votes/utils' import { DiscordService } from './DiscordService' import { DiscourseService } from './DiscourseService' diff --git a/src/services/RpcService.ts b/src/services/RpcService.ts index f3173e952..7727f2158 100644 --- a/src/services/RpcService.ts +++ b/src/services/RpcService.ts @@ -1,6 +1,6 @@ import { JsonRpcProvider, getNetwork } from '@ethersproject/providers' -import { getEnvironmentChainId } from '../utils/votes/utils' +import { getEnvironmentChainId } from '../helpers' export default class RpcService { static async getBlockNumber(): Promise { diff --git a/src/utils/votes/utils.ts b/src/utils/votes/utils.ts index d70f9bd26..98ac71ad3 100644 --- a/src/utils/votes/utils.ts +++ b/src/utils/votes/utils.ts @@ -1,31 +1,13 @@ -import { ChainId } from '@dcl/schemas' import upperFirst from 'lodash/upperFirst' import { DelegationsLabelProps } from '../../components/Proposal/View/ProposalVoting/DelegationsLabel' import { VotedChoice } from '../../components/Proposal/View/ProposalVoting/VotedChoiceButton' import { Vote } from '../../entities/Votes/types' import { Scores } from '../../entities/Votes/utils' -import { clientEnv } from '../clientEnv' import { DelegationsLabelBuilder } from './helpers/DelegationsLabelBuilder' import { VotedChoiceBuilder } from './helpers/VotedChoiceBuilder' -const DEFAULT_CHAIN_ID = process.env.GATSBY_DEFAULT_CHAIN_ID || clientEnv('GATSBY_DEFAULT_CHAIN_ID') - -export function getEnvironmentChainId() { - const CHAIN_ID = Number(DEFAULT_CHAIN_ID) - switch (CHAIN_ID) { - case ChainId.ETHEREUM_MAINNET.valueOf(): - return ChainId.ETHEREUM_MAINNET - case ChainId.ETHEREUM_GOERLI: - return ChainId.ETHEREUM_GOERLI - case ChainId.ETHEREUM_SEPOLIA: - return ChainId.ETHEREUM_SEPOLIA - default: - throw new Error(`GATSBY_DEFAULT_CHAIN_ID is not Mainnet or Testnet: ${DEFAULT_CHAIN_ID}`) - } -} - export interface VotingSectionConfigProps { vote: Vote | null delegateVote: Vote | null From 6860187c6ed91ce6a4e4952bd95d5e291dcc55b3 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Mon, 2 Oct 2023 15:20:03 -0300 Subject: [PATCH 30/42] feat: send proposal enacted notification only to grants --- src/back/routes/proposal.ts | 7 ++++--- src/back/services/notification.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/back/routes/proposal.ts b/src/back/routes/proposal.ts index ee523e1fc..4cbe844a4 100644 --- a/src/back/routes/proposal.ts +++ b/src/back/routes/proposal.ts @@ -523,10 +523,11 @@ export async function updateProposalStatus(req: WithAuth(update, { id }) - if (isEnactedStatus) { - NotificationService.proposalEnacted(proposal) + if (isEnactedStatus && isGrantProposal) { + NotificationService.grantProposalEnacted(proposal) } ProposalService.commentProposalUpdateInDiscourse(id) diff --git a/src/back/services/notification.ts b/src/back/services/notification.ts index 131129f84..ddc00901e 100644 --- a/src/back/services/notification.ts +++ b/src/back/services/notification.ts @@ -126,7 +126,7 @@ export class NotificationService { } } - static async proposalEnacted(proposal: ProposalAttributes) { + static async grantProposalEnacted(proposal: ProposalAttributes) { try { const coauthors = await CoauthorService.getAllFromProposalId(proposal.id) const coauthorsAddresses = coauthors.length > 0 ? coauthors.map((coauthor) => coauthor.address) : [] From 20405b08c68305334d4f8ece2f245e88b7c52c7b Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Mon, 2 Oct 2023 15:25:40 -0300 Subject: [PATCH 31/42] fix: subscriptions query keys and cleanup --- src/back/services/notification.ts | 5 +---- src/back/utils/validations.ts | 3 +++ src/components/Notifications/NotificationsFeed.css | 2 +- src/components/Notifications/NotificationsFeed.tsx | 2 +- src/hooks/useSubscriptions.ts | 2 +- src/pages/proposal.tsx | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/back/services/notification.ts b/src/back/services/notification.ts index ddc00901e..1af0c3363 100644 --- a/src/back/services/notification.ts +++ b/src/back/services/notification.ts @@ -1,6 +1,5 @@ import { ChainId } from '@dcl/schemas/dist/dapps/chain-id' import { ethers } from 'ethers' -import isEthereumAddress from 'validator/lib/isEthereumAddress' import { NOTIFICATIONS_SERVICE_ENABLED } from '../../constants' import { ProposalAttributes } from '../../entities/Proposal/types' @@ -10,6 +9,7 @@ import { NotificationCustomType } from '../../shared/types/notifications' import { ErrorCategory } from '../../utils/errorCategories' import { isProdEnv } from '../../utils/governanceEnvs' import { NotificationType, PUSH_CHANNEL_ID, getCaipAddress } from '../../utils/notifications' +import { areValidAddresses } from '../utils/validations' import { CoauthorService } from './coauthor' @@ -31,9 +31,6 @@ const NotificationIdentityType = { const ADDITIONAL_META_CUSTOM_TYPE = 0 const ADDITIONAL_META_CUSTOM_TYPE_VERSION = 1 -const areValidAddresses = (addresses: string[]) => - Array.isArray(addresses) && addresses.every((item) => isEthereumAddress(item)) - export class NotificationService { static async sendNotification({ type, diff --git a/src/back/utils/validations.ts b/src/back/utils/validations.ts index 8ecf4aed8..65cd9d8eb 100644 --- a/src/back/utils/validations.ts +++ b/src/back/utils/validations.ts @@ -72,6 +72,9 @@ export function validateAddress(address?: string) { return address } +export const areValidAddresses = (addresses: string[]) => + Array.isArray(addresses) && addresses.every((item) => isEthereumAddress(item)) + export function validateProposalSnapshotId(proposalSnapshotId?: string) { if (!proposalSnapshotId || proposalSnapshotId.length === 0) { throw new RequestError('Invalid snapshot id') diff --git a/src/components/Notifications/NotificationsFeed.css b/src/components/Notifications/NotificationsFeed.css index 857aee755..3330b0a23 100644 --- a/src/components/Notifications/NotificationsFeed.css +++ b/src/components/Notifications/NotificationsFeed.css @@ -14,7 +14,7 @@ z-index: 100; border-radius: 8px; border: 1px solid var(--alpha-black-300); - background: #fff; + background: var(--white-900); box-shadow: 0px 1px 24px 0px var(--alpha-black-300); transition: opacity 0.3s ease; overscroll-behavior-y: contain; diff --git a/src/components/Notifications/NotificationsFeed.tsx b/src/components/Notifications/NotificationsFeed.tsx index 4f55037f1..db1650d61 100644 --- a/src/components/Notifications/NotificationsFeed.tsx +++ b/src/components/Notifications/NotificationsFeed.tsx @@ -49,7 +49,7 @@ export default function NotificationsFeed({ isOpen, onClose }: Props) { isLoading: isLoadingSubscriptions, isRefetching: isRefetchingSubscriptions, } = useQuery({ - queryKey: [`subscriptions#${user}`], + queryKey: [`pushSubscriptions#${user}`], queryFn: () => user ? PushAPI.user.getSubscriptions({ diff --git a/src/hooks/useSubscriptions.ts b/src/hooks/useSubscriptions.ts index 99023d37d..938cdfdca 100644 --- a/src/hooks/useSubscriptions.ts +++ b/src/hooks/useSubscriptions.ts @@ -7,7 +7,7 @@ import { DEFAULT_QUERY_STALE_TIME } from './constants' export default function useSubscriptions() { const [account] = useAuthContext() - const queryKey = `subscriptions#${account}` + const queryKey = `userSubscriptions#${account}` const { data: subscriptions, ...subscriptionsState } = useQuery({ queryKey: [queryKey], queryFn: async () => { diff --git a/src/pages/proposal.tsx b/src/pages/proposal.tsx index 14a8c6a1f..05487d8ed 100644 --- a/src/pages/proposal.tsx +++ b/src/pages/proposal.tsx @@ -173,7 +173,7 @@ export default function ProposalPage() { const { votes, isLoadingVotes, reloadVotes } = useProposalVotes(proposal?.id) const { highQualityVotes, lowQualityVotes } = useMemo(() => getVoteSegmentation(votes), [votes]) - const subscriptionsQueryKey = `subscriptions#${proposal?.id || ''}` + const subscriptionsQueryKey = `proposalSubscriptions#${proposal?.id || ''}` const { data: subscriptions, isLoading: isSubscriptionsLoading } = useQuery({ queryKey: [subscriptionsQueryKey], queryFn: () => { From d730a2c38a612ff8a3eb9eaf130862f209c4fa0f Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Mon, 2 Oct 2023 15:38:17 -0300 Subject: [PATCH 32/42] feat: add function to get push notifications env --- src/back/services/notification.ts | 9 ++------- src/components/Notifications/NotificationsFeed.tsx | 6 +++--- src/utils/notifications.ts | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/back/services/notification.ts b/src/back/services/notification.ts index 1af0c3363..bc7121d11 100644 --- a/src/back/services/notification.ts +++ b/src/back/services/notification.ts @@ -8,18 +8,13 @@ import { ErrorService } from '../../services/ErrorService' import { NotificationCustomType } from '../../shared/types/notifications' import { ErrorCategory } from '../../utils/errorCategories' import { isProdEnv } from '../../utils/governanceEnvs' -import { NotificationType, PUSH_CHANNEL_ID, getCaipAddress } from '../../utils/notifications' +import { NotificationType, PUSH_CHANNEL_ID, getCaipAddress, getPushNotificationsEnv } from '../../utils/notifications' import { areValidAddresses } from '../utils/validations' import { CoauthorService } from './coauthor' const PushAPI = NOTIFICATIONS_SERVICE_ENABLED ? require('@pushprotocol/restapi') : null -enum ENV { - PROD = 'prod', - STAGING = 'staging', -} - const chainId = isProdEnv() ? ChainId.ETHEREUM_MAINNET : ChainId.ETHEREUM_GOERLI const PUSH_CHANNEL_OWNER_PK = process.env.PUSH_CHANNEL_OWNER_PK const PUSH_API_URL = process.env.PUSH_API_URL @@ -74,7 +69,7 @@ export class NotificationService { recipients: this.getRecipients(recipient), channel: getCaipAddress(PUSH_CHANNEL_ID, chainId), - env: isProdEnv() ? ENV.PROD : ENV.STAGING, + env: getPushNotificationsEnv(chainId), }) return response.data diff --git a/src/components/Notifications/NotificationsFeed.tsx b/src/components/Notifications/NotificationsFeed.tsx index db1650d61..ac5ccdd56 100644 --- a/src/components/Notifications/NotificationsFeed.tsx +++ b/src/components/Notifications/NotificationsFeed.tsx @@ -14,8 +14,8 @@ import { isSameAddress } from '../../entities/Snapshot/utils' import { DEFAULT_QUERY_STALE_TIME } from '../../hooks/constants' import { useClickOutside } from '../../hooks/useClickOutside' import useFormatMessage from '../../hooks/useFormatMessage' -import { ENV, Notification } from '../../shared/types/notifications' -import { PUSH_CHANNEL_ID, getCaipAddress } from '../../utils/notifications' +import { Notification } from '../../shared/types/notifications' +import { PUSH_CHANNEL_ID, getCaipAddress, getPushNotificationsEnv } from '../../utils/notifications' import FullWidthButton from '../Common/FullWidthButton' import Heading from '../Common/Typography/Heading' import Text from '../Common/Typography/Text' @@ -41,7 +41,7 @@ export default function NotificationsFeed({ isOpen, onClose }: Props) { useClickOutside('.NotificationsFeed', isOpen, onClose) const chainId = userState.chainId || ChainId.ETHEREUM_GOERLI - const env = ENV.STAGING // TODO: check if it is prod or staging according to chain id + const env = getPushNotificationsEnv(chainId) const { data: subscriptions, diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 6375b1877..67e7e6667 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -1,3 +1,7 @@ +import { ChainId } from '@dcl/schemas/dist/dapps/chain-id' + +import { ENV } from '../shared/types/notifications' + import { clientEnv } from './clientEnv' export const NotificationType = { @@ -11,3 +15,13 @@ export function getCaipAddress(address: string, chainId: number) { } export const PUSH_CHANNEL_ID = process.env.GATSBY_PUSH_CHANNEL_ID || clientEnv('GATSBY_PUSH_CHANNEL_ID') + +export function getPushNotificationsEnv(chainId: ChainId) { + switch (chainId) { + case ChainId.ETHEREUM_MAINNET: + return ENV.PROD + case ChainId.ETHEREUM_GOERLI: + default: + return ENV.STAGING + } +} From 54713251dc5adcfb2515683a59ad2028feeabb88 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Thu, 5 Oct 2023 14:05:02 -0300 Subject: [PATCH 33/42] refactor: code cleanup --- src/back/routes/notification.ts | 4 ++-- src/back/services/notification.ts | 14 ++++++++------ src/clients/Governance.ts | 2 +- src/components/Notifications/NotificationsFeed.tsx | 3 ++- src/constants.ts | 1 + src/utils/notifications.ts | 4 ---- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/back/routes/notification.ts b/src/back/routes/notification.ts index 81e7fdd7a..a8e0d2c65 100644 --- a/src/back/routes/notification.ts +++ b/src/back/routes/notification.ts @@ -17,7 +17,7 @@ export default routes((router) => { async function sendNotification(req: WithAuth) { validateDebugAddress(req.auth) - const { title, body, recipient, cta, type } = req.body + const { title, body, recipient, url, type } = req.body if (type === NotificationType.TARGET && !recipient) { throw new RequestError('Target type needs recipient', RequestError.BadRequest) @@ -32,7 +32,7 @@ async function sendNotification(req: WithAuth) { title, body, recipient, - url: cta, + url, customType: NotificationCustomType.Announcement, }) } diff --git a/src/back/services/notification.ts b/src/back/services/notification.ts index bc7121d11..05014e00c 100644 --- a/src/back/services/notification.ts +++ b/src/back/services/notification.ts @@ -1,14 +1,14 @@ import { ChainId } from '@dcl/schemas/dist/dapps/chain-id' import { ethers } from 'ethers' -import { NOTIFICATIONS_SERVICE_ENABLED } from '../../constants' +import { NOTIFICATIONS_SERVICE_ENABLED, PUSH_CHANNEL_ID } from '../../constants' import { ProposalAttributes } from '../../entities/Proposal/types' import { proposalUrl } from '../../entities/Proposal/utils' import { ErrorService } from '../../services/ErrorService' import { NotificationCustomType } from '../../shared/types/notifications' import { ErrorCategory } from '../../utils/errorCategories' import { isProdEnv } from '../../utils/governanceEnvs' -import { NotificationType, PUSH_CHANNEL_ID, getCaipAddress, getPushNotificationsEnv } from '../../utils/notifications' +import { NotificationType, getCaipAddress, getPushNotificationsEnv } from '../../utils/notifications' import { areValidAddresses } from '../utils/validations' import { CoauthorService } from './coauthor' @@ -26,6 +26,8 @@ const NotificationIdentityType = { const ADDITIONAL_META_CUSTOM_TYPE = 0 const ADDITIONAL_META_CUSTOM_TYPE_VERSION = 1 +type Recipient = string | string[] | undefined + export class NotificationService { static async sendNotification({ type, @@ -38,7 +40,7 @@ export class NotificationService { type?: number title: string body: string - recipient: string | string[] | undefined + recipient: Recipient url: string customType: NotificationCustomType }) { @@ -75,7 +77,7 @@ export class NotificationService { return response.data } - private static getType(type: number | undefined, recipient: string | string[] | undefined) { + private static getType(type: number | undefined, recipient: Recipient) { if (type) { return type } @@ -91,7 +93,7 @@ export class NotificationService { return NotificationType.TARGET } - private static getRecipients(recipient: string | string[] | undefined) { + private static getRecipients(recipient: Recipient) { if (!recipient) { return undefined } @@ -178,7 +180,7 @@ export class NotificationService { return await this.sendNotification({ title: `Voting Ended on Your Proposal ${proposal.title}`, - body: 'The votes are in! Find out the outcome of the voting on your proposal now.', + body: 'The votes are in! Find out the outcome of the voting on your proposal now', recipient: addresses, url: proposalUrl(proposal.id), customType: NotificationCustomType.Proposal, diff --git a/src/clients/Governance.ts b/src/clients/Governance.ts index dd0ff9377..cbb5aab72 100644 --- a/src/clients/Governance.ts +++ b/src/clients/Governance.ts @@ -684,7 +684,7 @@ export class Governance extends API { title, body, type, - cta: url, + url, }) ) return response.data diff --git a/src/components/Notifications/NotificationsFeed.tsx b/src/components/Notifications/NotificationsFeed.tsx index ac5ccdd56..e621c7ebc 100644 --- a/src/components/Notifications/NotificationsFeed.tsx +++ b/src/components/Notifications/NotificationsFeed.tsx @@ -10,12 +10,13 @@ import { Button } from 'decentraland-ui/dist/components/Button/Button' import { Loader } from 'decentraland-ui/dist/components/Loader/Loader' import { Governance } from '../../clients/Governance' +import { PUSH_CHANNEL_ID } from '../../constants' import { isSameAddress } from '../../entities/Snapshot/utils' import { DEFAULT_QUERY_STALE_TIME } from '../../hooks/constants' import { useClickOutside } from '../../hooks/useClickOutside' import useFormatMessage from '../../hooks/useFormatMessage' import { Notification } from '../../shared/types/notifications' -import { PUSH_CHANNEL_ID, getCaipAddress, getPushNotificationsEnv } from '../../utils/notifications' +import { getCaipAddress, getPushNotificationsEnv } from '../../utils/notifications' import FullWidthButton from '../Common/FullWidthButton' import Heading from '../Common/Typography/Heading' import Text from '../Common/Typography/Text' diff --git a/src/constants.ts b/src/constants.ts index 2367368b4..4550d4e61 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -44,3 +44,4 @@ export const DEBUG_ADDRESSES = (process.env.DEBUG_ADDRESSES || '') export const SNAPSHOT_STATUS_ENABLED = false export const DEFAULT_CHAIN_ID = process.env.GATSBY_DEFAULT_CHAIN_ID || clientEnv('GATSBY_DEFAULT_CHAIN_ID', String(ChainId.ETHEREUM_MAINNET)) +export const PUSH_CHANNEL_ID = process.env.GATSBY_PUSH_CHANNEL_ID || clientEnv('GATSBY_PUSH_CHANNEL_ID') diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 67e7e6667..3ac5c8d9b 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -2,8 +2,6 @@ import { ChainId } from '@dcl/schemas/dist/dapps/chain-id' import { ENV } from '../shared/types/notifications' -import { clientEnv } from './clientEnv' - export const NotificationType = { BROADCAST: 1, TARGET: 3, @@ -14,8 +12,6 @@ export function getCaipAddress(address: string, chainId: number) { return `eip155:${chainId}:${address}` } -export const PUSH_CHANNEL_ID = process.env.GATSBY_PUSH_CHANNEL_ID || clientEnv('GATSBY_PUSH_CHANNEL_ID') - export function getPushNotificationsEnv(chainId: ChainId) { switch (chainId) { case ChainId.ETHEREUM_MAINNET: From 8c076bfe712675387a86459743ced02865ed2abe Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Thu, 5 Oct 2023 14:29:00 -0300 Subject: [PATCH 34/42] feat: add notification dot ui --- .../Notifications/Notifications.css | 21 ++++++++++++++++++ .../Notifications/Notifications.tsx | 22 +++++++++++++------ 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/components/Notifications/Notifications.css b/src/components/Notifications/Notifications.css index f21eeee68..5916a0e42 100644 --- a/src/components/Notifications/Notifications.css +++ b/src/components/Notifications/Notifications.css @@ -6,8 +6,29 @@ margin: 0; transition: filter 0.2s ease; cursor: pointer; + position: relative; } .Notifications__Button--active { filter: drop-shadow(0 1px 8px var(--alpha-black-300)); } + +.Notifications__DotOuterCircle { + position: absolute; + top: -4px; + right: -4px; + width: 16px; + height: 16px; + background-color: var(--white-900); + border-radius: 25px; + display: flex; + align-items: center; + justify-content: center; +} + +.Notifications__DotInnerCircle { + width: 8px; + height: 8px; + border-radius: 25px; + background-color: var(--dcl-primary); +} diff --git a/src/components/Notifications/Notifications.tsx b/src/components/Notifications/Notifications.tsx index 89af85e2c..a84057972 100644 --- a/src/components/Notifications/Notifications.tsx +++ b/src/components/Notifications/Notifications.tsx @@ -12,16 +12,24 @@ import NotificationsFeed from './NotificationsFeed' export default function Notifications() { const t = useFormatMessage() const [isOpen, setOpen] = useState(false) + const hasNewNotifications = false // TODO: Integrate this return ( <> - +
+ +
setOpen(false)} /> ) From d452436193e2901211c1578446297ab4d3e1b7c8 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Fri, 6 Oct 2023 16:22:56 -0300 Subject: [PATCH 35/42] feat: add new notification styles in feed --- .../Notifications/NotificationItem.css | 6 +- .../Notifications/NotificationItem.tsx | 10 ++- .../Notifications/Notifications.tsx | 64 +++++++++++++++- .../Notifications/NotificationsFeed.css | 2 - .../Notifications/NotificationsFeed.tsx | 76 ++++++++----------- src/theme.css | 2 +- 6 files changed, 106 insertions(+), 54 deletions(-) diff --git a/src/components/Notifications/NotificationItem.css b/src/components/Notifications/NotificationItem.css index e449ed465..de69dfd4f 100644 --- a/src/components/Notifications/NotificationItem.css +++ b/src/components/Notifications/NotificationItem.css @@ -2,7 +2,11 @@ display: flex; flex-direction: row; gap: 12px; - padding: 0 16px; + padding: 8px 16px; +} + +.NotificationItem--new { + background-color: var(--alpha-blue-200); } .NotificationItem__IconContainer { diff --git a/src/components/Notifications/NotificationItem.tsx b/src/components/Notifications/NotificationItem.tsx index ba91341f5..e58ff458a 100644 --- a/src/components/Notifications/NotificationItem.tsx +++ b/src/components/Notifications/NotificationItem.tsx @@ -1,3 +1,5 @@ +import classNames from 'classnames' + import { Notification, NotificationCustomType } from '../../shared/types/notifications' import Time from '../../utils/date/Time' import Link from '../Common/Typography/Link' @@ -11,6 +13,7 @@ import './NotificationItem.css' interface Props { notification: Notification + isNew: boolean } function getIcon(metadata: { customType: string }) { @@ -25,14 +28,17 @@ function getIcon(metadata: { customType: string }) { } } -export default function NotificationItem({ notification }: Props) { +export default function NotificationItem({ notification, isNew }: Props) { const hasLink = !!notification.payload.data.acta const Component = hasLink ? Link : 'div' const metadata = notification.payload.data.additionalMeta?.data || '' const Icon = getIcon(metadata ? JSON.parse(metadata) : null) return ( - +
diff --git a/src/components/Notifications/Notifications.tsx b/src/components/Notifications/Notifications.tsx index a84057972..ddc1fdd98 100644 --- a/src/components/Notifications/Notifications.tsx +++ b/src/components/Notifications/Notifications.tsx @@ -1,8 +1,17 @@ -import { useState } from 'react' +import { useMemo, useState } from 'react' +import { ChainId } from '@dcl/schemas/dist/dapps/chain-id' +import * as PushAPI from '@pushprotocol/restapi' +import { useQuery } from '@tanstack/react-query' import classNames from 'classnames' +import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext' +import { Governance } from '../../clients/Governance' +import { PUSH_CHANNEL_ID } from '../../constants' +import { isSameAddress } from '../../entities/Snapshot/utils' +import { DEFAULT_QUERY_STALE_TIME } from '../../hooks/constants' import useFormatMessage from '../../hooks/useFormatMessage' +import { getCaipAddress, getPushNotificationsEnv } from '../../utils/notifications' import NotificationBellActive from '../Icon/NotificationBellActive' import NotificationBellInactive from '../Icon/NotificationBellInactive' @@ -11,8 +20,48 @@ import NotificationsFeed from './NotificationsFeed' export default function Notifications() { const t = useFormatMessage() + const [user, userState] = useAuthContext() const [isOpen, setOpen] = useState(false) + const chainId = userState.chainId || ChainId.ETHEREUM_GOERLI const hasNewNotifications = false // TODO: Integrate this + const lastNotificationId = 8403736 // TODO: Integrate this + const env = getPushNotificationsEnv(chainId) + + const { + data: subscriptions, + refetch: refetchSubscriptions, + isLoading: isLoadingSubscriptions, + isRefetching: isRefetchingSubscriptions, + } = useQuery({ + queryKey: [`pushSubscriptions#${user}`], + queryFn: () => + user + ? PushAPI.user.getSubscriptions({ + user: getCaipAddress(user, chainId), + env, + }) + : null, + enabled: !!user, + staleTime: DEFAULT_QUERY_STALE_TIME, + }) + + const isSubscribed = useMemo( + () => !!subscriptions?.find((item: { channel: string }) => isSameAddress(item.channel, PUSH_CHANNEL_ID)), + [subscriptions] + ) + + const { + data: userNotifications, + isLoading: isLoadingNotifications, + isRefetching: isRefetchingNotifications, + } = useQuery({ + queryKey: [`notifications#${user}`], + queryFn: () => (user ? Governance.get().getUserNotifications(user) : null), + enabled: !!user && isSubscribed, + }) + + console.log('u', userNotifications) + console.log('s', subscriptions, PUSH_CHANNEL_ID) return ( <> @@ -30,7 +79,18 @@ export default function Notifications() { )}
- setOpen(false)} /> + setOpen(false)} + userNotifications={userNotifications} + onSubscriptionChangeSuccess={refetchSubscriptions} + lastNotificationId={lastNotificationId} + isSubscribed={isSubscribed} + isLoadingNotifications={isLoadingNotifications} + isRefetchingNotifications={isRefetchingNotifications} + isLoadingSubscriptions={isLoadingSubscriptions} + isRefetchingSubscriptions={isRefetchingSubscriptions} + /> ) } diff --git a/src/components/Notifications/NotificationsFeed.css b/src/components/Notifications/NotificationsFeed.css index 3330b0a23..521ae4693 100644 --- a/src/components/Notifications/NotificationsFeed.css +++ b/src/components/Notifications/NotificationsFeed.css @@ -72,8 +72,6 @@ .NotificationsFeed__List { display: flex; flex-direction: column; - gap: 16px; - padding-bottom: 16px; } .NotificationsFeed__Content { diff --git a/src/components/Notifications/NotificationsFeed.tsx b/src/components/Notifications/NotificationsFeed.tsx index e621c7ebc..64e1a2c98 100644 --- a/src/components/Notifications/NotificationsFeed.tsx +++ b/src/components/Notifications/NotificationsFeed.tsx @@ -1,18 +1,14 @@ -import { useEffect, useMemo, useState } from 'react' +import { useEffect, useState } from 'react' import { ChainId } from '@dcl/schemas/dist/dapps/chain-id' import { Web3Provider } from '@ethersproject/providers' import * as PushAPI from '@pushprotocol/restapi' -import { useQuery } from '@tanstack/react-query' import classNames from 'classnames' import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext' import { Button } from 'decentraland-ui/dist/components/Button/Button' import { Loader } from 'decentraland-ui/dist/components/Loader/Loader' -import { Governance } from '../../clients/Governance' import { PUSH_CHANNEL_ID } from '../../constants' -import { isSameAddress } from '../../entities/Snapshot/utils' -import { DEFAULT_QUERY_STALE_TIME } from '../../hooks/constants' import { useClickOutside } from '../../hooks/useClickOutside' import useFormatMessage from '../../hooks/useFormatMessage' import { Notification } from '../../shared/types/notifications' @@ -32,52 +28,38 @@ const NOTIFICATIONS_PER_PAGE = 5 interface Props { isOpen: boolean onClose: () => void + onSubscriptionChangeSuccess: () => void + userNotifications: Notification[] | null | undefined + isSubscribed: boolean + isLoadingNotifications: boolean + isRefetchingNotifications: boolean + isLoadingSubscriptions: boolean + isRefetchingSubscriptions: boolean + lastNotificationId: number } -export default function NotificationsFeed({ isOpen, onClose }: Props) { +export default function NotificationsFeed({ + isOpen, + onClose, + onSubscriptionChangeSuccess, + userNotifications, + isSubscribed, + isLoadingNotifications, + isRefetchingNotifications, + isLoadingSubscriptions, + isRefetchingSubscriptions, + lastNotificationId, +}: Props) { const t = useFormatMessage() const [isSubscribing, setIsSubscribing] = useState(false) const [user, userState] = useAuthContext() const [filteredNotifications, setFilteredNotifications] = useState([]) + const lastNotificationIdIndex = userNotifications?.findIndex((item) => item.payload_id === lastNotificationId) useClickOutside('.NotificationsFeed', isOpen, onClose) const chainId = userState.chainId || ChainId.ETHEREUM_GOERLI const env = getPushNotificationsEnv(chainId) - const { - data: subscriptions, - refetch: refetchSubscriptions, - isLoading: isLoadingSubscriptions, - isRefetching: isRefetchingSubscriptions, - } = useQuery({ - queryKey: [`pushSubscriptions#${user}`], - queryFn: () => - user - ? PushAPI.user.getSubscriptions({ - user: getCaipAddress(user, chainId), - env, - }) - : null, - enabled: !!user, - staleTime: DEFAULT_QUERY_STALE_TIME, - }) - - const isSubscribed = useMemo( - () => !!subscriptions?.find((item: { channel: string }) => isSameAddress(item.channel, PUSH_CHANNEL_ID)), - [subscriptions] - ) - - const { - data: userNotifications, - isLoading: isLoadingNotifications, - isRefetching: isRefetchingNotifications, - } = useQuery({ - queryKey: [`notifications#${user}`], - queryFn: () => (user ? Governance.get().getUserNotifications(user) : null), - enabled: !!user && isSubscribed, - staleTime: DEFAULT_QUERY_STALE_TIME, - }) - const handleSubscribeUserToChannel = async () => { if (!user || !userState.provider) { return @@ -90,9 +72,7 @@ export default function NotificationsFeed({ isOpen, onClose }: Props) { signer, channelAddress: getCaipAddress(PUSH_CHANNEL_ID, chainId), userAddress: getCaipAddress(user, chainId), - onSuccess: () => { - refetchSubscriptions() - }, + onSuccess: onSubscriptionChangeSuccess, env, }) @@ -110,7 +90,7 @@ export default function NotificationsFeed({ isOpen, onClose }: Props) { signer, channelAddress: getCaipAddress(PUSH_CHANNEL_ID, chainId), userAddress: getCaipAddress(user, chainId), - onSuccess: () => refetchSubscriptions(), + onSuccess: onSubscriptionChangeSuccess, env, }) } @@ -178,8 +158,12 @@ export default function NotificationsFeed({ isOpen, onClose }: Props) { {showNotifications && (
- {filteredNotifications?.map((notification) => ( - + {filteredNotifications?.map((notification, index) => ( + ))}
{showLoadMoreButton && ( diff --git a/src/theme.css b/src/theme.css index 0cb771688..8c4b152ee 100644 --- a/src/theme.css +++ b/src/theme.css @@ -35,7 +35,7 @@ --alpha-black-200: rgba(115, 110, 125, 0.1); --alpha-black-300: rgba(115, 110, 125, 0.24); --alpha-black-400: rgba(115, 110, 125, 0.32); - --alpha-blue-200: rgba(72, 136, 238, 0.16); + --alpha-blue-200: rgba(226, 236, 252, 0.34); --alpha-fuchsia-200: rgba(255, 90, 254, 0.16); --alpha-fuchsia-500: rgba(255, 90, 254, 0.5); --alpha-green-200: rgba(68, 182, 0, 0.16); From fa30bfbdbc4beb81cddb73ddd1ba1992ab91de6f Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Fri, 6 Oct 2023 16:47:40 -0300 Subject: [PATCH 36/42] remove console.logs --- src/components/Notifications/Notifications.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/Notifications/Notifications.tsx b/src/components/Notifications/Notifications.tsx index ddc1fdd98..89285849e 100644 --- a/src/components/Notifications/Notifications.tsx +++ b/src/components/Notifications/Notifications.tsx @@ -60,9 +60,6 @@ export default function Notifications() { enabled: !!user && isSubscribed, }) - console.log('u', userNotifications) - console.log('s', subscriptions, PUSH_CHANNEL_ID) - return ( <>
From 9bdf65e183fdd12e5d3301829ed567333e1ed383 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Fri, 6 Oct 2023 19:16:16 -0300 Subject: [PATCH 37/42] feat: integrate mark as read --- src/back/models/UserNotificationConfig.ts | 12 +++++ src/back/routes/notification.ts | 24 ++++++++++ src/clients/Governance.ts | 18 ++++++++ .../Notifications/Notifications.tsx | 45 +++++++++++++++++-- .../Notifications/NotificationsFeed.tsx | 24 ++++------ ...28726163_user-notification-config-table.ts | 21 +++++++++ 6 files changed, 124 insertions(+), 20 deletions(-) create mode 100644 src/back/models/UserNotificationConfig.ts create mode 100644 src/migrations/1696628726163_user-notification-config-table.ts diff --git a/src/back/models/UserNotificationConfig.ts b/src/back/models/UserNotificationConfig.ts new file mode 100644 index 000000000..d699e3934 --- /dev/null +++ b/src/back/models/UserNotificationConfig.ts @@ -0,0 +1,12 @@ +import { Model } from 'decentraland-gatsby/dist/entities/Database/model' + +export type UserNotificationConfigAttributes = { + address: string + last_notification_id: number +} + +export default class UserNotificationConfigModel extends Model { + static tableName = 'user_notification_config' + static withTimestamps = false + static primaryKey = 'address' +} diff --git a/src/back/routes/notification.ts b/src/back/routes/notification.ts index a8e0d2c65..4501b0aa1 100644 --- a/src/back/routes/notification.ts +++ b/src/back/routes/notification.ts @@ -6,6 +6,7 @@ import { Request } from 'express' import { NotificationCustomType } from '../../shared/types/notifications' import { NotificationType } from '../../utils/notifications' +import UserNotificationConfigModel, { UserNotificationConfigAttributes } from '../models/UserNotificationConfig' import { NotificationService } from '../services/notification' import { validateDebugAddress } from '../utils/validations' @@ -13,6 +14,8 @@ export default routes((router) => { const withAuth = auth() router.post('/notifications/send', withAuth, handleAPI(sendNotification)) router.get('/notifications/user/:address', handleAPI(getUserFeed)) + router.get('/notifications/last-notification', withAuth, handleAPI(getUserLastNotification)) + router.post('/notifications/last-notification', withAuth, handleAPI(updateUserLastNotification)) }) async function sendNotification(req: WithAuth) { @@ -45,3 +48,24 @@ async function getUserFeed(req: Request) { return await NotificationService.getUserFeed(address) } + +async function getUserLastNotification(req: WithAuth) { + const config = await UserNotificationConfigModel.findOne({ address: req.auth }) + if (!config) { + throw new RequestError('User notification not found', RequestError.NotFound) + } + + return config.last_notification_id +} + +async function updateUserLastNotification(req: WithAuth) { + const last_notification_id = req.body.last_notification_id + if (!last_notification_id) { + throw new RequestError('Missing Notification ID', RequestError.BadRequest) + } + + return await UserNotificationConfigModel.upsert({ + address: req.auth, + last_notification_id: req.body.last_notification_id, + }) +} diff --git a/src/clients/Governance.ts b/src/clients/Governance.ts index cbb5aab72..22ae4608b 100644 --- a/src/clients/Governance.ts +++ b/src/clients/Governance.ts @@ -689,4 +689,22 @@ export class Governance extends API { ) return response.data } + + async getUserLastNotification() { + const response = await this.fetch>( + `/notifications/last-notification`, + this.options().method('GET').authorization({ sign: true }) + ) + return response.data + } + + async updateUserLastNotification(last_notification_id: number) { + const response = await this.fetch>( + `/notifications/last-notification`, + this.options().method('POST').authorization({ sign: true }).json({ + last_notification_id, + }) + ) + return response.data + } } diff --git a/src/components/Notifications/Notifications.tsx b/src/components/Notifications/Notifications.tsx index 89285849e..8f98675ee 100644 --- a/src/components/Notifications/Notifications.tsx +++ b/src/components/Notifications/Notifications.tsx @@ -2,7 +2,7 @@ import { useMemo, useState } from 'react' import { ChainId } from '@dcl/schemas/dist/dapps/chain-id' import * as PushAPI from '@pushprotocol/restapi' -import { useQuery } from '@tanstack/react-query' +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import classNames from 'classnames' import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext' @@ -23,8 +23,25 @@ export default function Notifications() { const [user, userState] = useAuthContext() const [isOpen, setOpen] = useState(false) const chainId = userState.chainId || ChainId.ETHEREUM_GOERLI - const hasNewNotifications = false // TODO: Integrate this - const lastNotificationId = 8403736 // TODO: Integrate this + const queryClient = useQueryClient() + const lastNotificationQueryKey = `lastNotificationId#${user}` + const mutation = useMutation( + (newLastNotificationId: number) => { + return Governance.get().updateUserLastNotification(newLastNotificationId) + }, + { + onMutate: async (newLastNotificationId: number) => { + await queryClient.cancelQueries([lastNotificationQueryKey]) + const previousTodos = queryClient.getQueryData([lastNotificationQueryKey]) + queryClient.setQueryData([lastNotificationQueryKey], () => newLastNotificationId) + return { previousTodos } + }, + onError: (_error, _newLastNotificationId, context) => { + queryClient.setQueryData([lastNotificationQueryKey], context?.previousTodos) + }, + } + ) + const env = getPushNotificationsEnv(chainId) const { @@ -60,6 +77,26 @@ export default function Notifications() { enabled: !!user && isSubscribed, }) + const { data: lastNotificationId } = useQuery({ + queryKey: [lastNotificationQueryKey], + queryFn: () => (user ? Governance.get().getUserLastNotification() : null), + enabled: !!user && isSubscribed, + }) + + const latestNotification = userNotifications?.[0].payload_id + const hasNewNotifications = + Number(userNotifications?.length) > 0 && + !!lastNotificationId && + latestNotification && + latestNotification !== Number(lastNotificationId) + + const handleFeedClose = () => { + setOpen(false) + if (hasNewNotifications) { + mutation.mutate(userNotifications?.[0].payload_id) + } + } + return ( <>
@@ -78,7 +115,7 @@ export default function Notifications() {
setOpen(false)} + onClose={handleFeedClose} userNotifications={userNotifications} onSubscriptionChangeSuccess={refetchSubscriptions} lastNotificationId={lastNotificationId} diff --git a/src/components/Notifications/NotificationsFeed.tsx b/src/components/Notifications/NotificationsFeed.tsx index 64e1a2c98..3f1b6b4cd 100644 --- a/src/components/Notifications/NotificationsFeed.tsx +++ b/src/components/Notifications/NotificationsFeed.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import { ChainId } from '@dcl/schemas/dist/dapps/chain-id' import { Web3Provider } from '@ethersproject/providers' @@ -35,7 +35,7 @@ interface Props { isRefetchingNotifications: boolean isLoadingSubscriptions: boolean isRefetchingSubscriptions: boolean - lastNotificationId: number + lastNotificationId: number | null | undefined } export default function NotificationsFeed({ @@ -53,7 +53,7 @@ export default function NotificationsFeed({ const t = useFormatMessage() const [isSubscribing, setIsSubscribing] = useState(false) const [user, userState] = useAuthContext() - const [filteredNotifications, setFilteredNotifications] = useState([]) + const [notificationsPerPage, setNotificationsPerPage] = useState(NOTIFICATIONS_PER_PAGE) const lastNotificationIdIndex = userNotifications?.findIndex((item) => item.payload_id === lastNotificationId) useClickOutside('.NotificationsFeed', isOpen, onClose) @@ -96,23 +96,15 @@ export default function NotificationsFeed({ } const handleLoadMoreClick = () => { - const notifications = userNotifications?.slice( - 0, - filteredNotifications.length + NOTIFICATIONS_PER_PAGE - ) as unknown as Notification[] - setFilteredNotifications(notifications) - } - - useEffect(() => { - if (filteredNotifications.length === 0 && userNotifications && userNotifications?.length > 0) { - const notifications = userNotifications.slice(0, NOTIFICATIONS_PER_PAGE) as unknown as Notification[] - setFilteredNotifications(notifications) + if (filteredNotifications) { + setNotificationsPerPage(filteredNotifications.length + NOTIFICATIONS_PER_PAGE) } - }, [userNotifications, filteredNotifications.length]) + } + const filteredNotifications = userNotifications?.slice(0, notificationsPerPage) const hasNotifications = filteredNotifications && filteredNotifications.length > 0 const showNotifications = isSubscribed && !isLoadingNotifications && hasNotifications - const showLoadMoreButton = filteredNotifications.length !== userNotifications?.length + const showLoadMoreButton = filteredNotifications?.length !== userNotifications?.length const unsubscribedKey = isSubscribing ? 'subscribing' : 'unsubscribed' const UnsubscribedIcon = isSubscribing ? SignGray : NotificationBellInactive const isLoading = diff --git a/src/migrations/1696628726163_user-notification-config-table.ts b/src/migrations/1696628726163_user-notification-config-table.ts new file mode 100644 index 000000000..db11544c8 --- /dev/null +++ b/src/migrations/1696628726163_user-notification-config-table.ts @@ -0,0 +1,21 @@ +import { MigrationBuilder } from 'node-pg-migrate' + +import Model from '../back/models/UserNotificationConfig' + +export async function up(pgm: MigrationBuilder): Promise { + pgm.createTable(Model.tableName, { + address: { + primaryKey: true, + type: 'TEXT', + notNull: true, + }, + last_notification_id: { + type: 'INTEGER', + notNull: true, + }, + }) +} + +export async function down(pgm: MigrationBuilder): Promise { + pgm.dropTable(Model.tableName) +} From f22578a35926284948a632e8f5d39ad17c80b5c0 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Tue, 10 Oct 2023 12:50:46 -0300 Subject: [PATCH 38/42] fix: save first last notification id --- src/back/routes/notification.ts | 2 +- src/components/Notifications/Notifications.tsx | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/back/routes/notification.ts b/src/back/routes/notification.ts index 4501b0aa1..a43e49624 100644 --- a/src/back/routes/notification.ts +++ b/src/back/routes/notification.ts @@ -66,6 +66,6 @@ async function updateUserLastNotification(req: WithAuth) { return await UserNotificationConfigModel.upsert({ address: req.auth, - last_notification_id: req.body.last_notification_id, + last_notification_id, }) } diff --git a/src/components/Notifications/Notifications.tsx b/src/components/Notifications/Notifications.tsx index 8f98675ee..ebaaad480 100644 --- a/src/components/Notifications/Notifications.tsx +++ b/src/components/Notifications/Notifications.tsx @@ -81,6 +81,7 @@ export default function Notifications() { queryKey: [lastNotificationQueryKey], queryFn: () => (user ? Governance.get().getUserLastNotification() : null), enabled: !!user && isSubscribed, + retry: 3, }) const latestNotification = userNotifications?.[0].payload_id @@ -92,8 +93,8 @@ export default function Notifications() { const handleFeedClose = () => { setOpen(false) - if (hasNewNotifications) { - mutation.mutate(userNotifications?.[0].payload_id) + if ((latestNotification && !lastNotificationId) || hasNewNotifications) { + mutation.mutate(latestNotification) } } From 0345f79393c6e485550638c96797ddfe1426eb6d Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Tue, 10 Oct 2023 13:10:45 -0300 Subject: [PATCH 39/42] feat: show dot on first view --- src/components/Notifications/Notifications.tsx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/components/Notifications/Notifications.tsx b/src/components/Notifications/Notifications.tsx index ebaaad480..51ef25586 100644 --- a/src/components/Notifications/Notifications.tsx +++ b/src/components/Notifications/Notifications.tsx @@ -18,6 +18,9 @@ import NotificationBellInactive from '../Icon/NotificationBellInactive' import './Notifications.css' import NotificationsFeed from './NotificationsFeed' +const NOTIFICATIONS_NEW_FEATURE_DISMISSED_LOCAL_STORAGE_KEY = + 'org.decentraland.governance.notifications-new-feature-dismissed' + export default function Notifications() { const t = useFormatMessage() const [user, userState] = useAuthContext() @@ -25,6 +28,11 @@ export default function Notifications() { const chainId = userState.chainId || ChainId.ETHEREUM_GOERLI const queryClient = useQueryClient() const lastNotificationQueryKey = `lastNotificationId#${user}` + const [isNewFeatureDismissed, setIsNewFeatureDismissed] = useState( + typeof window !== undefined + ? localStorage.getItem(NOTIFICATIONS_NEW_FEATURE_DISMISSED_LOCAL_STORAGE_KEY) === 'true' + : true + ) const mutation = useMutation( (newLastNotificationId: number) => { return Governance.get().updateUserLastNotification(newLastNotificationId) @@ -93,11 +101,19 @@ export default function Notifications() { const handleFeedClose = () => { setOpen(false) + + if (!isNewFeatureDismissed) { + localStorage.setItem(NOTIFICATIONS_NEW_FEATURE_DISMISSED_LOCAL_STORAGE_KEY, 'true') + setIsNewFeatureDismissed(true) + } + if ((latestNotification && !lastNotificationId) || hasNewNotifications) { mutation.mutate(latestNotification) } } + const showDot = (!isNewFeatureDismissed || hasNewNotifications) && !isOpen + return ( <>
@@ -107,7 +123,7 @@ export default function Notifications() { aria-label={t('navigation.notifications.button_label')} > {isOpen ? : } - {hasNewNotifications && !isOpen && ( + {showDot && (
From 349124b33641219d67e4e8cb60ef3c5aa713c88f Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Tue, 10 Oct 2023 15:12:37 -0300 Subject: [PATCH 40/42] feat: save last notification if there is none in db and has one --- .../Notifications/Notifications.tsx | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/components/Notifications/Notifications.tsx b/src/components/Notifications/Notifications.tsx index 51ef25586..79c663e7f 100644 --- a/src/components/Notifications/Notifications.tsx +++ b/src/components/Notifications/Notifications.tsx @@ -84,15 +84,31 @@ export default function Notifications() { queryFn: () => (user ? Governance.get().getUserNotifications(user) : null), enabled: !!user && isSubscribed, }) + const latestNotification = userNotifications?.[0].payload_id const { data: lastNotificationId } = useQuery({ queryKey: [lastNotificationQueryKey], - queryFn: () => (user ? Governance.get().getUserLastNotification() : null), + queryFn: async () => { + if (!user) { + return null + } + + try { + return await Governance.get().getUserLastNotification() + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + if (error.statusCode === 404 && latestNotification) { + mutation.mutate(latestNotification) + return latestNotification + } + + return null + } + }, enabled: !!user && isSubscribed, retry: 3, }) - const latestNotification = userNotifications?.[0].payload_id const hasNewNotifications = Number(userNotifications?.length) > 0 && !!lastNotificationId && From 0752976ec2429c31707699ca1bde9243dcb8eed2 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Tue, 10 Oct 2023 15:19:28 -0300 Subject: [PATCH 41/42] remove notifications page --- src/pages/notifications.tsx | 91 ------------------------------------- 1 file changed, 91 deletions(-) delete mode 100644 src/pages/notifications.tsx diff --git a/src/pages/notifications.tsx b/src/pages/notifications.tsx deleted file mode 100644 index c688dea68..000000000 --- a/src/pages/notifications.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import React, { useState } from 'react' - -import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext' -import { Button } from 'decentraland-ui/dist/components/Button/Button' -import { Container } from 'decentraland-ui/dist/components/Container/Container' -import { Field } from 'decentraland-ui/dist/components/Field/Field' -import { SelectField } from 'decentraland-ui/dist/components/SelectField/SelectField' - -import { Governance } from '../clients/Governance' -import Heading from '../components/Common/Typography/Heading' -import LogIn from '../components/Layout/LogIn' -import useIsDebugAddress from '../hooks/useIsDebugAddress' -import { NotificationType } from '../utils/notifications' - -import './debug.css' - -export default function DebugPage() { - const [user] = useAuthContext() - const { isDebugAddress } = useIsDebugAddress(user) - const [notificationType, setNotificationType] = useState(NotificationType.TARGET) - const [notificationAddress, setNotificationAddress] = useState('') - const [notificationTitle, setNotificationTitle] = useState('') - const [notificationBody, setNotificationBody] = useState('') - const [notificationURL, setNotificationURL] = useState('') - const [isSendingNotification, setIsSendingNotification] = useState(false) - - const handleSendNotification = async (e: any) => { - e.preventDefault() - setIsSendingNotification(true) - - try { - await Governance.get().sendNotification( - notificationAddress, - notificationTitle, - notificationBody, - notificationType, - notificationURL - ) - setNotificationAddress('') - setNotificationTitle('') - setNotificationBody('') - setNotificationURL('') - } catch (error) { - console.log('Error sending notification', error) - } - - setIsSendingNotification(false) - } - - if (!user || !isDebugAddress) { - return - } - - return ( - -
- Send announcement notification - setNotificationType(Number(value))} - options={[ - { text: 'Target', value: NotificationType.TARGET }, - { text: 'Broadcast', value: NotificationType.BROADCAST }, - ]} - /> - {notificationType === NotificationType.TARGET && ( - setNotificationAddress(e.target.value)} - /> - )} - setNotificationTitle(e.target.value)} - /> - setNotificationBody(e.target.value)} - /> - setNotificationURL(e.target.value)} /> - - -
- ) -} From b6dd1b462b1fe9e5b39423b4d439acf34bdddee6 Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Tue, 17 Oct 2023 11:47:54 -0300 Subject: [PATCH 42/42] fix: build error --- gatsby-node.js | 1 + 1 file changed, 1 insertion(+) diff --git a/gatsby-node.js b/gatsby-node.js index e19f93c6f..d0d779771 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -20,6 +20,7 @@ exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => { stream: false, util: false, zlib: false, + url: false, }, }, })