From 78021a128b184fd5a07d18e52fdc9c732ff2f090 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Mon, 31 Mar 2025 20:46:04 +0100 Subject: [PATCH 01/37] init setup --- .env.example | 17 - .gitignore | 1 + yarn.lock | 876 ++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 723 insertions(+), 171 deletions(-) delete mode 100644 .env.example diff --git a/.env.example b/.env.example deleted file mode 100644 index 54b558a8..00000000 --- a/.env.example +++ /dev/null @@ -1,17 +0,0 @@ -CHANGEFEED_ACCESS_TOKEN= -GITHUB_ACCESS_TOKEN= -LIVEPEER_COM_API_ADMIN_TOKEN= -API_TOKEN= -PINATA_JWT= - -NEXT_PUBLIC_ETHERSCAN_API_KEY= -NEXT_PUBLIC_GA_TRACKING_ID= -NEXT_PUBLIC_NETWORK=ARBITRUM_ONE -NEXT_PUBLIC_INFURA_KEY= -NEXT_PUBLIC_L1_RPC_URL= -NEXT_PUBLIC_L2_RPC_URL= -NEXT_PUBLIC_GITHUB_LIP_NAMESPACE=adamsoffer -NEXT_PUBLIC_SUBGRAPH_API_KEY= -NEXT_PUBLIC_SUBGRAPH_ID=FE63YgkzcpVocxdCEyEYbvjYqEf2kb1A6daMYRxmejYC -NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID= -NEXT_PUBLIC_METRICS_SERVER_URL=https://livepeer-leaderboard-serverless.vercel.app diff --git a/.gitignore b/.gitignore index 27222722..cf33238d 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,4 @@ yarn-error.log* .env.development.local .env.test.local .env.production.local +.qodo diff --git a/yarn.lock b/yarn.lock index 5d36753b..d8f3d5cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,7 +15,26 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@apollo/client@^3.5.8", "@apollo/client@~3.2.5 || ~3.3.0 || ~3.4.0 || ~3.5.0 || ~3.6.0": +"@apollo/client@^3.13.1": + version "3.13.5" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.13.5.tgz#588ac60c751c38d50ce0286fcefcc4994aa9c5d6" + integrity sha512-ceHa1lApLAiGmUur4V+G/CrjwVwHYujfB7U5HM++poCgHpfGn6eet8YGM93fgeWjYX85SaqwdZbQk18IVwhRHg== + dependencies: + "@graphql-typed-document-node/core" "^3.1.1" + "@wry/caches" "^1.0.0" + "@wry/equality" "^0.5.6" + "@wry/trie" "^0.5.0" + graphql-tag "^2.12.6" + hoist-non-react-statics "^3.3.2" + optimism "^0.18.0" + prop-types "^15.7.2" + rehackt "^0.1.0" + symbol-observable "^4.0.0" + ts-invariant "^0.10.3" + tslib "^2.3.0" + zen-observable-ts "^1.2.5" + +"@apollo/client@~3.2.5 || ~3.3.0 || ~3.4.0 || ~3.5.0 || ~3.6.0": version "3.6.9" resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.6.9.tgz#ad0ee2e3a3c92dbed4acd6917b6158a492739d94" integrity sha512-Y1yu8qa2YeaCUBVuw08x8NHenFi0sw2I3KCu7Kw9mDSu86HmmtHJkCAifKVrN2iPgDTW/BbP3EpSV8/EQCcxZA== @@ -3396,6 +3415,13 @@ dependencies: "@types/estree" "*" +"@types/estree-jsx@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-1.0.5.tgz#858a88ea20f34fe65111f005a689fa1ebf70dc18" + integrity sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg== + dependencies: + "@types/estree" "*" + "@types/estree@*": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" @@ -3406,6 +3432,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== +"@types/estree@^1.0.0": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.7.tgz#4158d3105276773d5b7695cd4834b1722e4f37a8" + integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ== + "@types/hast@^2.0.0": version "2.3.4" resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc" @@ -3413,6 +3444,13 @@ dependencies: "@types/unist" "*" +"@types/hast@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" + integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== + dependencies: + "@types/unist" "*" + "@types/hoist-non-react-statics@^3.0.1": version "3.3.1" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" @@ -3465,6 +3503,13 @@ dependencies: "@types/unist" "*" +"@types/mdast@^4.0.0": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6" + integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== + dependencies: + "@types/unist" "*" + "@types/mdurl@^1.0.0": version "1.0.2" resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9" @@ -3510,7 +3555,7 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.3.tgz#68ada76827b0010d0db071f739314fa429943d0a" integrity sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg== -"@types/prop-types@*", "@types/prop-types@^15.0.0": +"@types/prop-types@*": version "15.7.5" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== @@ -3591,6 +3636,11 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== +"@types/unist@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" + integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== + "@types/use-sync-external-store@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" @@ -3654,6 +3704,11 @@ "@typescript-eslint/types" "5.30.7" eslint-visitor-keys "^3.3.0" +"@ungap/structured-clone@^1.0.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" + integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== + "@vanilla-extract/css@1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@vanilla-extract/css/-/css-1.9.1.tgz#337b79faa5f8f98915a90c3fe3c30b54be746c09" @@ -4123,6 +4178,13 @@ undici "5.5.1" web-streams-polyfill "^3.2.0" +"@wry/caches@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@wry/caches/-/caches-1.0.1.tgz#8641fd3b6e09230b86ce8b93558d44cf1ece7e52" + integrity sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA== + dependencies: + tslib "^2.3.0" + "@wry/context@^0.6.0": version "0.6.1" resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.6.1.tgz#c3c29c0ad622adb00f6a53303c4f965ee06ebeb2" @@ -4130,6 +4192,13 @@ dependencies: tslib "^2.3.0" +"@wry/context@^0.7.0": + version "0.7.4" + resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.4.tgz#e32d750fa075955c4ab2cfb8c48095e1d42d5990" + integrity sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ== + dependencies: + tslib "^2.3.0" + "@wry/equality@^0.1.2": version "0.1.11" resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.1.11.tgz#35cb156e4a96695aa81a9ecc4d03787bc17f1790" @@ -4144,6 +4213,13 @@ dependencies: tslib "^2.3.0" +"@wry/equality@^0.5.6": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.7.tgz#72ec1a73760943d439d56b7b1e9985aec5d497bb" + integrity sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw== + dependencies: + tslib "^2.3.0" + "@wry/trie@^0.3.0": version "0.3.1" resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.3.1.tgz#2279b790f15032f8bcea7fc944d27988e5b3b139" @@ -4151,6 +4227,13 @@ dependencies: tslib "^2.3.0" +"@wry/trie@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.5.0.tgz#11e783f3a53f6e4cd1d42d2d1323f5bc3fa99c94" + integrity sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA== + dependencies: + tslib "^2.3.0" + "@xobotyi/scrollbar-width@^1.9.5": version "1.9.5" resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" @@ -5654,6 +5737,13 @@ detect-node-es@^1.1.0: resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== +devlop@^1.0.0, devlop@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" + integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== + dependencies: + dequal "^2.0.0" + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -6192,6 +6282,11 @@ estree-util-is-identifier-name@^2.0.0: resolved "https://registry.yarnpkg.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-2.0.1.tgz#cf07867f42705892718d9d89eb2d85eaa8f0fcb5" integrity sha512-rxZj1GkQhY4x1j/CSnybK9cGuMFQYFPLq0iNyopqf14aOVLFtMv7Esika+ObJWPWiOHuMOAHz3YkWoLYYRnzWQ== +estree-util-is-identifier-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz#0b5ef4c4ff13508b34dcd01ecfa945f61fce5dbd" + integrity sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg== + estree-util-visit@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/estree-util-visit/-/estree-util-visit-1.1.0.tgz#c0ea7942c40ac7889a77b57a11e92f987744bc6f" @@ -6944,11 +7039,39 @@ hast-util-to-estree@^2.0.0: unist-util-position "^4.0.0" zwitch "^2.0.0" +hast-util-to-jsx-runtime@^2.0.0: + version "2.3.6" + resolved "https://registry.yarnpkg.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz#ff31897aae59f62232e21594eac7ef6b63333e98" + integrity sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg== + dependencies: + "@types/estree" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + comma-separated-tokens "^2.0.0" + devlop "^1.0.0" + estree-util-is-identifier-name "^3.0.0" + hast-util-whitespace "^3.0.0" + mdast-util-mdx-expression "^2.0.0" + mdast-util-mdx-jsx "^3.0.0" + mdast-util-mdxjs-esm "^2.0.0" + property-information "^7.0.0" + space-separated-tokens "^2.0.0" + style-to-js "^1.0.0" + unist-util-position "^5.0.0" + vfile-message "^4.0.0" + hast-util-whitespace@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-2.0.0.tgz#4fc1086467cc1ef5ba20673cb6b03cec3a970f1c" integrity sha512-Pkw+xBHuV6xFeJprJe2BBEoDV+AvQySaz3pPDRUs5PNZEMQjpXJJueqrpcHIXxnWTcAGi/UOCgVShlkY6kLoqg== +hast-util-whitespace@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" + integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== + dependencies: + "@types/hast" "^3.0.0" + header-case@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063" @@ -6983,6 +7106,11 @@ hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react- dependencies: react-is "^16.7.0" +html-url-attributes@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/html-url-attributes/-/html-url-attributes-3.0.1.tgz#83b052cd5e437071b756cd74ae70f708870c2d87" + integrity sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ== + htmlparser2@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" @@ -7107,6 +7235,11 @@ inline-style-parser@0.1.1: resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== +inline-style-parser@0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.4.tgz#f4af5fe72e612839fcd453d989a586566d695f22" + integrity sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== + inline-style-prefixer@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-6.0.1.tgz#c5c0e43ba8831707afc5f5bbfd97edf45c1fa7ae" @@ -8195,15 +8328,15 @@ mdast-util-definitions@^5.0.0: "@types/unist" "^2.0.0" unist-util-visit "^3.0.0" -mdast-util-find-and-replace@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.2.tgz#cc2b774f7f3630da4bd592f61966fecade8b99b1" - integrity sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw== +mdast-util-find-and-replace@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz#70a3174c894e14df722abf43bc250cbae44b11df" + integrity sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg== dependencies: - "@types/mdast" "^3.0.0" + "@types/mdast" "^4.0.0" escape-string-regexp "^5.0.0" - unist-util-is "^5.0.0" - unist-util-visit-parents "^5.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" mdast-util-from-markdown@^1.0.0: version "1.2.0" @@ -8223,63 +8356,88 @@ mdast-util-from-markdown@^1.0.0: unist-util-stringify-position "^3.0.0" uvu "^0.5.0" -mdast-util-gfm-autolink-literal@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-1.0.3.tgz#67a13abe813d7eba350453a5333ae1bc0ec05c06" - integrity sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA== +mdast-util-from-markdown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz#4850390ca7cf17413a9b9a0fbefcd1bc0eb4160a" + integrity sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA== dependencies: - "@types/mdast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + mdast-util-to-string "^4.0.0" + micromark "^4.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-decode-string "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + unist-util-stringify-position "^4.0.0" + +mdast-util-gfm-autolink-literal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz#abd557630337bd30a6d5a4bd8252e1c2dc0875d5" + integrity sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ== + dependencies: + "@types/mdast" "^4.0.0" ccount "^2.0.0" - mdast-util-find-and-replace "^2.0.0" - micromark-util-character "^1.0.0" + devlop "^1.0.0" + mdast-util-find-and-replace "^3.0.0" + micromark-util-character "^2.0.0" -mdast-util-gfm-footnote@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-1.0.2.tgz#ce5e49b639c44de68d5bf5399877a14d5020424e" - integrity sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ== +mdast-util-gfm-footnote@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz#7778e9d9ca3df7238cc2bd3fa2b1bf6a65b19403" + integrity sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ== dependencies: - "@types/mdast" "^3.0.0" - mdast-util-to-markdown "^1.3.0" - micromark-util-normalize-identifier "^1.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.1.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" -mdast-util-gfm-strikethrough@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-1.0.3.tgz#5470eb105b483f7746b8805b9b989342085795b7" - integrity sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ== +mdast-util-gfm-strikethrough@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz#d44ef9e8ed283ac8c1165ab0d0dfd058c2764c16" + integrity sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg== dependencies: - "@types/mdast" "^3.0.0" - mdast-util-to-markdown "^1.3.0" + "@types/mdast" "^4.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" -mdast-util-gfm-table@^1.0.0: - version "1.0.7" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.7.tgz#3552153a146379f0f9c4c1101b071d70bbed1a46" - integrity sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg== +mdast-util-gfm-table@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz#7a435fb6223a72b0862b33afbd712b6dae878d38" + integrity sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg== dependencies: - "@types/mdast" "^3.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" markdown-table "^3.0.0" - mdast-util-from-markdown "^1.0.0" - mdast-util-to-markdown "^1.3.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" -mdast-util-gfm-task-list-item@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-1.0.2.tgz#b280fcf3b7be6fd0cc012bbe67a59831eb34097b" - integrity sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ== +mdast-util-gfm-task-list-item@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz#e68095d2f8a4303ef24094ab642e1047b991a936" + integrity sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ== dependencies: - "@types/mdast" "^3.0.0" - mdast-util-to-markdown "^1.3.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" -mdast-util-gfm@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/mdast-util-gfm/-/mdast-util-gfm-2.0.2.tgz#e92f4d8717d74bdba6de57ed21cc8b9552e2d0b6" - integrity sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg== +mdast-util-gfm@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz#2cdf63b92c2a331406b0fb0db4c077c1b0331751" + integrity sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ== dependencies: - mdast-util-from-markdown "^1.0.0" - mdast-util-gfm-autolink-literal "^1.0.0" - mdast-util-gfm-footnote "^1.0.0" - mdast-util-gfm-strikethrough "^1.0.0" - mdast-util-gfm-table "^1.0.0" - mdast-util-gfm-task-list-item "^1.0.0" - mdast-util-to-markdown "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-gfm-autolink-literal "^2.0.0" + mdast-util-gfm-footnote "^2.0.0" + mdast-util-gfm-strikethrough "^2.0.0" + mdast-util-gfm-table "^2.0.0" + mdast-util-gfm-task-list-item "^2.0.0" + mdast-util-to-markdown "^2.0.0" mdast-util-mdx-expression@^1.0.0: version "1.2.1" @@ -8292,6 +8450,18 @@ mdast-util-mdx-expression@^1.0.0: mdast-util-from-markdown "^1.0.0" mdast-util-to-markdown "^1.0.0" +mdast-util-mdx-expression@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz#43f0abac9adc756e2086f63822a38c8d3c3a5096" + integrity sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + mdast-util-mdx-jsx@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-2.0.2.tgz#087448dc29f6df9b9d9951132f82c20bd378bb68" @@ -8308,6 +8478,24 @@ mdast-util-mdx-jsx@^2.0.0: unist-util-stringify-position "^3.0.0" vfile-message "^3.0.0" +mdast-util-mdx-jsx@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz#fd04c67a2a7499efb905a8a5c578dddc9fdada0d" + integrity sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + ccount "^2.0.0" + devlop "^1.1.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + parse-entities "^4.0.0" + stringify-entities "^4.0.0" + unist-util-stringify-position "^4.0.0" + vfile-message "^4.0.0" + mdast-util-mdx@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mdast-util-mdx/-/mdast-util-mdx-2.0.0.tgz#dd4f6c993cf27da32725e50a04874f595b7b63fb" @@ -8328,6 +8516,26 @@ mdast-util-mdxjs-esm@^1.0.0: mdast-util-from-markdown "^1.0.0" mdast-util-to-markdown "^1.0.0" +mdast-util-mdxjs-esm@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz#019cfbe757ad62dd557db35a695e7314bcc9fa97" + integrity sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-phrasing@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz#7cc0a8dec30eaf04b7b1a9661a92adb3382aa6e3" + integrity sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== + dependencies: + "@types/mdast" "^4.0.0" + unist-util-is "^6.0.0" + mdast-util-to-hast@^12.1.0: version "12.1.1" resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-12.1.1.tgz#89a2bb405eaf3b05eb8bf45157678f35eef5dbca" @@ -8344,6 +8552,21 @@ mdast-util-to-hast@^12.1.0: unist-util-position "^4.0.0" unist-util-visit "^4.0.0" +mdast-util-to-hast@^13.0.0: + version "13.2.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz#5ca58e5b921cc0a3ded1bc02eed79a4fe4fe41f4" + integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@ungap/structured-clone" "^1.0.0" + devlop "^1.0.0" + micromark-util-sanitize-uri "^2.0.0" + trim-lines "^3.0.0" + unist-util-position "^5.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + mdast-util-to-markdown@^1.0.0, mdast-util-to-markdown@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-1.3.0.tgz#38b6cdc8dc417de642a469c4fc2abdf8c931bd1e" @@ -8357,11 +8580,33 @@ mdast-util-to-markdown@^1.0.0, mdast-util-to-markdown@^1.3.0: unist-util-visit "^4.0.0" zwitch "^2.0.0" +mdast-util-to-markdown@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz#f910ffe60897f04bb4b7e7ee434486f76288361b" + integrity sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + longest-streak "^3.0.0" + mdast-util-phrasing "^4.0.0" + mdast-util-to-string "^4.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-decode-string "^2.0.0" + unist-util-visit "^5.0.0" + zwitch "^2.0.0" + mdast-util-to-string@^3.0.0, mdast-util-to-string@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz#56c506d065fbf769515235e577b5a261552d56e9" integrity sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA== +mdast-util-to-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" + integrity sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== + dependencies: + "@types/mdast" "^4.0.0" + mdn-data@2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" @@ -8426,84 +8671,106 @@ micromark-core-commonmark@^1.0.0, micromark-core-commonmark@^1.0.1: micromark-util-types "^1.0.1" uvu "^0.5.0" -micromark-extension-gfm-autolink-literal@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-1.0.5.tgz#5853f0e579bbd8ef9e39a7c0f0f27c5a063a66e7" - integrity sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg== +micromark-core-commonmark@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz#c691630e485021a68cf28dbc2b2ca27ebf678cd4" + integrity sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg== dependencies: - micromark-util-character "^1.0.0" - micromark-util-sanitize-uri "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - -micromark-extension-gfm-footnote@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.1.2.tgz#05e13034d68f95ca53c99679040bc88a6f92fe2e" - integrity sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q== + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + micromark-factory-destination "^2.0.0" + micromark-factory-label "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-factory-title "^2.0.0" + micromark-factory-whitespace "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-html-tag-name "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-subtokenize "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-autolink-literal@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz#6286aee9686c4462c1e3552a9d505feddceeb935" + integrity sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw== dependencies: - micromark-core-commonmark "^1.0.0" - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-normalize-identifier "^1.0.0" - micromark-util-sanitize-uri "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - uvu "^0.5.0" + micromark-util-character "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" -micromark-extension-gfm-strikethrough@^1.0.0: - version "1.0.7" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-1.0.7.tgz#c8212c9a616fa3bf47cb5c711da77f4fdc2f80af" - integrity sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw== +micromark-extension-gfm-footnote@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz#4dab56d4e398b9853f6fe4efac4fc9361f3e0750" + integrity sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw== + dependencies: + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-strikethrough@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz#86106df8b3a692b5f6a92280d3879be6be46d923" + integrity sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw== dependencies: - micromark-util-chunked "^1.0.0" - micromark-util-classify-character "^1.0.0" - micromark-util-resolve-all "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - uvu "^0.5.0" + devlop "^1.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" -micromark-extension-gfm-table@^1.0.0: - version "1.0.7" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.7.tgz#dcb46074b0c6254c3fc9cc1f6f5002c162968008" - integrity sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw== +micromark-extension-gfm-table@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz#fac70bcbf51fe65f5f44033118d39be8a9b5940b" + integrity sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg== dependencies: - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - uvu "^0.5.0" + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" -micromark-extension-gfm-tagfilter@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-1.0.2.tgz#aa7c4dd92dabbcb80f313ebaaa8eb3dac05f13a7" - integrity sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g== +micromark-extension-gfm-tagfilter@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz#f26d8a7807b5985fba13cf61465b58ca5ff7dc57" + integrity sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg== dependencies: - micromark-util-types "^1.0.0" + micromark-util-types "^2.0.0" -micromark-extension-gfm-task-list-item@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.5.tgz#b52ce498dc4c69b6a9975abafc18f275b9dde9f4" - integrity sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ== +micromark-extension-gfm-task-list-item@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz#bcc34d805639829990ec175c3eea12bb5b781f2c" + integrity sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw== dependencies: - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - uvu "^0.5.0" + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" -micromark-extension-gfm@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm/-/micromark-extension-gfm-2.0.3.tgz#e517e8579949a5024a493e49204e884aa74f5acf" - integrity sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ== - dependencies: - micromark-extension-gfm-autolink-literal "^1.0.0" - micromark-extension-gfm-footnote "^1.0.0" - micromark-extension-gfm-strikethrough "^1.0.0" - micromark-extension-gfm-table "^1.0.0" - micromark-extension-gfm-tagfilter "^1.0.0" - micromark-extension-gfm-task-list-item "^1.0.0" - micromark-util-combine-extensions "^1.0.0" - micromark-util-types "^1.0.0" +micromark-extension-gfm@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz#3e13376ab95dd7a5cfd0e29560dfe999657b3c5b" + integrity sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w== + dependencies: + micromark-extension-gfm-autolink-literal "^2.0.0" + micromark-extension-gfm-footnote "^2.0.0" + micromark-extension-gfm-strikethrough "^2.0.0" + micromark-extension-gfm-table "^2.0.0" + micromark-extension-gfm-tagfilter "^2.0.0" + micromark-extension-gfm-task-list-item "^2.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-types "^2.0.0" micromark-extension-mdx-expression@^1.0.0: version "1.0.3" @@ -8577,6 +8844,15 @@ micromark-factory-destination@^1.0.0: micromark-util-symbol "^1.0.0" micromark-util-types "^1.0.0" +micromark-factory-destination@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz#8fef8e0f7081f0474fbdd92deb50c990a0264639" + integrity sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + micromark-factory-label@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-1.0.2.tgz#6be2551fa8d13542fcbbac478258fb7a20047137" @@ -8587,6 +8863,16 @@ micromark-factory-label@^1.0.0: micromark-util-types "^1.0.0" uvu "^0.5.0" +micromark-factory-label@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz#5267efa97f1e5254efc7f20b459a38cb21058ba1" + integrity sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg== + dependencies: + devlop "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + micromark-factory-mdx-expression@^1.0.0: version "1.0.6" resolved "https://registry.yarnpkg.com/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-1.0.6.tgz#917e17d16e6e9c2551f3a862e6a9ebdd22056476" @@ -8609,6 +8895,14 @@ micromark-factory-space@^1.0.0: micromark-util-character "^1.0.0" micromark-util-types "^1.0.0" +micromark-factory-space@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz#36d0212e962b2b3121f8525fc7a3c7c029f334fc" + integrity sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-types "^2.0.0" + micromark-factory-title@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-1.0.2.tgz#7e09287c3748ff1693930f176e1c4a328382494f" @@ -8620,6 +8914,16 @@ micromark-factory-title@^1.0.0: micromark-util-types "^1.0.0" uvu "^0.5.0" +micromark-factory-title@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz#237e4aa5d58a95863f01032d9ee9b090f1de6e94" + integrity sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw== + dependencies: + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + micromark-factory-whitespace@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz#e991e043ad376c1ba52f4e49858ce0794678621c" @@ -8630,6 +8934,16 @@ micromark-factory-whitespace@^1.0.0: micromark-util-symbol "^1.0.0" micromark-util-types "^1.0.0" +micromark-factory-whitespace@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz#06b26b2983c4d27bfcc657b33e25134d4868b0b1" + integrity sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ== + dependencies: + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + micromark-util-character@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-1.1.0.tgz#d97c54d5742a0d9611a68ca0cd4124331f264d86" @@ -8638,6 +8952,14 @@ micromark-util-character@^1.0.0: micromark-util-symbol "^1.0.0" micromark-util-types "^1.0.0" +micromark-util-character@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz#2f987831a40d4c510ac261e89852c4e9703ccda6" + integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== + dependencies: + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + micromark-util-chunked@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz#5b40d83f3d53b84c4c6bce30ed4257e9a4c79d06" @@ -8645,6 +8967,13 @@ micromark-util-chunked@^1.0.0: dependencies: micromark-util-symbol "^1.0.0" +micromark-util-chunked@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz#47fbcd93471a3fccab86cff03847fc3552db1051" + integrity sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA== + dependencies: + micromark-util-symbol "^2.0.0" + micromark-util-classify-character@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz#cbd7b447cb79ee6997dd274a46fc4eb806460a20" @@ -8654,6 +8983,15 @@ micromark-util-classify-character@^1.0.0: micromark-util-symbol "^1.0.0" micromark-util-types "^1.0.0" +micromark-util-classify-character@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz#d399faf9c45ca14c8b4be98b1ea481bced87b629" + integrity sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + micromark-util-combine-extensions@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz#91418e1e74fb893e3628b8d496085639124ff3d5" @@ -8662,6 +9000,14 @@ micromark-util-combine-extensions@^1.0.0: micromark-util-chunked "^1.0.0" micromark-util-types "^1.0.0" +micromark-util-combine-extensions@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz#2a0f490ab08bff5cc2fd5eec6dd0ca04f89b30a9" + integrity sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg== + dependencies: + micromark-util-chunked "^2.0.0" + micromark-util-types "^2.0.0" + micromark-util-decode-numeric-character-reference@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz#dcc85f13b5bd93ff8d2868c3dba28039d490b946" @@ -8669,6 +9015,13 @@ micromark-util-decode-numeric-character-reference@^1.0.0: dependencies: micromark-util-symbol "^1.0.0" +micromark-util-decode-numeric-character-reference@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz#fcf15b660979388e6f118cdb6bf7d79d73d26fe5" + integrity sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw== + dependencies: + micromark-util-symbol "^2.0.0" + micromark-util-decode-string@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-1.0.2.tgz#942252ab7a76dec2dbf089cc32505ee2bc3acf02" @@ -8679,11 +9032,26 @@ micromark-util-decode-string@^1.0.0: micromark-util-decode-numeric-character-reference "^1.0.0" micromark-util-symbol "^1.0.0" +micromark-util-decode-string@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz#6cb99582e5d271e84efca8e61a807994d7161eb2" + integrity sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ== + dependencies: + decode-named-character-reference "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-encode@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-1.0.1.tgz#2c1c22d3800870ad770ece5686ebca5920353383" integrity sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA== +micromark-util-encode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz#0d51d1c095551cfaac368326963cf55f15f540b8" + integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== + micromark-util-events-to-acorn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-1.1.0.tgz#9891638e201c266484d0af7cd2505d208f73db9d" @@ -8702,6 +9070,11 @@ micromark-util-html-tag-name@^1.0.0: resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.1.0.tgz#eb227118befd51f48858e879b7a419fc0df20497" integrity sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA== +micromark-util-html-tag-name@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz#e40403096481986b41c106627f98f72d4d10b825" + integrity sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA== + micromark-util-normalize-identifier@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz#4a3539cb8db954bbec5203952bfe8cedadae7828" @@ -8709,6 +9082,13 @@ micromark-util-normalize-identifier@^1.0.0: dependencies: micromark-util-symbol "^1.0.0" +micromark-util-normalize-identifier@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz#c30d77b2e832acf6526f8bf1aa47bc9c9438c16d" + integrity sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q== + dependencies: + micromark-util-symbol "^2.0.0" + micromark-util-resolve-all@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz#a7c363f49a0162e931960c44f3127ab58f031d88" @@ -8716,6 +9096,13 @@ micromark-util-resolve-all@^1.0.0: dependencies: micromark-util-types "^1.0.0" +micromark-util-resolve-all@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz#e1a2d62cdd237230a2ae11839027b19381e31e8b" + integrity sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg== + dependencies: + micromark-util-types "^2.0.0" + micromark-util-sanitize-uri@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.0.0.tgz#27dc875397cd15102274c6c6da5585d34d4f12b2" @@ -8725,6 +9112,15 @@ micromark-util-sanitize-uri@^1.0.0: micromark-util-encode "^1.0.0" micromark-util-symbol "^1.0.0" +micromark-util-sanitize-uri@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz#ab89789b818a58752b73d6b55238621b7faa8fd7" + integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-subtokenize@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.2.tgz#ff6f1af6ac836f8bfdbf9b02f40431760ad89105" @@ -8735,16 +9131,36 @@ micromark-util-subtokenize@^1.0.0: micromark-util-types "^1.0.0" uvu "^0.5.0" +micromark-util-subtokenize@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz#d8ade5ba0f3197a1cf6a2999fbbfe6357a1a19ee" + integrity sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA== + dependencies: + devlop "^1.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + micromark-util-symbol@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz#b90344db62042ce454f351cf0bebcc0a6da4920e" integrity sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ== +micromark-util-symbol@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz#e5da494e8eb2b071a0d08fb34f6cefec6c0a19b8" + integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== + micromark-util-types@^1.0.0, micromark-util-types@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-1.0.2.tgz#f4220fdb319205812f99c40f8c87a9be83eded20" integrity sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w== +micromark-util-types@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz#f00225f5f5a0ebc3254f96c36b6605c4b393908e" + integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== + micromark@^3.0.0: version "3.0.10" resolved "https://registry.yarnpkg.com/micromark/-/micromark-3.0.10.tgz#1eac156f0399d42736458a14b0ca2d86190b457c" @@ -8768,6 +9184,29 @@ micromark@^3.0.0: micromark-util-types "^1.0.1" uvu "^0.5.0" +micromark@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.2.tgz#91395a3e1884a198e62116e33c9c568e39936fdb" + integrity sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA== + dependencies: + "@types/debug" "^4.0.0" + debug "^4.0.0" + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-subtokenize "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + micromatch@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" @@ -9181,6 +9620,16 @@ optimism@^0.16.1: "@wry/context" "^0.6.0" "@wry/trie" "^0.3.0" +optimism@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.18.1.tgz#5cf16847921413dbb0ac809907370388b9c6335f" + integrity sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ== + dependencies: + "@wry/caches" "^1.0.0" + "@wry/context" "^0.7.0" + "@wry/trie" "^0.5.0" + tslib "^2.3.0" + optionator@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" @@ -9533,7 +9982,7 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prop-types@^15.0.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -9556,6 +10005,11 @@ property-information@^6.0.0: resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.1.1.tgz#5ca85510a3019726cb9afed4197b7b8ac5926a22" integrity sha512-hrzC564QIl0r0vy4l6MvRLhafmUowhO/O3KgVSoXIbbA2Sz4j8HGpJc6T2cubRVwMwpdiG/vKGfhT4IixmKN9w== +property-information@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-7.0.0.tgz#3508a6d6b0b8eb3ca6eb2c6623b164d2ed2ab112" + integrity sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg== + proxy-compare@2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/proxy-compare/-/proxy-compare-2.5.1.tgz#17818e33d1653fbac8c2ec31406bce8a2966f600" @@ -9748,36 +10202,27 @@ react-is@^16.10.2, react-is@^16.13.1, react-is@^16.7.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^18.0.0: - version "18.1.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.1.0.tgz#61aaed3096d30eacf2a2127118b5b41387d32a67" - integrity sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg== - react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-markdown@^8.0.3: - version "8.0.3" - resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-8.0.3.tgz#e8aba0d2f5a1b2124d476ee1fff9448a2f57e4b3" - integrity sha512-We36SfqaKoVNpN1QqsZwWSv/OZt5J15LNgTLWynwAN5b265hrQrsjMtlRNwUvS+YyR3yDM8HpTNc4pK9H/Gc0A== - dependencies: - "@types/hast" "^2.0.0" - "@types/prop-types" "^15.0.0" - "@types/unist" "^2.0.0" - comma-separated-tokens "^2.0.0" - hast-util-whitespace "^2.0.0" - prop-types "^15.0.0" - property-information "^6.0.0" - react-is "^18.0.0" - remark-parse "^10.0.0" - remark-rehype "^10.0.0" - space-separated-tokens "^2.0.0" - style-to-object "^0.3.0" - unified "^10.0.0" - unist-util-visit "^4.0.0" - vfile "^5.0.0" +react-markdown@^9.0.3: + version "9.1.0" + resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-9.1.0.tgz#606bd74c6af131ba382a7c1282ff506708ed2e26" + integrity sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + hast-util-to-jsx-runtime "^2.0.0" + html-url-attributes "^3.0.0" + mdast-util-to-hast "^13.0.0" + remark-parse "^11.0.0" + remark-rehype "^11.0.0" + unified "^11.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" react-masonry-css@^1.0.16: version "1.0.16" @@ -10078,6 +10523,11 @@ regexpp@^3.2.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== +rehackt@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/rehackt/-/rehackt-0.1.0.tgz#a7c5e289c87345f70da8728a7eb878e5d03c696b" + integrity sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw== + relay-runtime@12.0.0: version "12.0.0" resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-12.0.0.tgz#1e039282bdb5e0c1b9a7dc7f6b9a09d4f4ff8237" @@ -10087,15 +10537,17 @@ relay-runtime@12.0.0: fbjs "^3.0.0" invariant "^2.2.4" -remark-gfm@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-3.0.1.tgz#0b180f095e3036545e9dddac0e8df3fa5cfee54f" - integrity sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig== +remark-gfm@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-4.0.1.tgz#33227b2a74397670d357bf05c098eaf8513f0d6b" + integrity sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg== dependencies: - "@types/mdast" "^3.0.0" - mdast-util-gfm "^2.0.0" - micromark-extension-gfm "^2.0.0" - unified "^10.0.0" + "@types/mdast" "^4.0.0" + mdast-util-gfm "^3.0.0" + micromark-extension-gfm "^3.0.0" + remark-parse "^11.0.0" + remark-stringify "^11.0.0" + unified "^11.0.0" remark-mdx@^2.0.0: version "2.1.2" @@ -10114,6 +10566,16 @@ remark-parse@^10.0.0: mdast-util-from-markdown "^1.0.0" unified "^10.0.0" +remark-parse@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" + integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-from-markdown "^2.0.0" + micromark-util-types "^2.0.0" + unified "^11.0.0" + remark-rehype@^10.0.0: version "10.1.0" resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-10.1.0.tgz#32dc99d2034c27ecaf2e0150d22a6dcccd9a6279" @@ -10124,6 +10586,26 @@ remark-rehype@^10.0.0: mdast-util-to-hast "^12.1.0" unified "^10.0.0" +remark-rehype@^11.0.0: + version "11.1.1" + resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.1.tgz#f864dd2947889a11997c0a2667cd6b38f685bca7" + integrity sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + mdast-util-to-hast "^13.0.0" + unified "^11.0.0" + vfile "^6.0.0" + +remark-stringify@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-11.0.0.tgz#4c5b01dd711c269df1aaae11743eb7e2e7636fd3" + integrity sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-to-markdown "^2.0.0" + unified "^11.0.0" + remedial@^1.0.7: version "1.0.8" resolved "https://registry.yarnpkg.com/remedial/-/remedial-1.0.8.tgz#a5e4fd52a0e4956adbaf62da63a5a46a78c578a0" @@ -10753,6 +11235,20 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +style-to-js@^1.0.0: + version "1.1.16" + resolved "https://registry.yarnpkg.com/style-to-js/-/style-to-js-1.1.16.tgz#e6bd6cd29e250bcf8fa5e6591d07ced7575dbe7a" + integrity sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw== + dependencies: + style-to-object "1.0.8" + +style-to-object@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.8.tgz#67a29bca47eaa587db18118d68f9d95955e81292" + integrity sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g== + dependencies: + inline-style-parser "0.2.4" + style-to-object@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" @@ -10915,6 +11411,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +trim-lines@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" + integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== + trough@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/trough/-/trough-2.1.0.tgz#0f7b511a4fde65a46f18477ab38849b22c554876" @@ -11170,6 +11671,19 @@ unified@^10.0.0: trough "^2.0.0" vfile "^5.0.0" +unified@^11.0.0: + version "11.0.5" + resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.5.tgz#f66677610a5c0a9ee90cab2b8d4d66037026d9e1" + integrity sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA== + dependencies: + "@types/unist" "^3.0.0" + bail "^2.0.0" + devlop "^1.0.0" + extend "^3.0.0" + is-plain-obj "^4.0.0" + trough "^2.0.0" + vfile "^6.0.0" + unist-builder@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-3.0.0.tgz#728baca4767c0e784e1e64bb44b5a5a753021a04" @@ -11187,6 +11701,13 @@ unist-util-is@^5.0.0: resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-5.1.1.tgz#e8aece0b102fa9bc097b0fef8f870c496d4a6236" integrity sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ== +unist-util-is@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424" + integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-position-from-estree@^1.0.0, unist-util-position-from-estree@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/unist-util-position-from-estree/-/unist-util-position-from-estree-1.1.1.tgz#96f4d543dfb0428edc01ebb928570b602d280c4c" @@ -11201,6 +11722,13 @@ unist-util-position@^4.0.0: dependencies: "@types/unist" "^2.0.0" +unist-util-position@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4" + integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== + dependencies: + "@types/unist" "^3.0.0" + unist-util-remove-position@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-4.0.1.tgz#d5b46a7304ac114c8d91990ece085ca7c2c135c8" @@ -11216,6 +11744,13 @@ unist-util-stringify-position@^3.0.0: dependencies: "@types/unist" "^2.0.0" +unist-util-stringify-position@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" + integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== + dependencies: + "@types/unist" "^3.0.0" + unist-util-visit-parents@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-4.1.1.tgz#e83559a4ad7e6048a46b1bdb22614f2f3f4724f2" @@ -11232,6 +11767,14 @@ unist-util-visit-parents@^5.0.0: "@types/unist" "^2.0.0" unist-util-is "^5.0.0" +unist-util-visit-parents@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815" + integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-visit@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-3.1.0.tgz#9420d285e1aee938c7d9acbafc8e160186dbaf7b" @@ -11250,6 +11793,15 @@ unist-util-visit@^4.0.0: unist-util-is "^5.0.0" unist-util-visit-parents "^5.0.0" +unist-util-visit@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" + integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" + universal-cookie@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-4.0.4.tgz#06e8b3625bf9af049569ef97109b4bb226ad798d" @@ -11454,6 +12006,14 @@ vfile-message@^3.0.0: "@types/unist" "^2.0.0" unist-util-stringify-position "^3.0.0" +vfile-message@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181" + integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-stringify-position "^4.0.0" + vfile@^5.0.0: version "5.3.2" resolved "https://registry.yarnpkg.com/vfile/-/vfile-5.3.2.tgz#b499fbc50197ea50ad3749e9b60beb16ca5b7c54" @@ -11464,6 +12024,14 @@ vfile@^5.0.0: unist-util-stringify-position "^3.0.0" vfile-message "^3.0.0" +vfile@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.3.tgz#3652ab1c496531852bf55a6bac57af981ebc38ab" + integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== + dependencies: + "@types/unist" "^3.0.0" + vfile-message "^4.0.0" + viem@^1.0.0: version "1.2.6" resolved "https://registry.yarnpkg.com/viem/-/viem-1.2.6.tgz#aa205be5eea4153623ad8cd732fb0477efc55e0f" From 157d85321ffab4fcd47041ca09753c516b9c8257 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Mon, 31 Mar 2025 21:44:26 +0100 Subject: [PATCH 02/37] Intergration -Start of voting transparency intergration -Experiencing fetch limits with infura --- components/Votes/contracts.tsx | 14 ++ components/Votes/fetchVotes.tsx | 66 ++++++ components/Votes/queries.tsx | 69 ++++++ components/Votes/voteTable.tsx | 366 ++++++++++++++++++++++++++++++ components/Votes/voterPopover.tsx | 315 +++++++++++++++++++++++++ pages/treasury/[proposal].tsx | 3 + 6 files changed, 833 insertions(+) create mode 100644 components/Votes/contracts.tsx create mode 100644 components/Votes/fetchVotes.tsx create mode 100644 components/Votes/queries.tsx create mode 100644 components/Votes/voteTable.tsx create mode 100644 components/Votes/voterPopover.tsx diff --git a/components/Votes/contracts.tsx b/components/Votes/contracts.tsx new file mode 100644 index 00000000..d4a3b929 --- /dev/null +++ b/components/Votes/contracts.tsx @@ -0,0 +1,14 @@ +import { ethers } from "ethers"; + +export const INFURA_RPC_URL = `https://arbitrum-mainnet.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_KEY}`; +export const CONTRACT_ADDRESS = "0xcfe4e2879b786c3aa075813f0e364bb5accb6aa0"; + +export const VOTECAST_TOPIC0 = ethers.utils.id( + "VoteCast(address,uint256,uint8,uint256,string)" +); + +export const provider = new ethers.providers.JsonRpcProvider(INFURA_RPC_URL); + +export const contractInterface = new ethers.utils.Interface([ + "event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason)", +]); diff --git a/components/Votes/fetchVotes.tsx b/components/Votes/fetchVotes.tsx new file mode 100644 index 00000000..c56a0574 --- /dev/null +++ b/components/Votes/fetchVotes.tsx @@ -0,0 +1,66 @@ +import { ethers } from "ethers"; +import { gql, ApolloClient, InMemoryCache } from "@apollo/client"; +import { + CONTRACT_ADDRESS, + VOTECAST_TOPIC0, + provider, + contractInterface, +} from "./contracts"; + +import { + ENS_QUERY +} from "./queries"; + +const ensClient = new ApolloClient({ + uri: process.env.NEXT_PUBLIC_ENS_API_URI, + cache: new InMemoryCache(), +}); + +export const fetchVotesFromInfura = async (proposalId: string) => { + try { + const logs = await provider.getLogs({ + address: CONTRACT_ADDRESS, + fromBlock: "earliest", + toBlock: "latest", + topics: [VOTECAST_TOPIC0], + }); + + const votes = logs + .map((log) => { + const decoded = contractInterface.parseLog(log); + return { + transactionHash: log.transactionHash, + voter: decoded?.args.voter.toLowerCase() || "", + choiceID: decoded?.args.support.toString() || "", + proposalId: decoded?.args.proposalId.toString() || "", + weight: ethers.utils.formatUnits(decoded?.args.weight.toString() || "0", 18), + reason: decoded?.args.reason || "No reason provided", + }; + }) + .filter((vote) => vote.proposalId === proposalId); + + if (votes.length === 0) return votes; + + const uniqueVoters = Array.from(new Set(votes.map((vote) => vote.voter))); + + const { data } = await ensClient.query({ + query: ENS_QUERY, + variables: { addresses: uniqueVoters }, + }); + + const ensMap: { [key: string]: string } = {}; + if (data?.domains) { + data.domains.forEach((domain: { resolvedAddress: { id: string }; name: string }) => { + ensMap[domain.resolvedAddress.id.toLowerCase()] = domain.name; + }); + } + + return votes.map((vote) => ({ + ...vote, + voter: ensMap[vote.voter] || vote.voter, + })); + } catch (error) { + console.log("Error fetching logs from Infura:", error); + return []; + } +}; \ No newline at end of file diff --git a/components/Votes/queries.tsx b/components/Votes/queries.tsx new file mode 100644 index 00000000..e2b61dcf --- /dev/null +++ b/components/Votes/queries.tsx @@ -0,0 +1,69 @@ +import { gql } from "@apollo/client"; + +export const GET_PROPOSALS_VOTES = gql` + query GetProposalsVotes { + treasuryProposals(orderBy: voteStart, orderDirection: desc) { + id + description + targets + values + voteStart + voteEnd + calldatas + proposer { + id + } + } + votes(first: 1000, orderBy: voter, orderDirection: desc) { + choiceID + id + voteStake + voter + poll { + id + proposal + } + } + } +`; + +export const GET_BLOCK_TIMESTAMPS = gql` + query GetBlockTimestamps { + rounds(first: 1000, orderBy: startTimestamp, orderDirection: desc) { + startTimestamp + id + } + } +`; + +export const GET_PROTOCOL_STATS = gql` + query GetProtocolStats { + protocols { + totalActiveStake + } + } +`; + +export const ENS_QUERY = gql` + query getENS($address: String!) { + domains(where: { resolvedAddress: $address } + orderBy: createdAt + orderDirection: desc) { + name + resolvedAddress { + id + } + } +} +`; + +export const GET_PROPOSALS_BY_IDS = gql` + query getProposalsByIds($ids: [String!]!) { + treasuryProposals(where: { id_in: $ids }) { + id + description + voteStart + voteEnd + } +} +`; \ No newline at end of file diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx new file mode 100644 index 00000000..e1861466 --- /dev/null +++ b/components/Votes/voteTable.tsx @@ -0,0 +1,366 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import Image from "next/image"; +import { ethers } from "ethers"; +import { ApolloClient, InMemoryCache, gql } from "@apollo/client"; +import ArbitrumIcon from "../../public/img/logos/arbitrum.svg"; +import VoterPopover from "./voterPopover"; + +import { + CONTRACT_ADDRESS, + VOTECAST_TOPIC0, + provider, + contractInterface, +} from "./contracts"; + +import { + ENS_QUERY +} from "./queries"; + +const createEnsApolloClient = () => + new ApolloClient({ + uri: process.env.NEXT_PUBLIC_ENS_API_URI, + cache: new InMemoryCache(), + }); + + + + +interface Vote { + transactionHash?: string; + weight: string; + voter: string; + choiceID: string; + proposalId: string; + reason: string; + ensName?: string; +} + +interface VoteTableProps { + proposalId: string; + proposalTitle: string; + ensCache?: any; + votes: { voter: string; weight: string; choiceID: string }[]; + formatStake?: (stake: number) => string; +} + +const formatAddress = (addr: string): string => { + if (!addr) return ""; + // If it ends with .xyz, always shorten if longer than 21 chars + if (addr.endsWith(".xyz")) { + return addr.length > 21 ? `${addr.slice(0, 6)}...${addr.slice(-6)}` : addr; + } + // If ends with .eth, return as is + if (addr.endsWith(".eth")) { + return addr; + } + // Otherwise, shorten if longer than 21 chars + return addr.length > 21 ? `${addr.slice(0, 6)}...${addr.slice(-6)}` : addr; +}; + +const getSupportClasses = (choiceID: string) => { + switch (choiceID) { + case "1": + return "text-green-500 font-semibold"; + case "0": + return "text-red-500 font-semibold"; + default: + return "text-yellow-500 font-semibold"; + } +}; + +const fetchVotesFromInfura = async (proposalId: string): Promise => { + const ensClient = createEnsApolloClient(); + try { + const logs = await provider.getLogs({ + address: CONTRACT_ADDRESS, + fromBlock: "earliest", + toBlock: "latest", + topics: [VOTECAST_TOPIC0], + }); + + const decodedVotes = logs + .map((log) => { + const decoded = contractInterface.parseLog(log); + return { + transactionHash: log.transactionHash, + voter: decoded?.args.voter.toLowerCase() || "", + choiceID: decoded?.args.support.toString() || "", + proposalId: decoded?.args.proposalId.toString() || "", + weight: decoded?.args.weight.toString() || "0", + reason: decoded?.args.reason || "No reason provided", + }; + }) + .filter((vote) => vote.proposalId === proposalId); + + // Fetch ENS names for unique voters + const uniqueVoters = Array.from(new Set(decodedVotes.map((v) => v.voter))); + const localEnsCache: { [address: string]: string } = {}; + + await Promise.all( + uniqueVoters.map(async (address) => { + try { + const { data } = await ensClient.query({ + query: ENS_QUERY, + variables: { address }, + }); + if (data?.domains?.length > 0) { + localEnsCache[address] = data.domains[0].name; + } + } catch (e) { + console.warn(`Failed to fetch ENS for ${address}`, e); + } + }) + ); + + // Replace voter address with ENS name if found + return decodedVotes.map((vote) => ({ + ...vote, + ensName: localEnsCache[vote.voter] || formatAddress(vote.voter), + })); + } catch (error) { + console.error("Error fetching logs from Infura:", error); + return []; + } +}; + +const VoteTable: React.FC = ({ + proposalId, + proposalTitle, + formatStake = (stake: number) => + new Intl.NumberFormat("en-US", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }).format(stake / 1e18), +}) => { + const [votes, setVotes] = useState([]); + const [loading, setLoading] = useState(true); + const [selectedVoter, setSelectedVoter] = useState(null); + + useEffect(() => { + if (!proposalId) return; + const loadVotes = async () => { + setLoading(true); + const fetchedVotes = await fetchVotesFromInfura(proposalId); + + const sorted = fetchedVotes.sort( + (a, b) => parseFloat(b.weight) - parseFloat(a.weight) + ); + setVotes(sorted); + setLoading(false); + }; + loadVotes(); + }, [proposalId]); + + const totalWeight = votes.reduce( + (sum, vote) => sum + parseFloat(vote.weight), + 0 + ); + + const calculateVotePercentage = (weight: string) => { + return totalWeight > 0 + ? ((parseFloat(weight) / totalWeight) * 100).toFixed(2) + : "0"; + }; + + if (loading) { + return ( +
+
+
+ ); + } + + if (votes.length === 0) { + return ( +
+ No votes found for this proposal. +
+ ); + } + + const yesCount = votes.filter((v) => v.choiceID === "1").length; + const noCount = votes.filter((v) => v.choiceID === "0").length; + const abstainCount = votes.filter((v) => v.choiceID === "2").length; + + return ( + <> + {/* Desktop Table */} +
+

+ Yes ({yesCount}) |{" "} + No ({noCount}) |{" "} + Abstain ({abstainCount}) +

+ + + + + + + + + + + + {votes.map((vote, index) => { + const supportText = + vote.choiceID === "1" + ? "Yes" + : vote.choiceID === "2" + ? "Abstain" + : "No"; + return ( + setSelectedVoter(vote.voter)} + > + + + + + + + ); + })} + +
+ Voter + + Support + + Weight + + Reason + + Vote Txn +
+ e.stopPropagation()} + > + {vote.ensName || formatAddress(vote.voter)} + + + {supportText} + + {new Intl.NumberFormat("en-US", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }).format(parseFloat(vote.weight) / 1e18)}{" "} + LPT ({calculateVotePercentage(vote.weight)}%) + + {vote.reason} + + {vote.transactionHash ? ( + e.stopPropagation()} + > + Tx + + ) : ( + N/A + )} +
+
+ + {/* Mobile Version */} +
+ {votes.map((vote, index) => { + const supportText = + vote.choiceID === "1" + ? "Yes" + : vote.choiceID === "2" + ? "Abstain" + : "No"; + return ( +
setSelectedVoter(vote.voter)} + > + +

+ Support: + + {supportText} + +

+

+ Weight: {formatStake(parseFloat(vote.weight))} + {" ("} + {calculateVotePercentage(vote.weight)}%) +

+

+ Reason: {vote.reason} +

+ +

+ {vote.transactionHash ? ( + e.stopPropagation()} + > + Txn + + ) : ( + N/A + )} +

+
+ ); + })} +
+ + {selectedVoter && ( + setSelectedVoter(null)} + /> + )} + + ); +}; + +export default VoteTable; diff --git a/components/Votes/voterPopover.tsx b/components/Votes/voterPopover.tsx new file mode 100644 index 00000000..d3b16290 --- /dev/null +++ b/components/Votes/voterPopover.tsx @@ -0,0 +1,315 @@ +"use client"; + +import React, { useState, useEffect, ReactNode } from "react"; +import { createPortal } from "react-dom"; +import { utils } from "ethers"; +import Image from "next/image"; +import ArbitrumIcon from "../../public/img/logos/arbitrum.svg"; +import { ethers } from "ethers"; +import { useQuery } from "@apollo/client"; +import { GET_PROPOSALS_BY_IDS, GET_PROPOSALS_VOTES } from "./queries"; +import { ApolloClient, InMemoryCache } from "@apollo/client"; +import { + CONTRACT_ADDRESS, + VOTECAST_TOPIC0, + provider, + contractInterface, +} from "./contracts"; + + + +interface Vote { + endVote: number; + description: string; + transactionHash?: string; + weight: string; + voter: string; + choiceID: string; + proposalId: string; + reason: string; + proposalTitle: string; + [x: string]: ReactNode; +} + +interface VoterPopoverProps { + voter: string; + proposalId: string; + onClose: () => void; +} + +const clientInstance = new ApolloClient({ + uri: process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT, + cache: new InMemoryCache(), +}); + +const fetchProposalByIdGraphQL = async ( + proposalId: string +): Promise<{ title: string; endVote: number; description: string }> => { + try { + const { data } = await clientInstance.query({ query: GET_PROPOSALS_VOTES }); + const proposal = data.treasuryProposals.find((p: any) => p.id === proposalId); + + if (!proposal || !proposal.description || !proposal.voteEnd) { + console.error(`Proposal data missing for ID: ${proposalId}`, proposal); + return { + title: "Unknown Proposal", + endVote: 0, + description: "No description available.", + }; + } + + const title = proposal.description.split("\n")[0].replace(/^#\s*/, ""); + return { + title, + endVote: proposal.voteEnd, + description: proposal.description, + }; + } catch (error) { + console.error(`Error fetching proposal ${proposalId}:`, error); + return { title: "Unknown Proposal", endVote: 0, description: "No description available." }; + } +}; + +const fetchVotesByVoter = async (voter: string, proposalId: string): Promise => { + try { + const voterTopic = ethers.utils.zeroPad(voter, 32); + const logs = await provider.getLogs({ + address: CONTRACT_ADDRESS, + fromBlock: "earliest", + toBlock: "latest", + topics: [VOTECAST_TOPIC0, ethers.utils.hexlify(voterTopic)], + }); + + const proposalsMap = new Map(); + + const votesWithDetails = await Promise.all( + logs.map(async (log) => { + const decoded = contractInterface.parseLog(log); + const proposalIdFromLog = decoded?.args.proposalId.toString(); + + if (!proposalsMap.has(proposalIdFromLog)) { + const proposal = await fetchProposalByIdGraphQL(proposalIdFromLog); + proposalsMap.set(proposalIdFromLog, proposal); + } + + const proposal = proposalsMap.get(proposalIdFromLog); + + return { + transactionHash: log.transactionHash, + voter: decoded?.args.voter, + choiceID: decoded?.args.support.toString(), + proposalId: proposalIdFromLog, + weight: decoded?.args.weight.toString(), + reason: decoded?.args.reason, + endVote: proposal?.endVote || 0, + description: proposal?.description || "No description available.", + proposalTitle: proposal?.title || "Unknown Proposal", + }; + }) + ); + + return votesWithDetails.sort((a, b) => a.endVote - b.endVote); + } catch (error) { + console.error("Error fetching votes for voter:", error); + return []; + } +}; + +const VoterPopover: React.FC = ({ voter, proposalId, onClose }) => { + const [logsLoading, setLogsLoading] = useState(true); + const [proposalIds, setProposalIds] = useState([]); + const [rawVotes, setRawVotes] = useState([]); + + useEffect(() => { + const fetchLogsForVoter = async () => { + try { + setLogsLoading(true); + const voterTopic = ethers.utils.zeroPad(voter, 32); + const logs = await provider.getLogs({ + address: CONTRACT_ADDRESS, + fromBlock: "earliest", + toBlock: "latest", + topics: [VOTECAST_TOPIC0, ethers.utils.hexlify(voterTopic)], + }); + + const decodedVotes = logs.map((log) => { + const decoded = contractInterface.parseLog(log); + return { + transactionHash: log.transactionHash, + voter: decoded?.args.voter, + proposalId: decoded?.args.proposalId.toString(), + choiceID: decoded?.args.support.toString(), + weight: decoded?.args.weight.toString(), + reason: decoded?.args.reason, + }; + }); + + setRawVotes(decodedVotes); + + const uniqueProposalIds = Array.from( + new Set(decodedVotes.map((v) => v.proposalId)) + ); + + setProposalIds(uniqueProposalIds); + setLogsLoading(false); + } catch (error) { + console.error("Error fetching logs for voter:", error); + setLogsLoading(false); + } + }; + + fetchLogsForVoter(); + }, [voter, proposalId]); + + const { data, loading: proposalsLoading } = useQuery(GET_PROPOSALS_BY_IDS, { + variables: { ids: proposalIds }, + skip: proposalIds.length === 0, + }); + + const votes: Vote[] = React.useMemo(() => { + if (logsLoading || proposalsLoading || !rawVotes) return []; + + const proposalsMap = new Map(); + if (data?.treasuryProposals) { + data.treasuryProposals.forEach((p: any) => { + proposalsMap.set(p.id, { + description: p.description || "No description available.", + voteEnd: p.voteEnd || 0, + }); + }); + } + + return rawVotes + .map((v) => { + const subgraphData = proposalsMap.get(v.proposalId); + + const description = subgraphData?.description || "No description available."; + const endVote = subgraphData?.voteEnd || 0; + const title = description.split("\n")[0].replace(/^#\s*/, "") || "Unknown Proposal"; + + return { + transactionHash: v.transactionHash, + voter: v.voter, + choiceID: v.choiceID, + proposalId: v.proposalId, + weight: v.weight, + reason: v.reason || "", + endVote, + description, + proposalTitle: title, + }; + }) + .sort((b, a) => a.endVote - b.endVote); + }, [logsLoading, proposalsLoading, rawVotes, data]); + + const isLoading = logsLoading || proposalsLoading; + + const getSupportClasses = (choiceID: string) => { + switch (choiceID) { + case "1": + return "text-green-500 font-semibold"; + case "0": + return "text-red-500 font-semibold"; + default: + return "text-yellow-500 font-semibold"; + } + }; + + const formatAddress = (addr: string): string => { + if (!addr) return ""; + + if (addr.endsWith(".xyz")) { + return addr.length > 21 ? `${addr.slice(0, 6)}...${addr.slice(-6)}` : addr; + } + + if (addr.endsWith(".eth")) { + return addr; + } + + return addr.length > 21 ? `${addr.slice(0, 6)}...${addr.slice(-6)}` : addr; + }; + + const popoverContent = ( +
+
+ + + {isLoading ? ( +
+
+
+ ) : votes.length > 0 ? ( + votes.map((vote, index) => ( +
+

+ {vote.proposalTitle} +

+

+ Proposal ID: + + {formatAddress(vote.proposalId)} + +

+

+ Support: + + {vote.choiceID === "1" ? "Yes" : vote.choiceID === "2" ? "Abstain" : "No"} + +

+

+ Weight: + {new Intl.NumberFormat("en-US", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }).format(parseFloat(vote.weight) / 10 ** 18)}{" "} + LPT +

+

+ Reason: + {vote.reason || "No reason provided"} +

+ +

+ {vote.transactionHash ? ( + e.stopPropagation()} + > + Txn + + ) : ( + N/A + )} +

+
+ )) + ) : ( +

No votes found for this voter.

+ )} +
+
+ ); + + + return createPortal(popoverContent, document.body); +}; + +export default VoterPopover; +function zeroPadValue(voter: string, length: number): string { + return ethers.utils.hexlify(ethers.utils.zeroPad(voter, length)); +} + diff --git a/pages/treasury/[proposal].tsx b/pages/treasury/[proposal].tsx index dd556e55..f6713c1f 100644 --- a/pages/treasury/[proposal].tsx +++ b/pages/treasury/[proposal].tsx @@ -41,6 +41,7 @@ import { decodeFunctionData } from "viem"; import { livepeerToken } from "@lib/api/abis/main/LivepeerToken"; import { CHAIN_INFO, DEFAULT_CHAIN, DEFAULT_CHAIN_ID } from "@lib/chains"; import { BigNumber } from "ethers"; +import VoteTable from "@components/Votes/voteTable"; dayjs.extend(relativeTime); @@ -586,7 +587,9 @@ const Proposal = () => { {proposal.description} + + {width > 1200 ? ( From 1979dbdb9537ac3256073b0efe8d6cad75005bc7 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Tue, 1 Apr 2025 20:08:41 +0100 Subject: [PATCH 03/37] Test data is fetching and renders -Data renders -UI to work on with Arb Txn -UI for Mobile and Desktop -Test popOver and all associated links --- components/Votes/voteTable.tsx | 11 ++-- components/Votes/voterPopover.tsx | 6 +- lib/utils.tsx | 14 ++++- pages/treasury/[proposal].tsx | 96 ++++++++++++++++++++++++++++++- 4 files changed, 116 insertions(+), 11 deletions(-) diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx index e1861466..86318c1f 100644 --- a/components/Votes/voteTable.tsx +++ b/components/Votes/voteTable.tsx @@ -1,7 +1,7 @@ "use client"; import React, { useState, useEffect } from "react"; -import Image from "next/image"; +// import Image from "next/image"; import { ethers } from "ethers"; import { ApolloClient, InMemoryCache, gql } from "@apollo/client"; import ArbitrumIcon from "../../public/img/logos/arbitrum.svg"; @@ -264,12 +264,13 @@ const VoteTable: React.FC = ({ className="inline-flex items-center text-blue-400 underline" onClick={(e) => e.stopPropagation()} > - Tx + /> */} + ) : ( N/A @@ -335,13 +336,13 @@ const VoteTable: React.FC = ({ className="inline-flex items-center" onClick={(e) => e.stopPropagation()} > - Txn + /> */} ) : ( N/A diff --git a/components/Votes/voterPopover.tsx b/components/Votes/voterPopover.tsx index d3b16290..53e0812e 100644 --- a/components/Votes/voterPopover.tsx +++ b/components/Votes/voterPopover.tsx @@ -3,7 +3,7 @@ import React, { useState, useEffect, ReactNode } from "react"; import { createPortal } from "react-dom"; import { utils } from "ethers"; -import Image from "next/image"; +// import Image from "next/image"; import ArbitrumIcon from "../../public/img/logos/arbitrum.svg"; import { ethers } from "ethers"; import { useQuery } from "@apollo/client"; @@ -283,13 +283,13 @@ const VoterPopover: React.FC = ({ voter, proposalId, onClose className="inline-flex items-center" onClick={(e) => e.stopPropagation()} > - Txn + /> */} ) : ( N/A diff --git a/lib/utils.tsx b/lib/utils.tsx index 806e1d11..2ea633f7 100644 --- a/lib/utils.tsx +++ b/lib/utils.tsx @@ -420,7 +420,19 @@ export function toTitleCase(str) { }); } -export const fromWei = (wei: BigNumberish) => formatEther(wei); +export const fromWei = (wei: BigNumberish) => { + try { + const valueStr = + typeof wei === "number" || wei instanceof Number + ? wei.toString() + : wei; + + return formatEther(BigNumber.from(valueStr)); + } catch (e) { + console.error("fromWei error:", e, "input was:", wei); + return "0"; + } +}; export const toWei = (ether: BigNumberish) => parseUnits(ether.toString(), "ether").toBigInt(); diff --git a/pages/treasury/[proposal].tsx b/pages/treasury/[proposal].tsx index f6713c1f..18760c22 100644 --- a/pages/treasury/[proposal].tsx +++ b/pages/treasury/[proposal].tsx @@ -1,10 +1,12 @@ import { getLayout, LAYOUT_MAX_WIDTH } from "@layouts/main"; import { useRouter } from "next/router"; +import React, { useState, useEffect } from "react"; import { abbreviateNumber, fromWei, shortenAddress } from "@lib/utils"; import MarkdownRenderer from "@components/MarkdownRenderer"; import BottomDrawer from "@components/BottomDrawer"; import Spinner from "@components/Spinner"; import Stat from "@components/Stat"; +import { fetchVotesFromInfura } from "@components/Votes/fetchVotes"; import { Badge, Box, @@ -35,13 +37,30 @@ import { sentenceCase } from "change-case"; import relativeTime from "dayjs/plugin/relativeTime"; import numeral from "numeral"; import { BadgeVariantByState } from "@components/TreasuryProposalRow"; +import VoteList from "@components/Votes/voteTable"; import TreasuryVotingWidget from "@components/TreasuryVotingWidget"; import { getProposalExtended } from "@lib/api/treasury"; import { decodeFunctionData } from "viem"; import { livepeerToken } from "@lib/api/abis/main/LivepeerToken"; import { CHAIN_INFO, DEFAULT_CHAIN, DEFAULT_CHAIN_ID } from "@lib/chains"; import { BigNumber } from "ethers"; -import VoteTable from "@components/Votes/voteTable"; + + +interface Proposal { + id: string; + description: string; + voteStart: number; + voteEnd: number; + proposer: { id: string }; + votes?: { weight: string; choiceID: string; voter?: string }[]; + targets: string[]; + calldatas: string[]; +} +interface Vote { + weight: string; + choiceID: string; + voter?: string; +} dayjs.extend(relativeTime); @@ -81,6 +100,14 @@ const Proposal = () => { const { data: protocolQuery } = useProtocolQuery(); const currentRound = useCurrentRoundData(); + const [votes, setVotes] = useState([]); + const [voteCount, setVoteCount] = useState(0); + const [loadingVotes, setLoadingVotes] = useState(true); + const [ensCache, setEnsCache] = useState({}); + const [votesOpen, setVotesOpen] = useState(false); + + + const proposal = useMemo(() => { if (!proposalQuery || !state || !protocolQuery || !currentRound) { return null; @@ -130,6 +157,39 @@ const Proposal = () => { return ; } + useEffect(() => { + async function fetchVotes() { + if (!proposal?.id) return; + + setLoadingVotes(true); + try { + const fetchedVotes = await fetchVotesFromInfura(proposal.id); + setVotes(fetchedVotes); + setVoteCount(fetchedVotes.length); + + const cache = {}; + for (const vote of fetchedVotes) { + if (vote.voter && !cache[vote.voter]) { + cache[vote.voter] = null; + } + } + setEnsCache(cache); + } catch (err) { + console.error("Error fetching votes", err); + } finally { + setLoadingVotes(false); + } + } + + fetchVotes(); + }, [proposal?.id]); + + const formatStake = (stake: number) => + `${numeral(parseFloat(fromWei(stake.toString()))).format("0,0.[00]")} LPT`; + + + + if (!proposal) { return ( <> @@ -587,7 +647,39 @@ const Proposal = () => { {proposal.description} - + + +
setVotesOpen(!votesOpen)} + > +

+ Votes ({loadingVotes ? "Loading..." : voteCount ?? "0"}) +

+ + {votesOpen ? "–" : "+"} +
+ {votesOpen && ( + ({ + voter: vote.voter || "Unknown", + weight: vote.weight, + choiceID: vote.choiceID, + }))} + /> + + + )} + +
From 77bda06aaba6f475ade61a17b534419b48704f8b Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Tue, 1 Apr 2025 22:16:19 +0100 Subject: [PATCH 04/37] Temp limit to fetches and updated UI --- components/Votes/fetchVotes.tsx | 14 +++--- components/Votes/voteTable.tsx | 80 ++++++++++++++++++--------------- hooks/useSwr.tsx | 5 ++- lib/axios.ts | 4 +- pages/treasury/[proposal].tsx | 20 +++++---- 5 files changed, 69 insertions(+), 54 deletions(-) diff --git a/components/Votes/fetchVotes.tsx b/components/Votes/fetchVotes.tsx index c56a0574..8beaec31 100644 --- a/components/Votes/fetchVotes.tsx +++ b/components/Votes/fetchVotes.tsx @@ -18,12 +18,14 @@ const ensClient = new ApolloClient({ export const fetchVotesFromInfura = async (proposalId: string) => { try { - const logs = await provider.getLogs({ - address: CONTRACT_ADDRESS, - fromBlock: "earliest", - toBlock: "latest", - topics: [VOTECAST_TOPIC0], - }); +const currentBlock = await provider.getBlockNumber(); +const logs = await provider.getLogs({ + address: CONTRACT_ADDRESS, + fromBlock: currentBlock - 10000, + toBlock: "latest", + topics: [VOTECAST_TOPIC0], +}); + const votes = logs .map((log) => { diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx index 86318c1f..2d2755cf 100644 --- a/components/Votes/voteTable.tsx +++ b/components/Votes/voteTable.tsx @@ -3,7 +3,7 @@ import React, { useState, useEffect } from "react"; // import Image from "next/image"; import { ethers } from "ethers"; -import { ApolloClient, InMemoryCache, gql } from "@apollo/client"; +import { ApolloClient, InMemoryCache } from "@apollo/client"; import ArbitrumIcon from "../../public/img/logos/arbitrum.svg"; import VoterPopover from "./voterPopover"; @@ -14,9 +14,7 @@ import { contractInterface, } from "./contracts"; -import { - ENS_QUERY -} from "./queries"; +import { ENS_QUERY } from "./queries"; const createEnsApolloClient = () => new ApolloClient({ @@ -24,9 +22,6 @@ const createEnsApolloClient = () => cache: new InMemoryCache(), }); - - - interface Vote { transactionHash?: string; weight: string; @@ -180,19 +175,23 @@ const VoteTable: React.FC = ({ ); } + // Count votes by support const yesCount = votes.filter((v) => v.choiceID === "1").length; const noCount = votes.filter((v) => v.choiceID === "0").length; const abstainCount = votes.filter((v) => v.choiceID === "2").length; + const totalVotes = yesCount + noCount + abstainCount; + return ( <> {/* Desktop Table */}
-

- Yes ({yesCount}) |{" "} - No ({noCount}) |{" "} - Abstain ({abstainCount}) -

+
+

Total: {totalVotes}

+ Yes ({yesCount}) |{" "} + No ({noCount}) |{" "} + Abstain ({abstainCount}) +
@@ -264,13 +263,13 @@ const VoteTable: React.FC = ({ className="inline-flex items-center text-blue-400 underline" onClick={(e) => e.stopPropagation()} > - {/* */} - ) : ( N/A @@ -285,6 +284,14 @@ const VoteTable: React.FC = ({ {/* Mobile Version */}
+ +
+

Total: {totalVotes}

+
+ Yes ({yesCount}) |{" "} + No ({noCount}) |{" "} + Abstain ({abstainCount}) +
{votes.map((vote, index) => { const supportText = vote.choiceID === "1" @@ -299,7 +306,7 @@ const VoteTable: React.FC = ({ onClick={() => setSelectedVoter(vote.voter)} > ); diff --git a/hooks/useSwr.tsx b/hooks/useSwr.tsx index 0dd87b72..bae5ce6c 100644 --- a/hooks/useSwr.tsx +++ b/hooks/useSwr.tsx @@ -81,7 +81,10 @@ export const useScoreData = (address: string | undefined | null) => { export const useCurrentRoundData = () => { const { data } = useSWR(`/current-round`, { - refreshInterval: 10000, + // refreshInterval: 10000, + refreshInterval: 120000, + revalidateOnFocus: false, + dedupingInterval: 120000, }); return data ?? null; diff --git a/lib/axios.ts b/lib/axios.ts index a35e91d8..d33901fd 100644 --- a/lib/axios.ts +++ b/lib/axios.ts @@ -2,8 +2,10 @@ import { default as defaultAxios } from "axios"; export const axiosClient = defaultAxios.create({ baseURL: "/api", - timeout: 10000, + // timeout: 10000, + timeout: 120000, }); + export const fetcher = (url: string) => axiosClient.get(url).then((res) => res.data); diff --git a/pages/treasury/[proposal].tsx b/pages/treasury/[proposal].tsx index 18760c22..726ae643 100644 --- a/pages/treasury/[proposal].tsx +++ b/pages/treasury/[proposal].tsx @@ -164,11 +164,14 @@ const Proposal = () => { setLoadingVotes(true); try { const fetchedVotes = await fetchVotesFromInfura(proposal.id); - setVotes(fetchedVotes); - setVoteCount(fetchedVotes.length); - - const cache = {}; - for (const vote of fetchedVotes) { + const validVotes = fetchedVotes.filter(vote => + ["0", "1", "2"].includes(vote.choiceID) + ); + setVotes(validVotes); + setVoteCount(validVotes.length); + + const cache: { [key: string]: null } = {}; + for (const vote of validVotes) { if (vote.voter && !cache[vote.voter]) { cache[vote.voter] = null; } @@ -184,6 +187,7 @@ const Proposal = () => { fetchVotes(); }, [proposal?.id]); + const formatStake = (stake: number) => `${numeral(parseFloat(fromWei(stake.toString()))).format("0,0.[00]")} LPT`; @@ -658,10 +662,8 @@ const Proposal = () => { onClick={() => setVotesOpen(!votesOpen)} >

- Votes ({loadingVotes ? "Loading..." : voteCount ?? "0"}) -

- - {votesOpen ? "–" : "+"} + View Votes {votesOpen ? "–" : "+"} +
{votesOpen && ( Date: Wed, 2 Apr 2025 20:45:15 +0100 Subject: [PATCH 05/37] updated styling to Livepeer CSS -Converted from Tailwind to Livepper CSS format -Improved UI / UX --- components/Votes/voteTable.tsx | 495 ++++++++++++++++++++------------- pages/treasury/[proposal].tsx | 82 +++--- 2 files changed, 351 insertions(+), 226 deletions(-) diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx index 2d2755cf..a7b69ee2 100644 --- a/components/Votes/voteTable.tsx +++ b/components/Votes/voteTable.tsx @@ -1,10 +1,16 @@ "use client"; import React, { useState, useEffect } from "react"; -// import Image from "next/image"; -import { ethers } from "ethers"; import { ApolloClient, InMemoryCache } from "@apollo/client"; -import ArbitrumIcon from "../../public/img/logos/arbitrum.svg"; +import { ethers } from "ethers"; +import { + Box, + Card, + Flex, + Heading, + Link, + Text, +} from "@livepeer/design-system"; import VoterPopover from "./voterPopover"; import { @@ -13,8 +19,7 @@ import { provider, contractInterface, } from "./contracts"; - -import { ENS_QUERY } from "./queries"; +import { ENS_QUERY } from "./queries"; const createEnsApolloClient = () => new ApolloClient({ @@ -35,33 +40,30 @@ interface Vote { interface VoteTableProps { proposalId: string; proposalTitle: string; - ensCache?: any; + ensCache?: any; votes: { voter: string; weight: string; choiceID: string }[]; formatStake?: (stake: number) => string; } const formatAddress = (addr: string): string => { if (!addr) return ""; - // If it ends with .xyz, always shorten if longer than 21 chars if (addr.endsWith(".xyz")) { return addr.length > 21 ? `${addr.slice(0, 6)}...${addr.slice(-6)}` : addr; } - // If ends with .eth, return as is if (addr.endsWith(".eth")) { return addr; } - // Otherwise, shorten if longer than 21 chars return addr.length > 21 ? `${addr.slice(0, 6)}...${addr.slice(-6)}` : addr; }; -const getSupportClasses = (choiceID: string) => { +const getSupportStyles = (choiceID: string) => { switch (choiceID) { case "1": - return "text-green-500 font-semibold"; + return { color: "$green9", fontWeight: 600 }; case "0": - return "text-red-500 font-semibold"; + return { color: "$red9", fontWeight: 600 }; default: - return "text-yellow-500 font-semibold"; + return { color: "$yellow9", fontWeight: 600 }; } }; @@ -109,7 +111,6 @@ const fetchVotesFromInfura = async (proposalId: string): Promise => { }) ); - // Replace voter address with ENS name if found return decodedVotes.map((vote) => ({ ...vote, ensName: localEnsCache[vote.voter] || formatAddress(vote.voter), @@ -138,7 +139,6 @@ const VoteTable: React.FC = ({ const loadVotes = async () => { setLoading(true); const fetchedVotes = await fetchVotesFromInfura(proposalId); - const sorted = fetchedVotes.sort( (a, b) => parseFloat(b.weight) - parseFloat(a.weight) ); @@ -161,17 +161,29 @@ const VoteTable: React.FC = ({ if (loading) { return ( -
-
-
+ + Loading votes... + ); } if (votes.length === 0) { return ( -
+ No votes found for this proposal. -
+ ); } @@ -179,186 +191,279 @@ const VoteTable: React.FC = ({ const yesCount = votes.filter((v) => v.choiceID === "1").length; const noCount = votes.filter((v) => v.choiceID === "0").length; const abstainCount = votes.filter((v) => v.choiceID === "2").length; - const totalVotes = yesCount + noCount + abstainCount; + // Desktop Layout (Table-like) +const renderDesktopTable = () => ( + + + Yes ({yesCount}) + | + No ({noCount}) + | + + Abstain ({abstainCount}) + + + + + + {[ + { label: "Voter", width: "auto" }, + { label: "Support", width: "auto" }, + { label: "Weight", width: "30%" }, + { label: "Reason", width: "10%" }, + { label: "Vote Txn", width: "auto" }, + ].map((header) => ( + + {header.label} + + ))} + + + + {votes.map((vote, index) => { + const supportText = + vote.choiceID === "1" + ? "Yes" + : vote.choiceID === "2" + ? "Abstain" + : "No"; + return ( + td": { padding: "$2" }, // Added padding to each cell in the row + }} + onClick={(e) => { + e.stopPropagation(); + setSelectedVoter(vote.voter); + }} + > + + e.stopPropagation()} + > + {formatAddress(vote.ensName ?? "") || formatAddress(vote.voter)} + + + + {supportText} + + + {new Intl.NumberFormat("en-US", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }).format(parseFloat(vote.weight) / 1e18)}{" "} + LPT ({calculateVotePercentage(vote.weight)}%) + + + {vote.reason} + + + {vote.transactionHash ? ( + e.stopPropagation()} + > + Txn + + ) : ( + N/A + )} + + + ); + })} + + + + ); + + + // Mobile Layout (Card-based) + const renderMobileCards = () => ( + + + Total: {totalVotes} + Yes ({yesCount}) + | + No ({noCount}) + | + + Abstain ({abstainCount}) + + + {votes.map((vote, index) => { + const supportText = + vote.choiceID === "1" + ? "Yes" + : vote.choiceID === "2" + ? "Abstain" + : "No"; return ( - <> - {/* Desktop Table */} -
-
-

Total: {totalVotes}

- Yes ({yesCount}) |{" "} - No ({noCount}) |{" "} - Abstain ({abstainCount}) -
-
- - - - - - - - - - - {votes.map((vote, index) => { - const supportText = - vote.choiceID === "1" - ? "Yes" - : vote.choiceID === "2" - ? "Abstain" - : "No"; - return ( - setSelectedVoter(vote.voter)} - > - - - - - - - ); - })} - -
- Voter - - Support - - Weight - - Reason - - Vote Txn -
- e.stopPropagation()} - > - {vote.ensName || formatAddress(vote.voter)} - - - {supportText} - - {new Intl.NumberFormat("en-US", { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }).format(parseFloat(vote.weight) / 1e18)}{" "} - LPT ({calculateVotePercentage(vote.weight)}%) - - {vote.reason} - - {vote.transactionHash ? ( - e.stopPropagation()} - > - {/* Uncomment and use Image if needed - Tx */} - - ) : ( - N/A - )} -
-
+ { + e.stopPropagation(); + setSelectedVoter(vote.voter); + }} + > + + {vote.ensName || formatAddress(vote.voter)} + + + + Support: + {" "} + + {supportText} + + + + + Weight: + {" "} + {formatStake(parseFloat(vote.weight))} LPT ({calculateVotePercentage(vote.weight)}%) + + + + Reason: + {" "} + {vote.reason} + + + {vote.transactionHash ? ( + e.stopPropagation()} + > + Txn + + ) : ( + N/A + )} + + + ); +})} - {/* Mobile Version */} -
- -
-

Total: {totalVotes}

-
- Yes ({yesCount}) |{" "} - No ({noCount}) |{" "} - Abstain ({abstainCount}) -
- {votes.map((vote, index) => { - const supportText = - vote.choiceID === "1" - ? "Yes" - : vote.choiceID === "2" - ? "Abstain" - : "No"; - return ( -
setSelectedVoter(vote.voter)} - > - -

- Support: - - {supportText} - -

-

- Weight: {formatStake(parseFloat(vote.weight))} ( - {calculateVotePercentage(vote.weight)}%) -

-

- Reason: {vote.reason} -

-

- {vote.transactionHash ? ( - e.stopPropagation()} - > - {/* Uncomment and use Image if needed - Txn */} - - ) : ( - N/A - )} -

-
- ); - })} -
+ + ); + return ( + <> + {renderDesktopTable()} + {renderMobileCards()} {selectedVoter && ( { const [ensCache, setEnsCache] = useState({}); const [votesOpen, setVotesOpen] = useState(false); - - const proposal = useMemo(() => { if (!proposalQuery || !state || !protocolQuery || !currentRound) { return null; @@ -653,35 +651,57 @@ const Proposal = () => { -
setVotesOpen(!votesOpen)} - > -

- View Votes {votesOpen ? "–" : "+"} -

-
- {votesOpen && ( - ({ - voter: vote.voter || "Unknown", - weight: vote.weight, - choiceID: vote.choiceID, - }))} - /> - - - )} - -
+ css={{ + padding: "$4", + border: "1px solid $neutral4", + cursor: "pointer", + }} + onClick={() => setVotesOpen(!votesOpen)} +> + + + View Votes + + {votesOpen ? "–" : "+"} + + + + + {votesOpen && ( + ({ + voter: vote.voter || "Unknown", + weight: vote.weight, + choiceID: vote.choiceID, + }))} + /> + )} + + From e64faa679a4021100bc5d3d22ef350708ef1de7d Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Wed, 2 Apr 2025 21:33:59 +0100 Subject: [PATCH 06/37] Total Votes added --- components/Votes/voteTable.tsx | 71 ++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx index a7b69ee2..1cf3330f 100644 --- a/components/Votes/voteTable.tsx +++ b/components/Votes/voteTable.tsx @@ -2,7 +2,8 @@ import React, { useState, useEffect } from "react"; import { ApolloClient, InMemoryCache } from "@apollo/client"; -import { ethers } from "ethers"; +import ArbitrumIcon from "../../public/img/logos/arbitrum.png"; +import Image from "next/image"; import { Box, Card, @@ -187,7 +188,6 @@ const VoteTable: React.FC = ({ ); } - // Count votes by support const yesCount = votes.filter((v) => v.choiceID === "1").length; const noCount = votes.filter((v) => v.choiceID === "0").length; const abstainCount = votes.filter((v) => v.choiceID === "2").length; @@ -211,6 +211,8 @@ const renderDesktopTable = () => ( color: "$white", }} > + Total: {totalVotes} +

Yes ({yesCount}) | No ({noCount}) @@ -275,7 +277,7 @@ const renderDesktopTable = () => ( position: "relative", zIndex: 10, "&:hover": { backgroundColor: "$neutral4" }, - "& > td": { padding: "$2" }, // Added padding to each cell in the row + "& > td": { padding: "$2" }, }} onClick={(e) => { e.stopPropagation(); @@ -319,7 +321,7 @@ const renderDesktopTable = () => ( textAlign: "center", color: "$white", borderBottom: "1px solid $neutral5", - width: "15%", // Adjusted width for Weight + width: "15%", }} > {new Intl.NumberFormat("en-US", { @@ -334,31 +336,32 @@ const renderDesktopTable = () => ( textAlign: "center", color: "$neutral9", borderBottom: "1px solid $neutral5", - width: "35%", // Adjusted width for Reason + width: "35%", }} > {vote.reason} + - {vote.transactionHash ? ( - e.stopPropagation()} - > - Txn - - ) : ( - N/A - )} - + as="td" + css={{ + textAlign: "center", + borderBottom: "1px solid $neutral5", + }} +> + {vote.transactionHash ? ( + e.stopPropagation()} + > + Arbitrum Icon + + ) : ( + N/A + )} + + ); })} @@ -385,14 +388,15 @@ const renderDesktopTable = () => ( color: "$white", }} > - Total: {totalVotes} - Yes ({yesCount}) - | - No ({noCount}) - | - - Abstain ({abstainCount}) - + Total: {totalVotes} +

+ Yes ({yesCount}) + | + No ({noCount}) + | + + Abstain ({abstainCount}) + {votes.map((vote, index) => { const supportText = @@ -446,8 +450,7 @@ const renderDesktopTable = () => ( target="_blank" css={{ color: "$blue9", textDecoration: "underline" }} onClick={(e) => e.stopPropagation()} - > - Txn + >Txn ) : ( N/A From bab08c078dc5d322c8d1e37c599c25a4d225dd7a Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Thu, 3 Apr 2025 15:41:48 +0100 Subject: [PATCH 07/37] voting popover logic -Voting popover now rendering -Need to fix logic to prevent voting from also closing when closed. --- components/Votes/voteTable.tsx | 97 ++++++++++--------- components/Votes/voterPopover.tsx | 150 +++++++++++++++--------------- pages/treasury/[proposal].tsx | 5 +- 3 files changed, 130 insertions(+), 122 deletions(-) diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx index 1cf3330f..a88258f4 100644 --- a/components/Votes/voteTable.tsx +++ b/components/Votes/voteTable.tsx @@ -191,7 +191,6 @@ const VoteTable: React.FC = ({ const yesCount = votes.filter((v) => v.choiceID === "1").length; const noCount = votes.filter((v) => v.choiceID === "0").length; const abstainCount = votes.filter((v) => v.choiceID === "2").length; - const totalVotes = yesCount + noCount + abstainCount; // Desktop Layout (Table-like) const renderDesktopTable = () => ( @@ -211,8 +210,6 @@ const renderDesktopTable = () => ( color: "$white", }} > - Total: {totalVotes} -

Yes ({yesCount}) | No ({noCount}) @@ -269,21 +266,22 @@ const renderDesktopTable = () => ( : "No"; return ( td": { padding: "$2" }, - }} - onClick={(e) => { - e.stopPropagation(); - setSelectedVoter(vote.voter); - }} - > + as="tr" + key={index} + css={{ + backgroundColor: "$neutral3", + cursor: "pointer", + position: "relative", + zIndex: 10, + "&:hover": { backgroundColor: "$neutral4" }, + "& > td": { padding: "$2" }, + }} + onClickCapture={(e) => { + e.stopPropagation(); + console.log("Card clicked:", vote.voter); + setSelectedVoter(vote.voter); + }} + > ( fontWeight: 700, fontSize: "$3", color: "$white", + marginTop: "$2", + marginBottom: "$2", }} > - Total: {totalVotes} -

Yes ({yesCount}) | No ({noCount}) @@ -407,35 +405,43 @@ const renderDesktopTable = () => ( : "No"; return ( { - e.stopPropagation(); - setSelectedVoter(vote.voter); - }} - > - - {vote.ensName || formatAddress(vote.voter)} + key={index} + css={{ + padding: "$4", + marginBottom: "$3", + cursor: "pointer", + position: "relative", + zIndex: 5, + backgroundColor: "$neutral3", + "&:hover": { backgroundColor: "$neutral4" }, + }} + onClickCapture={(e) => { + e.stopPropagation(); + console.log("Card clicked:", vote.voter); + setSelectedVoter(vote.voter); + }} +> + + {formatAddress(vote.ensName ?? "") || formatAddress(vote.voter)} - - - Support: - {" "} - - {supportText} - - + + + Support: + + + {supportText} + + + Weight: {" "} - {formatStake(parseFloat(vote.weight))} LPT ({calculateVotePercentage(vote.weight)}%) + {new Intl.NumberFormat("en-US", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }).format(parseFloat(vote.weight) / 1e18)}{" "} + LPT ({calculateVotePercentage(vote.weight)}%) @@ -450,7 +456,8 @@ const renderDesktopTable = () => ( target="_blank" css={{ color: "$blue9", textDecoration: "underline" }} onClick={(e) => e.stopPropagation()} - >Txn + > + Arbitrum Icon ) : ( N/A diff --git a/components/Votes/voterPopover.tsx b/components/Votes/voterPopover.tsx index 53e0812e..93cf753b 100644 --- a/components/Votes/voterPopover.tsx +++ b/components/Votes/voterPopover.tsx @@ -2,13 +2,17 @@ import React, { useState, useEffect, ReactNode } from "react"; import { createPortal } from "react-dom"; -import { utils } from "ethers"; -// import Image from "next/image"; -import ArbitrumIcon from "../../public/img/logos/arbitrum.svg"; import { ethers } from "ethers"; import { useQuery } from "@apollo/client"; -import { GET_PROPOSALS_BY_IDS, GET_PROPOSALS_VOTES } from "./queries"; import { ApolloClient, InMemoryCache } from "@apollo/client"; +import { + Box, + Button, + Flex, + Heading, + Text, +} from "@livepeer/design-system"; +import { GET_PROPOSALS_BY_IDS, GET_PROPOSALS_VOTES } from "./queries"; import { CONTRACT_ADDRESS, VOTECAST_TOPIC0, @@ -16,8 +20,6 @@ import { contractInterface, } from "./contracts"; - - interface Vote { endVote: number; description: string; @@ -182,7 +184,6 @@ const VoterPopover: React.FC = ({ voter, proposalId, onClose return rawVotes .map((v) => { const subgraphData = proposalsMap.get(v.proposalId); - const description = subgraphData?.description || "No description available."; const endVote = subgraphData?.voteEnd || 0; const title = description.split("\n")[0].replace(/^#\s*/, "") || "Unknown Proposal"; @@ -204,112 +205,109 @@ const VoterPopover: React.FC = ({ voter, proposalId, onClose const isLoading = logsLoading || proposalsLoading; - const getSupportClasses = (choiceID: string) => { + // Replace Tailwind-based support styling with Livepeer design tokens. + const getSupportStyles = (choiceID: string) => { switch (choiceID) { case "1": - return "text-green-500 font-semibold"; + return { color: "$green9", fontWeight: 600 }; case "0": - return "text-red-500 font-semibold"; + return { color: "$red9", fontWeight: 600 }; default: - return "text-yellow-500 font-semibold"; + return { color: "$yellow9", fontWeight: 600 }; } }; + // Helper to format addresses. const formatAddress = (addr: string): string => { if (!addr) return ""; - if (addr.endsWith(".xyz")) { return addr.length > 21 ? `${addr.slice(0, 6)}...${addr.slice(-6)}` : addr; } - if (addr.endsWith(".eth")) { return addr; } - return addr.length > 21 ? `${addr.slice(0, 6)}...${addr.slice(-6)}` : addr; }; const popoverContent = ( -
-
- - + + + {isLoading ? ( -
-
-
+ + Loading votes... + ) : votes.length > 0 ? ( votes.map((vote, index) => ( -
-

+ + {vote.proposalTitle} -

-

- Proposal ID: - - {formatAddress(vote.proposalId)} - -

-

- Support: - + + + Proposal ID: {formatAddress(vote.proposalId)} + + + Support:{" "} + {vote.choiceID === "1" ? "Yes" : vote.choiceID === "2" ? "Abstain" : "No"} - -

-

- Weight: + + + + Weight:{" "} {new Intl.NumberFormat("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2, - }).format(parseFloat(vote.weight) / 10 ** 18)}{" "} + }).format(parseFloat(vote.weight) / 1e18)}{" "} LPT -

-

- Reason: - {vote.reason || "No reason provided"} -

- -

- {vote.transactionHash ? ( - e.stopPropagation()} - > - {/* Txn */} - - ) : ( - N/A - )} -

-
+ + + Reason: {vote.reason || "No reason provided"} + +
)) ) : ( -

No votes found for this voter.

+ No votes found for this voter. )} -
-
+
+
); - return createPortal(popoverContent, document.body); }; export default VoterPopover; + function zeroPadValue(voter: string, length: number): string { return ethers.utils.hexlify(ethers.utils.zeroPad(voter, length)); } - diff --git a/pages/treasury/[proposal].tsx b/pages/treasury/[proposal].tsx index 7d5474bb..5d26b176 100644 --- a/pages/treasury/[proposal].tsx +++ b/pages/treasury/[proposal].tsx @@ -189,7 +189,10 @@ const Proposal = () => { const formatStake = (stake: number) => `${numeral(parseFloat(fromWei(stake.toString()))).format("0,0.[00]")} LPT`; - + const totalVotes = votes.filter(vote => + ["0", "1", "2"].includes(vote.choiceID) + ).length; + if (!proposal) { From 1e9ab0db1af69312256237091d5e14f6b264c8a4 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Thu, 3 Apr 2025 15:47:22 +0100 Subject: [PATCH 08/37] Fix --- pages/treasury/[proposal].tsx | 64 +++++++++++++++++------------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/pages/treasury/[proposal].tsx b/pages/treasury/[proposal].tsx index 5d26b176..e4847735 100644 --- a/pages/treasury/[proposal].tsx +++ b/pages/treasury/[proposal].tsx @@ -223,25 +223,25 @@ const Proposal = () => { Livepeer Explorer - Treasury - + - + @@ -288,8 +288,8 @@ const Proposal = () => { variant="primary" css={{ display: "flex", - mt: "$3", - mr: "$3", + marginTop: "$3", + marginRight: "$3", "@bp3": { display: "none", }, @@ -307,7 +307,7 @@ const Proposal = () => { display: "grid", gridGap: "$3", gridTemplateColumns: "100%", - mb: "$3", + marginBottom: "$3", "@bp2": { gridTemplateColumns: "repeat(auto-fit, minmax(128px, 1fr))", }, @@ -330,11 +330,11 @@ const Proposal = () => { } meta={ - + { { {formatPercent(proposal.votes.percent.voters)} } meta={ - + { @@ -483,10 +483,10 @@ const Proposal = () => { > {action.lptTransfer ? ( <> - + LPT Transfer: - + Receiver: @@ -513,7 +513,7 @@ const Proposal = () => { - + Amount: @@ -522,7 +522,7 @@ const Proposal = () => { display: "block", fontWeight: 600, color: "$white", - ml: "auto", + marginLeft: "auto", }} size="2" > @@ -532,10 +532,10 @@ const Proposal = () => { ) : ( <> - + Custom: - + Target: @@ -565,7 +565,7 @@ const Proposal = () => { - + Value: @@ -574,7 +574,7 @@ const Proposal = () => { display: "block", fontWeight: 600, color: "$white", - ml: "auto", + marginLeft: "auto", }} size="2" > @@ -583,7 +583,7 @@ const Proposal = () => { {action.functionName ? ( - + Function: @@ -592,7 +592,7 @@ const Proposal = () => { display: "block", fontWeight: 600, color: "$white", - ml: "auto", + marginLeft: "auto", maxWidth: "50%", textAlign: "right", }} @@ -604,7 +604,7 @@ const Proposal = () => { ) : ( <> - + Calldata: @@ -613,7 +613,7 @@ const Proposal = () => { display: "block", fontWeight: 600, color: "$white", - ml: "auto", + marginLeft: "auto", maxWidth: "50%", wordBreak: "break-all", textAlign: "right", @@ -632,9 +632,9 @@ const Proposal = () => { { position: "sticky", alignSelf: "flex-start", top: "$9", - mt: "$6", + marginTop: "$6", width: "25%", display: "flex", }, From b3cd4033a6f312512b825511ee334f10bdc0cfc1 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Thu, 3 Apr 2025 16:03:31 +0100 Subject: [PATCH 09/37] Voting Popover UI fix -Voting popover close now doesnt also close voting data -Voting Cards do not show over Navbar when cross. --- components/Votes/voteTable.tsx | 4 ++-- components/Votes/voterPopover.tsx | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx index a88258f4..b56083f5 100644 --- a/components/Votes/voteTable.tsx +++ b/components/Votes/voteTable.tsx @@ -272,7 +272,7 @@ const renderDesktopTable = () => ( backgroundColor: "$neutral3", cursor: "pointer", position: "relative", - zIndex: 10, + zIndex: 2, "&:hover": { backgroundColor: "$neutral4" }, "& > td": { padding: "$2" }, }} @@ -411,7 +411,7 @@ const renderDesktopTable = () => ( marginBottom: "$3", cursor: "pointer", position: "relative", - zIndex: 5, + zIndex: 2, backgroundColor: "$neutral3", "&:hover": { backgroundColor: "$neutral4" }, }} diff --git a/components/Votes/voterPopover.tsx b/components/Votes/voterPopover.tsx index 93cf753b..caadc57e 100644 --- a/components/Votes/voterPopover.tsx +++ b/components/Votes/voterPopover.tsx @@ -256,7 +256,10 @@ const VoterPopover: React.FC = ({ voter, proposalId, onClose position: "absolute", top: "$2", right: "$2", - }} onClick={onClose}> + }} onClick={(e) => { + e.stopPropagation(); + onClose(); + }}> Close {isLoading ? ( From 433756042259942edf03c79860a14c4416a6cc08 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Mon, 7 Apr 2025 20:05:22 +0100 Subject: [PATCH 10/37] Fetches most recent ENS name --- components/Votes/queries.tsx | 4 ++-- components/Votes/voteTable.tsx | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/components/Votes/queries.tsx b/components/Votes/queries.tsx index e2b61dcf..dde47999 100644 --- a/components/Votes/queries.tsx +++ b/components/Votes/queries.tsx @@ -47,8 +47,8 @@ export const GET_PROTOCOL_STATS = gql` export const ENS_QUERY = gql` query getENS($address: String!) { domains(where: { resolvedAddress: $address } - orderBy: createdAt - orderDirection: desc) { + orderBy: createdAt + orderDirection: asc) { name resolvedAddress { id diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx index b56083f5..547524a9 100644 --- a/components/Votes/voteTable.tsx +++ b/components/Votes/voteTable.tsx @@ -92,7 +92,6 @@ const fetchVotesFromInfura = async (proposalId: string): Promise => { }) .filter((vote) => vote.proposalId === proposalId); - // Fetch ENS names for unique voters const uniqueVoters = Array.from(new Set(decodedVotes.map((v) => v.voter))); const localEnsCache: { [address: string]: string } = {}; @@ -277,8 +276,10 @@ const renderDesktopTable = () => ( "& > td": { padding: "$2" }, }} onClickCapture={(e) => { - e.stopPropagation(); - console.log("Card clicked:", vote.voter); + const target = e.target as HTMLElement; + if (target.closest("a")) return; + + e.stopPropagation(); setSelectedVoter(vote.voter); }} > @@ -351,7 +352,13 @@ const renderDesktopTable = () => ( e.stopPropagation()} + onClickCapture={(e) => { + const target = e.target as HTMLElement; + if (target.closest("a")) return; + + e.stopPropagation(); + setSelectedVoter(vote.voter); + }} > Arbitrum Icon @@ -416,8 +423,10 @@ const renderDesktopTable = () => ( "&:hover": { backgroundColor: "$neutral4" }, }} onClickCapture={(e) => { + const target = e.target as HTMLElement; + if (target.closest("a")) return; + e.stopPropagation(); - console.log("Card clicked:", vote.voter); setSelectedVoter(vote.voter); }} > From 8b28694def2cddae669173d35a6ab884dc74e50b Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Tue, 8 Apr 2025 10:52:09 +0100 Subject: [PATCH 11/37] ENS name fix and link -ENS now fetches latest registrered ENS, no matter when it was created. Ensuring its the right one. -Arbicscan link no longer closes votes dropdown and onHover UI --- components/Votes/queries.tsx | 6 ++++-- components/Votes/voteTable.tsx | 30 ++++++++++++++++++++++-------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/components/Votes/queries.tsx b/components/Votes/queries.tsx index dde47999..978eac76 100644 --- a/components/Votes/queries.tsx +++ b/components/Votes/queries.tsx @@ -47,12 +47,14 @@ export const GET_PROTOCOL_STATS = gql` export const ENS_QUERY = gql` query getENS($address: String!) { domains(where: { resolvedAddress: $address } - orderBy: createdAt - orderDirection: asc) { + orderBy: registration__registrationDate + orderDirection: desc + ) { name resolvedAddress { id } + createdAt } } `; diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx index 547524a9..8e5efbe0 100644 --- a/components/Votes/voteTable.tsx +++ b/components/Votes/voteTable.tsx @@ -191,7 +191,7 @@ const VoteTable: React.FC = ({ const noCount = votes.filter((v) => v.choiceID === "0").length; const abstainCount = votes.filter((v) => v.choiceID === "2").length; - // Desktop Layout (Table-like) + // Desktop Layout (Table-based) const renderDesktopTable = () => ( ( css={{ textAlign: "center", borderBottom: "1px solid $neutral5", + position: "relative", + zIndex: 5, }} > {vote.transactionHash ? ( @@ -353,20 +355,32 @@ const renderDesktopTable = () => ( href={`https://arbiscan.io/tx/${vote.transactionHash}#eventlog`} target="_blank" onClickCapture={(e) => { - const target = e.target as HTMLElement; - if (target.closest("a")) return; - - e.stopPropagation(); - setSelectedVoter(vote.voter); + e.stopPropagation(); + }} + css={{ + display: "inline-block", + transition: "transform 0.2s ease", + zIndex: 9999, + position: "relative", + "&:hover": { + transform: "scale(1.3)", + zIndex: 9999, + }, }} > - Arbitrum Icon + Arbitrum Icon ) : ( N/A )} + ); })} @@ -376,7 +390,7 @@ const renderDesktopTable = () => ( ); - // Mobile Layout (Card-based) + // Mobile Layout (Card-based for better UI/UX) const renderMobileCards = () => ( Date: Tue, 8 Apr 2025 11:15:35 +0100 Subject: [PATCH 12/37] Loading Spinner, ENS and Popover --- components/Votes/voteTable.tsx | 9 +++++---- components/Votes/voterPopover.tsx | 6 ++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx index 8e5efbe0..7eccbdb9 100644 --- a/components/Votes/voteTable.tsx +++ b/components/Votes/voteTable.tsx @@ -3,6 +3,7 @@ import React, { useState, useEffect } from "react"; import { ApolloClient, InMemoryCache } from "@apollo/client"; import ArbitrumIcon from "../../public/img/logos/arbitrum.png"; +import Spinner from "@components/Spinner"; import Image from "next/image"; import { Box, @@ -51,7 +52,7 @@ const formatAddress = (addr: string): string => { if (addr.endsWith(".xyz")) { return addr.length > 21 ? `${addr.slice(0, 6)}...${addr.slice(-6)}` : addr; } - if (addr.endsWith(".eth")) { + if (addr.endsWith(".eth") && addr.length < 21) { return addr; } return addr.length > 21 ? `${addr.slice(0, 6)}...${addr.slice(-6)}` : addr; @@ -168,7 +169,7 @@ const VoteTable: React.FC = ({ height: "150px", }} > - Loading votes... + ); } @@ -371,8 +372,8 @@ const renderDesktopTable = () => ( Arbitrum Icon ) : ( diff --git a/components/Votes/voterPopover.tsx b/components/Votes/voterPopover.tsx index caadc57e..6e4f9b3c 100644 --- a/components/Votes/voterPopover.tsx +++ b/components/Votes/voterPopover.tsx @@ -4,6 +4,7 @@ import React, { useState, useEffect, ReactNode } from "react"; import { createPortal } from "react-dom"; import { ethers } from "ethers"; import { useQuery } from "@apollo/client"; +import Spinner from "@components/Spinner"; import { ApolloClient, InMemoryCache } from "@apollo/client"; import { Box, @@ -246,11 +247,12 @@ const VoterPopover: React.FC = ({ voter, proposalId, onClose padding: "$4", paddingTop: "$6", borderRadius: "$2", - maxHeight: "100%", + maxHeight: "90%", overflowY: "auto", width: "90%", "@bp2": { width: "50%" }, zIndex: 10, + marginTop: "$6", }}> {isLoading ? ( - Loading votes... + ) : votes.length > 0 ? ( votes.map((vote, index) => ( From 57284a871030f6f807efadf3c72eefb2aea373aa Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Thu, 10 Apr 2025 13:48:44 +0100 Subject: [PATCH 13/37] formatAddress --- components/Votes/formatAddress.tsx | 10 ++++++++++ components/Votes/voteTable.tsx | 11 +---------- components/Votes/voterPopover.tsx | 15 +++------------ 3 files changed, 14 insertions(+), 22 deletions(-) create mode 100644 components/Votes/formatAddress.tsx diff --git a/components/Votes/formatAddress.tsx b/components/Votes/formatAddress.tsx new file mode 100644 index 00000000..c0fadef5 --- /dev/null +++ b/components/Votes/formatAddress.tsx @@ -0,0 +1,10 @@ +export const formatAddress = (addr: string): string => { + if (!addr) return ""; + if (addr.endsWith(".xyz")) { + return addr.length > 21 ? `${addr.slice(0, 6)}...${addr.slice(-6)}` : addr; + } + if (addr.endsWith(".eth") && addr.length < 21) { + return addr; + } + return addr.length > 21 ? `${addr.slice(0, 6)}...${addr.slice(-6)}` : addr; + }; \ No newline at end of file diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx index 7eccbdb9..88ce709f 100644 --- a/components/Votes/voteTable.tsx +++ b/components/Votes/voteTable.tsx @@ -22,6 +22,7 @@ import { contractInterface, } from "./contracts"; import { ENS_QUERY } from "./queries"; +import { formatAddress } from "./formatAddress"; const createEnsApolloClient = () => new ApolloClient({ @@ -47,16 +48,6 @@ interface VoteTableProps { formatStake?: (stake: number) => string; } -const formatAddress = (addr: string): string => { - if (!addr) return ""; - if (addr.endsWith(".xyz")) { - return addr.length > 21 ? `${addr.slice(0, 6)}...${addr.slice(-6)}` : addr; - } - if (addr.endsWith(".eth") && addr.length < 21) { - return addr; - } - return addr.length > 21 ? `${addr.slice(0, 6)}...${addr.slice(-6)}` : addr; -}; const getSupportStyles = (choiceID: string) => { switch (choiceID) { diff --git a/components/Votes/voterPopover.tsx b/components/Votes/voterPopover.tsx index 6e4f9b3c..8bce68ae 100644 --- a/components/Votes/voterPopover.tsx +++ b/components/Votes/voterPopover.tsx @@ -5,6 +5,7 @@ import { createPortal } from "react-dom"; import { ethers } from "ethers"; import { useQuery } from "@apollo/client"; import Spinner from "@components/Spinner"; +import { formatAddress } from "./formatAddress"; import { ApolloClient, InMemoryCache } from "@apollo/client"; import { Box, @@ -21,6 +22,8 @@ import { contractInterface, } from "./contracts"; + + interface Vote { endVote: number; description: string; @@ -218,18 +221,6 @@ const VoterPopover: React.FC = ({ voter, proposalId, onClose } }; - // Helper to format addresses. - const formatAddress = (addr: string): string => { - if (!addr) return ""; - if (addr.endsWith(".xyz")) { - return addr.length > 21 ? `${addr.slice(0, 6)}...${addr.slice(-6)}` : addr; - } - if (addr.endsWith(".eth")) { - return addr; - } - return addr.length > 21 ? `${addr.slice(0, 6)}...${addr.slice(-6)}` : addr; - }; - const popoverContent = ( Date: Thu, 10 Apr 2025 16:24:17 +0100 Subject: [PATCH 14/37] Address for popover ENS to be added --- components/Votes/voteTable.tsx | 1 - components/Votes/voterPopover.tsx | 133 +++++++----------------------- 2 files changed, 28 insertions(+), 106 deletions(-) diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx index 88ce709f..646c4b5f 100644 --- a/components/Votes/voteTable.tsx +++ b/components/Votes/voteTable.tsx @@ -492,7 +492,6 @@ const renderDesktopTable = () => ( {selectedVoter && ( setSelectedVoter(null)} /> )} diff --git a/components/Votes/voterPopover.tsx b/components/Votes/voterPopover.tsx index 8bce68ae..ca2038c4 100644 --- a/components/Votes/voterPopover.tsx +++ b/components/Votes/voterPopover.tsx @@ -1,12 +1,11 @@ "use client"; -import React, { useState, useEffect, ReactNode } from "react"; +import React, { useState, useEffect } from "react"; import { createPortal } from "react-dom"; import { ethers } from "ethers"; import { useQuery } from "@apollo/client"; import Spinner from "@components/Spinner"; import { formatAddress } from "./formatAddress"; -import { ApolloClient, InMemoryCache } from "@apollo/client"; import { Box, Button, @@ -14,7 +13,7 @@ import { Heading, Text, } from "@livepeer/design-system"; -import { GET_PROPOSALS_BY_IDS, GET_PROPOSALS_VOTES } from "./queries"; +import { GET_PROPOSALS_BY_IDS } from "./queries"; import { CONTRACT_ADDRESS, VOTECAST_TOPIC0, @@ -22,8 +21,6 @@ import { contractInterface, } from "./contracts"; - - interface Vote { endVote: number; description: string; @@ -34,102 +31,22 @@ interface Vote { proposalId: string; reason: string; proposalTitle: string; - [x: string]: ReactNode; } interface VoterPopoverProps { voter: string; - proposalId: string; onClose: () => void; } -const clientInstance = new ApolloClient({ - uri: process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT, - cache: new InMemoryCache(), -}); - -const fetchProposalByIdGraphQL = async ( - proposalId: string -): Promise<{ title: string; endVote: number; description: string }> => { - try { - const { data } = await clientInstance.query({ query: GET_PROPOSALS_VOTES }); - const proposal = data.treasuryProposals.find((p: any) => p.id === proposalId); - - if (!proposal || !proposal.description || !proposal.voteEnd) { - console.error(`Proposal data missing for ID: ${proposalId}`, proposal); - return { - title: "Unknown Proposal", - endVote: 0, - description: "No description available.", - }; - } - - const title = proposal.description.split("\n")[0].replace(/^#\s*/, ""); - return { - title, - endVote: proposal.voteEnd, - description: proposal.description, - }; - } catch (error) { - console.error(`Error fetching proposal ${proposalId}:`, error); - return { title: "Unknown Proposal", endVote: 0, description: "No description available." }; - } -}; - -const fetchVotesByVoter = async (voter: string, proposalId: string): Promise => { - try { - const voterTopic = ethers.utils.zeroPad(voter, 32); - const logs = await provider.getLogs({ - address: CONTRACT_ADDRESS, - fromBlock: "earliest", - toBlock: "latest", - topics: [VOTECAST_TOPIC0, ethers.utils.hexlify(voterTopic)], - }); - - const proposalsMap = new Map(); - - const votesWithDetails = await Promise.all( - logs.map(async (log) => { - const decoded = contractInterface.parseLog(log); - const proposalIdFromLog = decoded?.args.proposalId.toString(); - - if (!proposalsMap.has(proposalIdFromLog)) { - const proposal = await fetchProposalByIdGraphQL(proposalIdFromLog); - proposalsMap.set(proposalIdFromLog, proposal); - } - - const proposal = proposalsMap.get(proposalIdFromLog); - - return { - transactionHash: log.transactionHash, - voter: decoded?.args.voter, - choiceID: decoded?.args.support.toString(), - proposalId: proposalIdFromLog, - weight: decoded?.args.weight.toString(), - reason: decoded?.args.reason, - endVote: proposal?.endVote || 0, - description: proposal?.description || "No description available.", - proposalTitle: proposal?.title || "Unknown Proposal", - }; - }) - ); - - return votesWithDetails.sort((a, b) => a.endVote - b.endVote); - } catch (error) { - console.error("Error fetching votes for voter:", error); - return []; - } -}; - -const VoterPopover: React.FC = ({ voter, proposalId, onClose }) => { +const VoterPopover: React.FC = ({ voter, onClose }) => { const [logsLoading, setLogsLoading] = useState(true); const [proposalIds, setProposalIds] = useState([]); const [rawVotes, setRawVotes] = useState([]); useEffect(() => { const fetchLogsForVoter = async () => { + setLogsLoading(true); try { - setLogsLoading(true); const voterTopic = ethers.utils.zeroPad(voter, 32); const logs = await provider.getLogs({ address: CONTRACT_ADDRESS, @@ -138,34 +55,34 @@ const VoterPopover: React.FC = ({ voter, proposalId, onClose topics: [VOTECAST_TOPIC0, ethers.utils.hexlify(voterTopic)], }); - const decodedVotes = logs.map((log) => { + const decodedVotes: any[] = []; + const proposalIdsSet = new Set(); + + logs.forEach((log) => { const decoded = contractInterface.parseLog(log); - return { + const proposalId = decoded?.args.proposalId.toString(); + decodedVotes.push({ transactionHash: log.transactionHash, voter: decoded?.args.voter, - proposalId: decoded?.args.proposalId.toString(), + proposalId, choiceID: decoded?.args.support.toString(), weight: decoded?.args.weight.toString(), reason: decoded?.args.reason, - }; + }); + proposalIdsSet.add(proposalId); }); setRawVotes(decodedVotes); - - const uniqueProposalIds = Array.from( - new Set(decodedVotes.map((v) => v.proposalId)) - ); - - setProposalIds(uniqueProposalIds); - setLogsLoading(false); + setProposalIds(Array.from(proposalIdsSet)); } catch (error) { console.error("Error fetching logs for voter:", error); + } finally { setLogsLoading(false); } }; fetchLogsForVoter(); - }, [voter, proposalId]); + }, [voter]); const { data, loading: proposalsLoading } = useQuery(GET_PROPOSALS_BY_IDS, { variables: { ids: proposalIds }, @@ -173,7 +90,7 @@ const VoterPopover: React.FC = ({ voter, proposalId, onClose }); const votes: Vote[] = React.useMemo(() => { - if (logsLoading || proposalsLoading || !rawVotes) return []; + if (logsLoading || proposalsLoading) return []; const proposalsMap = new Map(); if (data?.treasuryProposals) { @@ -209,7 +126,6 @@ const VoterPopover: React.FC = ({ voter, proposalId, onClose const isLoading = logsLoading || proposalsLoading; - // Replace Tailwind-based support styling with Livepeer design tokens. const getSupportStyles = (choiceID: string) => { switch (choiceID) { case "1": @@ -255,6 +171,17 @@ const VoterPopover: React.FC = ({ voter, proposalId, onClose }}> Close + + {/* Add ENS for address */} + Voting History for {formatAddress(voter)} + {isLoading ? ( @@ -303,7 +230,3 @@ const VoterPopover: React.FC = ({ voter, proposalId, onClose }; export default VoterPopover; - -function zeroPadValue(voter: string, length: number): string { - return ethers.utils.hexlify(ethers.utils.zeroPad(voter, length)); -} From 48da46b3bad58766b0a25d448829780a65cb2138 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:17:45 +0100 Subject: [PATCH 15/37] Removed on popover --- components/Votes/voterPopover.tsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/components/Votes/voterPopover.tsx b/components/Votes/voterPopover.tsx index ca2038c4..e58ff228 100644 --- a/components/Votes/voterPopover.tsx +++ b/components/Votes/voterPopover.tsx @@ -171,17 +171,6 @@ const VoterPopover: React.FC = ({ voter, onClose }) => { }}> Close - - {/* Add ENS for address */} - Voting History for {formatAddress(voter)} - {isLoading ? ( From 0334e3a0f7a33d34e2e361dc75a6eb402b136660 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:03:13 +0100 Subject: [PATCH 16/37] remove qodo --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index cf33238d..27222722 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,3 @@ yarn-error.log* .env.development.local .env.test.local .env.production.local -.qodo From b8904acedffc87e9aa1220e8a172a5c716cc53cc Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:05:17 +0100 Subject: [PATCH 17/37] .env.example --- .env.example | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..54b558a8 --- /dev/null +++ b/.env.example @@ -0,0 +1,17 @@ +CHANGEFEED_ACCESS_TOKEN= +GITHUB_ACCESS_TOKEN= +LIVEPEER_COM_API_ADMIN_TOKEN= +API_TOKEN= +PINATA_JWT= + +NEXT_PUBLIC_ETHERSCAN_API_KEY= +NEXT_PUBLIC_GA_TRACKING_ID= +NEXT_PUBLIC_NETWORK=ARBITRUM_ONE +NEXT_PUBLIC_INFURA_KEY= +NEXT_PUBLIC_L1_RPC_URL= +NEXT_PUBLIC_L2_RPC_URL= +NEXT_PUBLIC_GITHUB_LIP_NAMESPACE=adamsoffer +NEXT_PUBLIC_SUBGRAPH_API_KEY= +NEXT_PUBLIC_SUBGRAPH_ID=FE63YgkzcpVocxdCEyEYbvjYqEf2kb1A6daMYRxmejYC +NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID= +NEXT_PUBLIC_METRICS_SERVER_URL=https://livepeer-leaderboard-serverless.vercel.app From 64ae030b2ab14bb526db5aaf4a3161469a252326 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Fri, 18 Apr 2025 22:04:49 +0100 Subject: [PATCH 18/37] queries, fetch votes, fomratAddress, chains moved to retrespective place within codebase --- components/Votes/contracts.tsx | 14 -------------- components/Votes/voteTable.tsx | 6 +++--- components/Votes/voterPopover.tsx | 6 +++--- .../Votes/fetchVotes.tsx => hooks/fetchVotes.ts | 4 ++-- lib/chains.ts | 16 ++++++++++++++++ pages/treasury/[proposal].tsx | 2 +- .../queries.tsx => queries/treasuryProposals.ts | 0 .../formatAddress.tsx => utils/formatAddress.ts | 0 8 files changed, 25 insertions(+), 23 deletions(-) delete mode 100644 components/Votes/contracts.tsx rename components/Votes/fetchVotes.tsx => hooks/fetchVotes.ts (96%) rename components/Votes/queries.tsx => queries/treasuryProposals.ts (100%) rename components/Votes/formatAddress.tsx => utils/formatAddress.ts (100%) diff --git a/components/Votes/contracts.tsx b/components/Votes/contracts.tsx deleted file mode 100644 index d4a3b929..00000000 --- a/components/Votes/contracts.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { ethers } from "ethers"; - -export const INFURA_RPC_URL = `https://arbitrum-mainnet.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_KEY}`; -export const CONTRACT_ADDRESS = "0xcfe4e2879b786c3aa075813f0e364bb5accb6aa0"; - -export const VOTECAST_TOPIC0 = ethers.utils.id( - "VoteCast(address,uint256,uint8,uint256,string)" -); - -export const provider = new ethers.providers.JsonRpcProvider(INFURA_RPC_URL); - -export const contractInterface = new ethers.utils.Interface([ - "event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason)", -]); diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx index 646c4b5f..84a016bc 100644 --- a/components/Votes/voteTable.tsx +++ b/components/Votes/voteTable.tsx @@ -20,9 +20,9 @@ import { VOTECAST_TOPIC0, provider, contractInterface, -} from "./contracts"; -import { ENS_QUERY } from "./queries"; -import { formatAddress } from "./formatAddress"; +} from "@lib/chains";; +import { ENS_QUERY } from "../../queries/TreasuryProposals"; +import { formatAddress } from "../../utils/formatAddress"; const createEnsApolloClient = () => new ApolloClient({ diff --git a/components/Votes/voterPopover.tsx b/components/Votes/voterPopover.tsx index e58ff228..47b40d06 100644 --- a/components/Votes/voterPopover.tsx +++ b/components/Votes/voterPopover.tsx @@ -5,7 +5,7 @@ import { createPortal } from "react-dom"; import { ethers } from "ethers"; import { useQuery } from "@apollo/client"; import Spinner from "@components/Spinner"; -import { formatAddress } from "./formatAddress"; +import { formatAddress } from "../../utils/formatAddress"; import { Box, Button, @@ -13,13 +13,13 @@ import { Heading, Text, } from "@livepeer/design-system"; -import { GET_PROPOSALS_BY_IDS } from "./queries"; +import { GET_PROPOSALS_BY_IDS } from "../../queries/TreasuryProposals"; import { CONTRACT_ADDRESS, VOTECAST_TOPIC0, provider, contractInterface, -} from "./contracts"; +} from "@lib/chains"; interface Vote { endVote: number; diff --git a/components/Votes/fetchVotes.tsx b/hooks/fetchVotes.ts similarity index 96% rename from components/Votes/fetchVotes.tsx rename to hooks/fetchVotes.ts index 8beaec31..430a3ada 100644 --- a/components/Votes/fetchVotes.tsx +++ b/hooks/fetchVotes.ts @@ -5,11 +5,11 @@ import { VOTECAST_TOPIC0, provider, contractInterface, -} from "./contracts"; +} from "@lib/chains"; import { ENS_QUERY -} from "./queries"; +} from "../queries/TreasuryProposals"; const ensClient = new ApolloClient({ uri: process.env.NEXT_PUBLIC_ENS_API_URI, diff --git a/lib/chains.ts b/lib/chains.ts index 0685ffd9..ee9f786a 100644 --- a/lib/chains.ts +++ b/lib/chains.ts @@ -255,6 +255,22 @@ export const l2Provider = new ethers.providers.JsonRpcProvider( INFURA_NETWORK_URLS[DEFAULT_CHAIN_ID] ); + +// Votecast Constants +export const INFURA_RPC_URL = `https://arbitrum-mainnet.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_KEY}`; +export const CONTRACT_ADDRESS = "0xcfe4e2879b786c3aa075813f0e364bb5accb6aa0"; + +export const VOTECAST_TOPIC0 = ethers.utils.id( + "VoteCast(address,uint256,uint8,uint256,string)" +); + +export const provider = new ethers.providers.JsonRpcProvider(INFURA_RPC_URL); + +export const contractInterface = new ethers.utils.Interface([ + "event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason)", +]); + + export function isL2ChainId(chainId: number | undefined): boolean { return L2_CHAIN_IDS.some((e) => e.id === chainId); } diff --git a/pages/treasury/[proposal].tsx b/pages/treasury/[proposal].tsx index e4847735..796a2967 100644 --- a/pages/treasury/[proposal].tsx +++ b/pages/treasury/[proposal].tsx @@ -6,7 +6,7 @@ import MarkdownRenderer from "@components/MarkdownRenderer"; import BottomDrawer from "@components/BottomDrawer"; import Spinner from "@components/Spinner"; import Stat from "@components/Stat"; -import { fetchVotesFromInfura } from "@components/Votes/fetchVotes"; +import { fetchVotesFromInfura } from "../../hooks/fetchVotes"; import { Badge, Box, diff --git a/components/Votes/queries.tsx b/queries/treasuryProposals.ts similarity index 100% rename from components/Votes/queries.tsx rename to queries/treasuryProposals.ts diff --git a/components/Votes/formatAddress.tsx b/utils/formatAddress.ts similarity index 100% rename from components/Votes/formatAddress.tsx rename to utils/formatAddress.ts From eafc541f3985d905b0e248c85ecb965153ec3f35 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Fri, 25 Apr 2025 15:49:21 +0100 Subject: [PATCH 19/37] cleanup: factor out duplicate vote so reusable between voting components --- components/Votes/desktopVoteTable.tsx | 84 +++++ components/Votes/mobileVoteCards.tsx | 64 ++++ components/Votes/types.ts | 15 + components/Votes/voteTable.tsx | 510 ++------------------------ components/Votes/voterPopover.tsx | 2 +- hooks/fetchVotes.ts | 85 +++-- hooks/useVotes.ts | 32 ++ 7 files changed, 281 insertions(+), 511 deletions(-) create mode 100644 components/Votes/desktopVoteTable.tsx create mode 100644 components/Votes/mobileVoteCards.tsx create mode 100644 components/Votes/types.ts create mode 100644 hooks/useVotes.ts diff --git a/components/Votes/desktopVoteTable.tsx b/components/Votes/desktopVoteTable.tsx new file mode 100644 index 00000000..952c8740 --- /dev/null +++ b/components/Votes/desktopVoteTable.tsx @@ -0,0 +1,84 @@ +import React from 'react'; +import Image from 'next/image'; +import { Box, Flex, Text, Link } from '@livepeer/design-system'; +import ArbitrumIcon from '../../public/img/logos/arbitrum.png'; +import { formatAddress } from '../../utils/formatAddress'; +import { Vote, SUPPORT } from './types'; + +interface DesktopVoteTableProps { + votes: Vote[]; + counts: { yes: number; no: number; abstain: number }; + formatWeight: (weight: string) => string; + onSelect: (voter: string) => void; +} + + +export const DesktopVoteTable: React.FC = ({ votes, counts, formatWeight, onSelect }) => ( + + + Yes ({counts.yes}) + | + No ({counts.no}) + | + Abstain ({counts.abstain}) + + + + + {['Voter', 'Support', 'Weight', 'Reason', 'Vote Txn'].map(label => ( + + {label} + + ))} + + + + {votes.map(vote => { + const support = SUPPORT[vote.choiceID] || SUPPORT['2']; + return ( + td': { padding: '$2' } }} + onClickCapture={e => { if ((e.target as HTMLElement).closest('a')) return; e.stopPropagation(); onSelect(vote.voter); }} + > + + e.stopPropagation()} + > + {formatAddress(vote.ensName ?? '') || formatAddress(vote.voter)} + + + + {support.text} + + + {formatWeight(vote.weight)} + + + {vote.reason} + + + {vote.transactionHash ? ( + e.stopPropagation()} + css={{ display: 'inline-block', transition: 'transform 0.2s ease', zIndex: 9999, position: 'relative', '&:hover': { transform: 'scale(1.3)', zIndex: 9999 } }} + > + Arbitrum Icon + + ) : ( + N/A + )} + + + ); + })} + + + +); \ No newline at end of file diff --git a/components/Votes/mobileVoteCards.tsx b/components/Votes/mobileVoteCards.tsx new file mode 100644 index 00000000..de497082 --- /dev/null +++ b/components/Votes/mobileVoteCards.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import Image from 'next/image'; +import { Card, Flex, Heading, Link, Text, Box } from '@livepeer/design-system'; +import ArbitrumIcon from '../../public/img/logos/arbitrum.png'; +import { formatAddress } from '../../utils/formatAddress'; +import { Vote, SUPPORT } from './types'; + +interface MobileVoteCardsProps { + votes: Vote[]; + counts: { yes: number; no: number; abstain: number }; + formatWeight: (weight: string) => string; + onSelect: (voter: string) => void; +} + + +export const MobileVoteCards: React.FC = ({ votes, counts, formatWeight, onSelect }) => ( + + + Yes ({counts.yes}) + | + No ({counts.no}) + | + Abstain ({counts.abstain}) + + {votes.map(vote => { + const support = SUPPORT[vote.choiceID] || SUPPORT['2']; + return ( + { if ((e.target as HTMLElement).closest('a')) return; e.stopPropagation(); onSelect(vote.voter); }} + > + + {formatAddress(vote.ensName ?? '') || formatAddress(vote.voter)} + + + Support: + {support.text} + + + Weight: {formatWeight(vote.weight)} + + + Reason: {vote.reason} + + + {vote.transactionHash ? ( + e.stopPropagation()} + > + Arbitrum Icon + + ) : ( + N/A + )} + + + ); + })} + +); \ No newline at end of file diff --git a/components/Votes/types.ts b/components/Votes/types.ts new file mode 100644 index 00000000..808aab96 --- /dev/null +++ b/components/Votes/types.ts @@ -0,0 +1,15 @@ +export interface Vote { + transactionHash?: string; + weight: string; + voter: string; + choiceID: string; + proposalId: string; + reason: string; + ensName?: string; + } + + export const SUPPORT = { + '0': { text: 'No', style: { color: '$red9', fontWeight: 600 } }, + '1': { text: 'Yes', style: { color: '$green9', fontWeight: 600 } }, + '2': { text: 'Abstain',style: { color: '$yellow9', fontWeight: 600 } }, + } as const \ No newline at end of file diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx index 84a016bc..e796a625 100644 --- a/components/Votes/voteTable.tsx +++ b/components/Votes/voteTable.tsx @@ -1,500 +1,62 @@ "use client"; -import React, { useState, useEffect } from "react"; -import { ApolloClient, InMemoryCache } from "@apollo/client"; -import ArbitrumIcon from "../../public/img/logos/arbitrum.png"; -import Spinner from "@components/Spinner"; -import Image from "next/image"; -import { - Box, - Card, - Flex, - Heading, - Link, - Text, -} from "@livepeer/design-system"; -import VoterPopover from "./voterPopover"; - -import { - CONTRACT_ADDRESS, - VOTECAST_TOPIC0, - provider, - contractInterface, -} from "@lib/chains";; -import { ENS_QUERY } from "../../queries/TreasuryProposals"; -import { formatAddress } from "../../utils/formatAddress"; - -const createEnsApolloClient = () => - new ApolloClient({ - uri: process.env.NEXT_PUBLIC_ENS_API_URI, - cache: new InMemoryCache(), - }); - -interface Vote { - transactionHash?: string; - weight: string; - voter: string; - choiceID: string; - proposalId: string; - reason: string; - ensName?: string; -} +import React, { useState, useMemo, useCallback } from 'react'; +import Spinner from '@components/Spinner'; +import { useVotes } from '../../hooks/useVotes'; +import { DesktopVoteTable } from './desktopVoteTable'; +import { MobileVoteCards } from './mobileVoteCards'; +import VoterPopover from './voterPopover'; +import { Text, Flex } from '@livepeer/design-system'; interface VoteTableProps { proposalId: string; - proposalTitle: string; - ensCache?: any; - votes: { voter: string; weight: string; choiceID: string }[]; - formatStake?: (stake: number) => string; } +const lptFormatter = new Intl.NumberFormat('en-US', { + minimumFractionDigits: 2, + maximumFractionDigits: 2, +}); -const getSupportStyles = (choiceID: string) => { - switch (choiceID) { - case "1": - return { color: "$green9", fontWeight: 600 }; - case "0": - return { color: "$red9", fontWeight: 600 }; - default: - return { color: "$yellow9", fontWeight: 600 }; - } -}; - -const fetchVotesFromInfura = async (proposalId: string): Promise => { - const ensClient = createEnsApolloClient(); - try { - const logs = await provider.getLogs({ - address: CONTRACT_ADDRESS, - fromBlock: "earliest", - toBlock: "latest", - topics: [VOTECAST_TOPIC0], - }); - - const decodedVotes = logs - .map((log) => { - const decoded = contractInterface.parseLog(log); - return { - transactionHash: log.transactionHash, - voter: decoded?.args.voter.toLowerCase() || "", - choiceID: decoded?.args.support.toString() || "", - proposalId: decoded?.args.proposalId.toString() || "", - weight: decoded?.args.weight.toString() || "0", - reason: decoded?.args.reason || "No reason provided", - }; - }) - .filter((vote) => vote.proposalId === proposalId); - - const uniqueVoters = Array.from(new Set(decodedVotes.map((v) => v.voter))); - const localEnsCache: { [address: string]: string } = {}; - - await Promise.all( - uniqueVoters.map(async (address) => { - try { - const { data } = await ensClient.query({ - query: ENS_QUERY, - variables: { address }, - }); - if (data?.domains?.length > 0) { - localEnsCache[address] = data.domains[0].name; - } - } catch (e) { - console.warn(`Failed to fetch ENS for ${address}`, e); - } - }) - ); - - return decodedVotes.map((vote) => ({ - ...vote, - ensName: localEnsCache[vote.voter] || formatAddress(vote.voter), - })); - } catch (error) { - console.error("Error fetching logs from Infura:", error); - return []; - } -}; - -const VoteTable: React.FC = ({ - proposalId, - proposalTitle, - formatStake = (stake: number) => - new Intl.NumberFormat("en-US", { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }).format(stake / 1e18), -}) => { - const [votes, setVotes] = useState([]); - const [loading, setLoading] = useState(true); +export const VoteTable: React.FC = ({ proposalId }) => { + const { votes, loading } = useVotes(proposalId); const [selectedVoter, setSelectedVoter] = useState(null); - useEffect(() => { - if (!proposalId) return; - const loadVotes = async () => { - setLoading(true); - const fetchedVotes = await fetchVotesFromInfura(proposalId); - const sorted = fetchedVotes.sort( - (a, b) => parseFloat(b.weight) - parseFloat(a.weight) - ); - setVotes(sorted); - setLoading(false); - }; - loadVotes(); - }, [proposalId]); + const counts = { + yes: votes.filter(v => v.choiceID === '1').length, + no: votes.filter(v => v.choiceID === '0').length, + abstain: votes.filter(v => v.choiceID === '2').length, + }; const totalWeight = votes.reduce( - (sum, vote) => sum + parseFloat(vote.weight), + (sum, v) => sum + parseFloat(v.weight), 0 ); - const calculateVotePercentage = (weight: string) => { - return totalWeight > 0 - ? ((parseFloat(weight) / totalWeight) * 100).toFixed(2) - : "0"; - }; - - if (loading) { - return ( - - - - ); - } - - if (votes.length === 0) { - return ( - - No votes found for this proposal. - - ); - } - - const yesCount = votes.filter((v) => v.choiceID === "1").length; - const noCount = votes.filter((v) => v.choiceID === "0").length; - const abstainCount = votes.filter((v) => v.choiceID === "2").length; - - // Desktop Layout (Table-based) -const renderDesktopTable = () => ( - - - Yes ({yesCount}) - | - No ({noCount}) - | - - Abstain ({abstainCount}) - - - - - - {[ - { label: "Voter", width: "auto" }, - { label: "Support", width: "auto" }, - { label: "Weight", width: "30%" }, - { label: "Reason", width: "10%" }, - { label: "Vote Txn", width: "auto" }, - ].map((header) => ( - - {header.label} - - ))} - - - - {votes.map((vote, index) => { - const supportText = - vote.choiceID === "1" - ? "Yes" - : vote.choiceID === "2" - ? "Abstain" - : "No"; - return ( - td": { padding: "$2" }, - }} - onClickCapture={(e) => { - const target = e.target as HTMLElement; - if (target.closest("a")) return; - - e.stopPropagation(); - setSelectedVoter(vote.voter); - }} - > - - e.stopPropagation()} - > - {formatAddress(vote.ensName ?? "") || formatAddress(vote.voter)} - - - - {supportText} - - - {new Intl.NumberFormat("en-US", { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }).format(parseFloat(vote.weight) / 1e18)}{" "} - LPT ({calculateVotePercentage(vote.weight)}%) - - - {vote.reason} - - - - {vote.transactionHash ? ( - { - e.stopPropagation(); - }} - css={{ - display: "inline-block", - transition: "transform 0.2s ease", - zIndex: 9999, - position: "relative", - "&:hover": { - transform: "scale(1.3)", - zIndex: 9999, - }, - }} - > - Arbitrum Icon - - ) : ( - N/A - )} - - - - - ); - })} - - - + const formatWeight = useCallback( + (w: string) => + `${lptFormatter.format(parseFloat(w) / 1e18)} LPT (${ + totalWeight > 0 + ? ((parseFloat(w) / totalWeight) * 100).toFixed(2) + : '0' + }%)`, + [totalWeight] ); - - // Mobile Layout (Card-based for better UI/UX) - const renderMobileCards = () => ( - - - Yes ({yesCount}) - | - No ({noCount}) - | - - Abstain ({abstainCount}) - - - {votes.map((vote, index) => { - const supportText = - vote.choiceID === "1" - ? "Yes" - : vote.choiceID === "2" - ? "Abstain" - : "No"; - return ( - { - const target = e.target as HTMLElement; - if (target.closest("a")) return; - - e.stopPropagation(); - setSelectedVoter(vote.voter); - }} -> - - {formatAddress(vote.ensName ?? "") || formatAddress(vote.voter)} - - - - Support: - - - {supportText} - - - - - - Weight: - {" "} - {new Intl.NumberFormat("en-US", { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }).format(parseFloat(vote.weight) / 1e18)}{" "} - LPT ({calculateVotePercentage(vote.weight)}%) - - - - Reason: - {" "} - {vote.reason} - - - {vote.transactionHash ? ( - e.stopPropagation()} - > - Arbitrum Icon - - ) : ( - N/A - )} - - + if (loading) return ( + ); -})} - + if (!votes.length) return ( + + No votes found for this proposal. + ); return ( <> - {renderDesktopTable()} - {renderMobileCards()} - {selectedVoter && ( - setSelectedVoter(null)} - /> - )} + + + {selectedVoter && setSelectedVoter(null)} />} ); }; diff --git a/components/Votes/voterPopover.tsx b/components/Votes/voterPopover.tsx index 47b40d06..0b22aa65 100644 --- a/components/Votes/voterPopover.tsx +++ b/components/Votes/voterPopover.tsx @@ -13,7 +13,7 @@ import { Heading, Text, } from "@livepeer/design-system"; -import { GET_PROPOSALS_BY_IDS } from "../../queries/TreasuryProposals"; +import { GET_PROPOSALS_BY_IDS } from "../../queries/treasuryProposals"; import { CONTRACT_ADDRESS, VOTECAST_TOPIC0, diff --git a/hooks/fetchVotes.ts b/hooks/fetchVotes.ts index 430a3ada..9b0e3b9d 100644 --- a/hooks/fetchVotes.ts +++ b/hooks/fetchVotes.ts @@ -1,5 +1,5 @@ -import { ethers } from "ethers"; -import { gql, ApolloClient, InMemoryCache } from "@apollo/client"; +import { ApolloClient, InMemoryCache } from "@apollo/client"; +import { formatAddress } from "../utils/formatAddress"; import { CONTRACT_ADDRESS, VOTECAST_TOPIC0, @@ -9,60 +9,73 @@ import { import { ENS_QUERY -} from "../queries/TreasuryProposals"; +} from "../queries/treasuryProposals"; -const ensClient = new ApolloClient({ - uri: process.env.NEXT_PUBLIC_ENS_API_URI, - cache: new InMemoryCache(), -}); +interface Vote { + transactionHash?: string; + weight: string; + voter: string; + choiceID: string; + proposalId: string; + reason: string; + ensName?: string; +} -export const fetchVotesFromInfura = async (proposalId: string) => { - try { -const currentBlock = await provider.getBlockNumber(); -const logs = await provider.getLogs({ - address: CONTRACT_ADDRESS, - fromBlock: currentBlock - 10000, - toBlock: "latest", - topics: [VOTECAST_TOPIC0], -}); +const createEnsApolloClient = () => + new ApolloClient({ + uri: process.env.NEXT_PUBLIC_ENS_API_URI, + cache: new InMemoryCache(), + }); +export const fetchVotesFromInfura = async (proposalId: string): Promise => { + const ensClient = createEnsApolloClient(); + try { + const logs = await provider.getLogs({ + address: CONTRACT_ADDRESS, + fromBlock: "earliest", + toBlock: "latest", + topics: [VOTECAST_TOPIC0], + }); - const votes = logs + const decodedVotes = logs .map((log) => { const decoded = contractInterface.parseLog(log); return { transactionHash: log.transactionHash, - voter: decoded?.args.voter.toLowerCase() || "", + voter: decoded?.args.voter.toLowerCase() || "", choiceID: decoded?.args.support.toString() || "", proposalId: decoded?.args.proposalId.toString() || "", - weight: ethers.utils.formatUnits(decoded?.args.weight.toString() || "0", 18), + weight: decoded?.args.weight.toString() || "0", reason: decoded?.args.reason || "No reason provided", }; }) .filter((vote) => vote.proposalId === proposalId); - if (votes.length === 0) return votes; - - const uniqueVoters = Array.from(new Set(votes.map((vote) => vote.voter))); + const uniqueVoters = Array.from(new Set(decodedVotes.map((v) => v.voter))); + const localEnsCache: { [address: string]: string } = {}; - const { data } = await ensClient.query({ - query: ENS_QUERY, - variables: { addresses: uniqueVoters }, - }); - - const ensMap: { [key: string]: string } = {}; - if (data?.domains) { - data.domains.forEach((domain: { resolvedAddress: { id: string }; name: string }) => { - ensMap[domain.resolvedAddress.id.toLowerCase()] = domain.name; - }); - } + await Promise.all( + uniqueVoters.map(async (address) => { + try { + const { data } = await ensClient.query({ + query: ENS_QUERY, + variables: { address }, + }); + if (data?.domains?.length > 0) { + localEnsCache[address] = data.domains[0].name; + } + } catch (e) { + console.warn(`Failed to fetch ENS for ${address}`, e); + } + }) + ); - return votes.map((vote) => ({ + return decodedVotes.map((vote) => ({ ...vote, - voter: ensMap[vote.voter] || vote.voter, + ensName: localEnsCache[vote.voter] || formatAddress(vote.voter), })); } catch (error) { - console.log("Error fetching logs from Infura:", error); + console.error("Error fetching logs from Infura:", error); return []; } }; \ No newline at end of file diff --git a/hooks/useVotes.ts b/hooks/useVotes.ts new file mode 100644 index 00000000..a4a2c35a --- /dev/null +++ b/hooks/useVotes.ts @@ -0,0 +1,32 @@ +import { useState, useEffect } from 'react'; +import { fetchVotesFromInfura } from './fetchVotes'; + +interface Vote { + transactionHash?: string; + weight: string; + voter: string; + choiceID: string; + proposalId: string; + reason: string; + ensName?: string; + } + +export function useVotes(proposalId: string) { + const [votes, setVotes] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + if (!proposalId) return; + let cancelled = false; + setLoading(true); + fetchVotesFromInfura(proposalId) + .then(fetched => + !cancelled && + setVotes(fetched.sort((a,b) => parseFloat(b.weight) - parseFloat(a.weight))) + ) + .finally(() => !cancelled && setLoading(false)); + return () => { cancelled = true }; + }, [proposalId]); + + return { votes, loading }; +} From b5c5b4226e193b2fc156214700facfd11f722ece Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Fri, 25 Apr 2025 21:30:38 +0100 Subject: [PATCH 20/37] Reduced duplicated code and fixed UX issue. -Moved duplicated code and import it into multiple components that use it. -Desktop and mobile votes split and will lazy load to optimise perfomance of loading votes for user. -Fixed UX issue that I found. The voter votes history on click off would close all vote list not just popover. Also improved some UI for close button along with logic to only close popover. --- components/Votes/types.ts | 31 ++-- components/Votes/useVoterVotes.ts | 65 ++++++++ components/Votes/voteDetailItem.tsx | 91 +++++++++++ components/Votes/voteModal.tsx | 73 +++++++++ components/Votes/voterPopover.tsx | 234 +++------------------------- 5 files changed, 272 insertions(+), 222 deletions(-) create mode 100644 components/Votes/useVoterVotes.ts create mode 100644 components/Votes/voteDetailItem.tsx create mode 100644 components/Votes/voteModal.tsx diff --git a/components/Votes/types.ts b/components/Votes/types.ts index 808aab96..6500a17a 100644 --- a/components/Votes/types.ts +++ b/components/Votes/types.ts @@ -1,15 +1,20 @@ export interface Vote { - transactionHash?: string; - weight: string; - voter: string; - choiceID: string; - proposalId: string; - reason: string; - ensName?: string; - } + transactionHash?: string; + voter: string; + choiceID: string; + proposalId: string; + weight: string; + reason: string; + ensName?: string; + endVote?: number; + description?: string; + proposalTitle?: string; +} - export const SUPPORT = { - '0': { text: 'No', style: { color: '$red9', fontWeight: 600 } }, - '1': { text: 'Yes', style: { color: '$green9', fontWeight: 600 } }, - '2': { text: 'Abstain',style: { color: '$yellow9', fontWeight: 600 } }, - } as const \ No newline at end of file +export const SUPPORT = { + '0': { text: 'No', style: { color: '$red9', fontWeight: 600 } }, + '1': { text: 'Yes', style: { color: '$green9', fontWeight: 600 } }, + '2': { text: 'Abstain',style: { color: '$yellow9', fontWeight: 600 } }, +} as const; + +export type SupportKey = keyof typeof SUPPORT; \ No newline at end of file diff --git a/components/Votes/useVoterVotes.ts b/components/Votes/useVoterVotes.ts new file mode 100644 index 00000000..c336c8c5 --- /dev/null +++ b/components/Votes/useVoterVotes.ts @@ -0,0 +1,65 @@ +// votes/useVoterVotes.ts +import { useState, useEffect, useMemo } from 'react' +import { provider, VOTECAST_TOPIC0, contractInterface, CONTRACT_ADDRESS } from '@lib/chains' +import { useQuery } from '@apollo/client' +import { ethers } from "ethers"; +import { GET_PROPOSALS_BY_IDS } from '../../queries/treasuryProposals' +import { Vote } from './types' + +export function useVoterVotes(voter: string) { + const [logsLoading, setLogsLoading] = useState(true) + const [rawVotes, setRawVotes] = useState([]) + const proposalIds = useMemo(() => Array.from(new Set(rawVotes.map(v => v.proposalId))), [rawVotes]) + + useEffect(() => { + let cancelled = false + async function fetch() { + setLogsLoading(true) + try { + const topic = ethers.utils.zeroPad(voter, 32) + const logs = await provider.getLogs({ address: CONTRACT_ADDRESS, fromBlock:'earliest', toBlock:'latest', topics: [VOTECAST_TOPIC0, ethers.utils.hexlify(topic)] }) + if (cancelled) return + const decoded = logs.map(log => { + const args = contractInterface.parseLog(log).args + return { + transactionHash: log.transactionHash, + voter: args.voter, + choiceID: args.support.toString(), + proposalId: args.proposalId.toString(), + weight: args.weight.toString(), + reason: args.reason || '', + } + }) + setRawVotes(decoded) + } catch(e) { + console.error(e) + } finally { + if (!cancelled) setLogsLoading(false) + } + } + fetch() + return () => { cancelled = true } + }, [voter]) + + const { data, loading: proposalsLoading } = useQuery(GET_PROPOSALS_BY_IDS, { + skip: proposalIds.length === 0, + variables: { ids: proposalIds }, + }) + + const votes: Vote[] = useMemo(() => { + if (logsLoading || proposalsLoading) return [] + const map = new Map() + data?.treasuryProposals?.forEach((p: any) => { + map.set(p.id, { description: p.description || '', voteEnd: p.voteEnd || 0 }) + }) + return rawVotes + .map(r => { + const meta = map.get(r.proposalId) ?? { description: '', voteEnd: 0 } + const title = (meta.description.split('\n')[0] || '').replace(/^#\s*/, '') || 'Unknown Proposal' + return { ...r, endVote: meta.voteEnd, description: meta.description, proposalTitle: title } + }) + .sort((a, b) => b.endVote - a.endVote) + }, [rawVotes, data, logsLoading, proposalsLoading]) + + return { votes, isLoading: logsLoading || proposalsLoading } +} diff --git a/components/Votes/voteDetailItem.tsx b/components/Votes/voteDetailItem.tsx new file mode 100644 index 00000000..9854235d --- /dev/null +++ b/components/Votes/voteDetailItem.tsx @@ -0,0 +1,91 @@ +'use client'; + +import React from 'react'; +import Image from 'next/image'; +import { Box, Heading, Text, Link } from '@livepeer/design-system'; +import ArbitrumIcon from '../../public/img/logos/arbitrum.png'; +import { SUPPORT, Vote } from './types'; +import { formatAddress } from '../../utils/formatAddress'; + +const lptFormatter = new Intl.NumberFormat('en-US', { + minimumFractionDigits: 2, + maximumFractionDigits: 2, +}); + +function formatLpt(w: string) { + return `${lptFormatter.format(parseFloat(w) / 1e18)} LPT`; +} + +interface VoteDetailItemProps { + vote: Vote; +} + +export const VoteDetailItem: React.FC = ({ vote }) => { + const support = SUPPORT[vote.choiceID] || SUPPORT['2']; + return ( + + + + {vote.proposalTitle} + + + + + Proposal ID:{' '} + + {formatAddress(vote.proposalId)} + + + + + + Support: + + + {support.text} + + + + + + Weight:{' '} + + {formatLpt(vote.weight)} + + + + + Reason:{' '} + + {vote.reason || 'No reason provided'} + + + + + {vote.transactionHash ? ( + e.stopPropagation()} + css={{ display: 'inline-block', transition: 'transform 0.2s ease', '&:hover': { transform: 'scale(1.3)' } }} + > + Arbitrum Icon + + ) : ( + N/A + )} + + + ); +}; \ No newline at end of file diff --git a/components/Votes/voteModal.tsx b/components/Votes/voteModal.tsx new file mode 100644 index 00000000..dbc50606 --- /dev/null +++ b/components/Votes/voteModal.tsx @@ -0,0 +1,73 @@ +'use client'; + +import React from 'react'; +import { createPortal } from 'react-dom'; +import { Box, Button } from '@livepeer/design-system'; + +interface VoteModalProps { + onClose: () => void; + children: React.ReactNode; +} + +export const VoteModal: React.FC = ({ onClose, children }) => + createPortal( + e.stopPropagation()} + > + e.stopPropagation()} + > + + + + + + {children} + + + , + document.body + ); diff --git a/components/Votes/voterPopover.tsx b/components/Votes/voterPopover.tsx index 0b22aa65..fa4b10de 100644 --- a/components/Votes/voterPopover.tsx +++ b/components/Votes/voterPopover.tsx @@ -1,37 +1,11 @@ -"use client"; +'use client'; -import React, { useState, useEffect } from "react"; -import { createPortal } from "react-dom"; -import { ethers } from "ethers"; -import { useQuery } from "@apollo/client"; -import Spinner from "@components/Spinner"; -import { formatAddress } from "../../utils/formatAddress"; -import { - Box, - Button, - Flex, - Heading, - Text, -} from "@livepeer/design-system"; -import { GET_PROPOSALS_BY_IDS } from "../../queries/treasuryProposals"; -import { - CONTRACT_ADDRESS, - VOTECAST_TOPIC0, - provider, - contractInterface, -} from "@lib/chains"; - -interface Vote { - endVote: number; - description: string; - transactionHash?: string; - weight: string; - voter: string; - choiceID: string; - proposalId: string; - reason: string; - proposalTitle: string; -} +import React from 'react'; +import Spinner from '@components/Spinner'; +import { Flex, Text } from '@livepeer/design-system'; +import { VoteModal } from './voteModal'; +import { VoteDetailItem } from './voteDetailItem'; +import { useVoterVotes } from './useVoterVotes'; interface VoterPopoverProps { voter: string; @@ -39,183 +13,25 @@ interface VoterPopoverProps { } const VoterPopover: React.FC = ({ voter, onClose }) => { - const [logsLoading, setLogsLoading] = useState(true); - const [proposalIds, setProposalIds] = useState([]); - const [rawVotes, setRawVotes] = useState([]); - - useEffect(() => { - const fetchLogsForVoter = async () => { - setLogsLoading(true); - try { - const voterTopic = ethers.utils.zeroPad(voter, 32); - const logs = await provider.getLogs({ - address: CONTRACT_ADDRESS, - fromBlock: "earliest", - toBlock: "latest", - topics: [VOTECAST_TOPIC0, ethers.utils.hexlify(voterTopic)], - }); - - const decodedVotes: any[] = []; - const proposalIdsSet = new Set(); - - logs.forEach((log) => { - const decoded = contractInterface.parseLog(log); - const proposalId = decoded?.args.proposalId.toString(); - decodedVotes.push({ - transactionHash: log.transactionHash, - voter: decoded?.args.voter, - proposalId, - choiceID: decoded?.args.support.toString(), - weight: decoded?.args.weight.toString(), - reason: decoded?.args.reason, - }); - proposalIdsSet.add(proposalId); - }); - - setRawVotes(decodedVotes); - setProposalIds(Array.from(proposalIdsSet)); - } catch (error) { - console.error("Error fetching logs for voter:", error); - } finally { - setLogsLoading(false); - } - }; - - fetchLogsForVoter(); - }, [voter]); - - const { data, loading: proposalsLoading } = useQuery(GET_PROPOSALS_BY_IDS, { - variables: { ids: proposalIds }, - skip: proposalIds.length === 0, - }); - - const votes: Vote[] = React.useMemo(() => { - if (logsLoading || proposalsLoading) return []; - - const proposalsMap = new Map(); - if (data?.treasuryProposals) { - data.treasuryProposals.forEach((p: any) => { - proposalsMap.set(p.id, { - description: p.description || "No description available.", - voteEnd: p.voteEnd || 0, - }); - }); - } - - return rawVotes - .map((v) => { - const subgraphData = proposalsMap.get(v.proposalId); - const description = subgraphData?.description || "No description available."; - const endVote = subgraphData?.voteEnd || 0; - const title = description.split("\n")[0].replace(/^#\s*/, "") || "Unknown Proposal"; - - return { - transactionHash: v.transactionHash, - voter: v.voter, - choiceID: v.choiceID, - proposalId: v.proposalId, - weight: v.weight, - reason: v.reason || "", - endVote, - description, - proposalTitle: title, - }; - }) - .sort((b, a) => a.endVote - b.endVote); - }, [logsLoading, proposalsLoading, rawVotes, data]); - - const isLoading = logsLoading || proposalsLoading; - - const getSupportStyles = (choiceID: string) => { - switch (choiceID) { - case "1": - return { color: "$green9", fontWeight: 600 }; - case "0": - return { color: "$red9", fontWeight: 600 }; - default: - return { color: "$yellow9", fontWeight: 600 }; - } - }; - - const popoverContent = ( - - - - {isLoading ? ( - - - - ) : votes.length > 0 ? ( - votes.map((vote, index) => ( - - - {vote.proposalTitle} - - - Proposal ID: {formatAddress(vote.proposalId)} - - - Support:{" "} - - {vote.choiceID === "1" ? "Yes" : vote.choiceID === "2" ? "Abstain" : "No"} - - - - Weight:{" "} - {new Intl.NumberFormat("en-US", { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }).format(parseFloat(vote.weight) / 1e18)}{" "} - LPT - - - Reason: {vote.reason || "No reason provided"} - - - )) - ) : ( - No votes found for this voter. - )} - - + const { votes, isLoading } = useVoterVotes(voter); + + return ( + + {isLoading ? ( + + + + ) : votes.length > 0 ? ( + votes.map((vote, idx) => ( + + )) + ) : ( + + No votes found for this voter. + + )} + ); - - return createPortal(popoverContent, document.body); }; export default VoterPopover; From fb3f7a0e910ee2559dd94555f30eb9e852cfd141 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Fri, 25 Apr 2025 21:38:24 +0100 Subject: [PATCH 21/37] Missed link for mobile voter --- components/Votes/mobileVoteCards.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/components/Votes/mobileVoteCards.tsx b/components/Votes/mobileVoteCards.tsx index de497082..4bd93aa1 100644 --- a/components/Votes/mobileVoteCards.tsx +++ b/components/Votes/mobileVoteCards.tsx @@ -31,7 +31,15 @@ export const MobileVoteCards: React.FC = ({ votes, counts, onClickCapture={e => { if ((e.target as HTMLElement).closest('a')) return; e.stopPropagation(); onSelect(vote.voter); }} > - {formatAddress(vote.ensName ?? '') || formatAddress(vote.voter)} + e.stopPropagation()} + > + {formatAddress(vote.ensName ?? '') || formatAddress(vote.voter)} + + Support: @@ -40,7 +48,7 @@ export const MobileVoteCards: React.FC = ({ votes, counts, Weight: {formatWeight(vote.weight)} - + Reason: {vote.reason} @@ -48,7 +56,7 @@ export const MobileVoteCards: React.FC = ({ votes, counts, e.stopPropagation()} > Arbitrum Icon From c5634eb374e5ccfb436ae84762559a95c324707a Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Sat, 26 Apr 2025 21:50:34 +0100 Subject: [PATCH 22/37] Removed technical debt no longer needed. Added No Votes - Since refactoring code, some of this was no longer needed in here. -Added Number of votes alongside view votes. -Added info for user to view popover. --- components/Votes/desktopVoteTable.tsx | 7 ++ components/Votes/mobileVoteCards.tsx | 8 +++ pages/treasury/[proposal].tsx | 92 ++++----------------------- 3 files changed, 29 insertions(+), 78 deletions(-) diff --git a/components/Votes/desktopVoteTable.tsx b/components/Votes/desktopVoteTable.tsx index 952c8740..0d8f7726 100644 --- a/components/Votes/desktopVoteTable.tsx +++ b/components/Votes/desktopVoteTable.tsx @@ -15,6 +15,9 @@ interface DesktopVoteTableProps { export const DesktopVoteTable: React.FC = ({ votes, counts, formatWeight, onSelect }) => ( + + Vote Results + Yes ({counts.yes}) | @@ -22,6 +25,10 @@ export const DesktopVoteTable: React.FC = ({ votes, count | Abstain ({counts.abstain}) + + + Click on a vote to view a voter's proposal voting history. + diff --git a/components/Votes/mobileVoteCards.tsx b/components/Votes/mobileVoteCards.tsx index 4bd93aa1..b415c548 100644 --- a/components/Votes/mobileVoteCards.tsx +++ b/components/Votes/mobileVoteCards.tsx @@ -15,6 +15,9 @@ interface MobileVoteCardsProps { export const MobileVoteCards: React.FC = ({ votes, counts, formatWeight, onSelect }) => ( + + Vote Results + Yes ({counts.yes}) | @@ -22,6 +25,11 @@ export const MobileVoteCards: React.FC = ({ votes, counts, | Abstain ({counts.abstain}) + + + Click on a vote to view a voter's proposal voting history. + + {votes.map(vote => { const support = SUPPORT[vote.choiceID] || SUPPORT['2']; return ( diff --git a/pages/treasury/[proposal].tsx b/pages/treasury/[proposal].tsx index 796a2967..2c5aec2d 100644 --- a/pages/treasury/[proposal].tsx +++ b/pages/treasury/[proposal].tsx @@ -1,12 +1,11 @@ import { getLayout, LAYOUT_MAX_WIDTH } from "@layouts/main"; import { useRouter } from "next/router"; -import React, { useState, useEffect } from "react"; +import React, { useState, useMemo } from "react"; import { abbreviateNumber, fromWei, shortenAddress } from "@lib/utils"; import MarkdownRenderer from "@components/MarkdownRenderer"; import BottomDrawer from "@components/BottomDrawer"; import Spinner from "@components/Spinner"; import Stat from "@components/Stat"; -import { fetchVotesFromInfura } from "../../hooks/fetchVotes"; import { Badge, Box, @@ -20,7 +19,6 @@ import { } from "@livepeer/design-system"; import dayjs from "dayjs"; import Head from "next/head"; -import { useMemo } from "react"; import { useWindowSize } from "react-use"; import { useAccountAddress, @@ -31,6 +29,7 @@ import { useProposalVotingPowerData, useTreasuryProposalState, } from "../../hooks"; +import { useVotes } from '../../hooks/useVotes'; import FourZeroFour from "../404"; import { useProtocolQuery, useTreasuryProposalQuery } from "apollo"; import { sentenceCase } from "change-case"; @@ -45,23 +44,6 @@ import { livepeerToken } from "@lib/api/abis/main/LivepeerToken"; import { CHAIN_INFO, DEFAULT_CHAIN, DEFAULT_CHAIN_ID } from "@lib/chains"; import { BigNumber } from "ethers"; - -interface Proposal { - id: string; - description: string; - voteStart: number; - voteEnd: number; - proposer: { id: string }; - votes?: { weight: string; choiceID: string; voter?: string }[]; - targets: string[]; - calldatas: string[]; -} -interface Vote { - weight: string; - choiceID: string; - voter?: string; -} - dayjs.extend(relativeTime); const formatPercent = (percent: number) => numeral(percent).format("0.0000%"); @@ -100,12 +82,15 @@ const Proposal = () => { const { data: protocolQuery } = useProtocolQuery(); const currentRound = useCurrentRoundData(); - const [votes, setVotes] = useState([]); - const [voteCount, setVoteCount] = useState(0); - const [loadingVotes, setLoadingVotes] = useState(true); - const [ensCache, setEnsCache] = useState({}); + const { votes, loading: votesLoading } = useVotes(proposalId ?? ""); const [votesOpen, setVotesOpen] = useState(false); + function votesContent() { + if (votesLoading) return ; + if (votes.length === 0) return No votes yet.; + return proposal ? : null; + } + const proposal = useMemo(() => { if (!proposalQuery || !state || !protocolQuery || !currentRound) { return null; @@ -155,46 +140,6 @@ const Proposal = () => { return ; } - useEffect(() => { - async function fetchVotes() { - if (!proposal?.id) return; - - setLoadingVotes(true); - try { - const fetchedVotes = await fetchVotesFromInfura(proposal.id); - const validVotes = fetchedVotes.filter(vote => - ["0", "1", "2"].includes(vote.choiceID) - ); - setVotes(validVotes); - setVoteCount(validVotes.length); - - const cache: { [key: string]: null } = {}; - for (const vote of validVotes) { - if (vote.voter && !cache[vote.voter]) { - cache[vote.voter] = null; - } - } - setEnsCache(cache); - } catch (err) { - console.error("Error fetching votes", err); - } finally { - setLoadingVotes(false); - } - } - - fetchVotes(); - }, [proposal?.id]); - - - const formatStake = (stake: number) => - `${numeral(parseFloat(fromWei(stake.toString()))).format("0,0.[00]")} LPT`; - - const totalVotes = votes.filter(vote => - ["0", "1", "2"].includes(vote.choiceID) - ).length; - - - if (!proposal) { return ( <> @@ -676,7 +621,10 @@ const Proposal = () => { alignItems: "center", }} > - View Votes + +{votesLoading ? "Loading votes…" : `View Votes (${votes.length})`} + + { - {votesOpen && ( - ({ - voter: vote.voter || "Unknown", - weight: vote.weight, - choiceID: vote.choiceID, - }))} - /> - )} + {votesOpen && {votesContent()}} From 6b53e24e7964f0bccb1ef34e6b6e1e9c26b705bc Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Sat, 26 Apr 2025 22:22:25 +0100 Subject: [PATCH 23/37] Cleanup --- components/Votes/useVoterVotes.ts | 1 - components/Votes/voteTable.tsx | 14 ++++++++++++-- hooks/fetchVotes.ts | 11 +---------- hooks/useVotes.ts | 11 +---------- pages/treasury/[proposal].tsx | 14 +++++++------- 5 files changed, 21 insertions(+), 30 deletions(-) diff --git a/components/Votes/useVoterVotes.ts b/components/Votes/useVoterVotes.ts index c336c8c5..3720f447 100644 --- a/components/Votes/useVoterVotes.ts +++ b/components/Votes/useVoterVotes.ts @@ -1,4 +1,3 @@ -// votes/useVoterVotes.ts import { useState, useEffect, useMemo } from 'react' import { provider, VOTECAST_TOPIC0, contractInterface, CONTRACT_ADDRESS } from '@lib/chains' import { useQuery } from '@apollo/client' diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx index e796a625..1e7d8376 100644 --- a/components/Votes/voteTable.tsx +++ b/components/Votes/voteTable.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useState, useMemo, useCallback } from 'react'; +import { useWindowSize } from 'react-use'; import Spinner from '@components/Spinner'; import { useVotes } from '../../hooks/useVotes'; import { DesktopVoteTable } from './desktopVoteTable'; @@ -19,6 +20,9 @@ const lptFormatter = new Intl.NumberFormat('en-US', { export const VoteTable: React.FC = ({ proposalId }) => { const { votes, loading } = useVotes(proposalId); + const { width } = useWindowSize(); + const isDesktop = width >= 768; + const [selectedVoter, setSelectedVoter] = useState(null); const counts = { @@ -52,10 +56,16 @@ export const VoteTable: React.FC = ({ proposalId }) => { ); + const VoteListView = isDesktop ? DesktopVoteTable : MobileVoteCards; + return ( <> - - + {selectedVoter && setSelectedVoter(null)} />} ); diff --git a/hooks/fetchVotes.ts b/hooks/fetchVotes.ts index 9b0e3b9d..3c1697af 100644 --- a/hooks/fetchVotes.ts +++ b/hooks/fetchVotes.ts @@ -10,16 +10,7 @@ import { import { ENS_QUERY } from "../queries/treasuryProposals"; - -interface Vote { - transactionHash?: string; - weight: string; - voter: string; - choiceID: string; - proposalId: string; - reason: string; - ensName?: string; -} +import { Vote } from '../components/Votes/types'; const createEnsApolloClient = () => new ApolloClient({ diff --git a/hooks/useVotes.ts b/hooks/useVotes.ts index a4a2c35a..524c04a5 100644 --- a/hooks/useVotes.ts +++ b/hooks/useVotes.ts @@ -1,16 +1,7 @@ import { useState, useEffect } from 'react'; import { fetchVotesFromInfura } from './fetchVotes'; +import { Vote } from '../components/Votes/types'; -interface Vote { - transactionHash?: string; - weight: string; - voter: string; - choiceID: string; - proposalId: string; - reason: string; - ensName?: string; - } - export function useVotes(proposalId: string) { const [votes, setVotes] = useState([]); const [loading, setLoading] = useState(true); diff --git a/pages/treasury/[proposal].tsx b/pages/treasury/[proposal].tsx index 2c5aec2d..e66780b1 100644 --- a/pages/treasury/[proposal].tsx +++ b/pages/treasury/[proposal].tsx @@ -1,6 +1,6 @@ import { getLayout, LAYOUT_MAX_WIDTH } from "@layouts/main"; import { useRouter } from "next/router"; -import React, { useState, useMemo } from "react"; +import React, { useState, useMemo, useCallback } from "react"; import { abbreviateNumber, fromWei, shortenAddress } from "@lib/utils"; import MarkdownRenderer from "@components/MarkdownRenderer"; import BottomDrawer from "@components/BottomDrawer"; @@ -84,12 +84,6 @@ const Proposal = () => { const { votes, loading: votesLoading } = useVotes(proposalId ?? ""); const [votesOpen, setVotesOpen] = useState(false); - - function votesContent() { - if (votesLoading) return ; - if (votes.length === 0) return No votes yet.; - return proposal ? : null; - } const proposal = useMemo(() => { if (!proposalQuery || !state || !protocolQuery || !currentRound) { @@ -105,6 +99,12 @@ const Proposal = () => { const proposerId = useEnsData(proposal?.proposer.id); + const votesContent = useCallback(() => { + if (votesLoading) return ; + if (votes.length === 0) return No votes yet.; + return ; + }, [votesLoading, votes.length, proposal?.id]); + const actions = useMemo(() => { if (!proposal || !contractAddresses) { return null; From 2f8ede0346db886087a500e4c7f9cda9dec2faef Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Sat, 3 May 2025 20:51:33 +0100 Subject: [PATCH 24/37] Vote hooks, hydration fix -Voting feature hooks now situated in final location within codebase. -Hydration errors fix. --- components/Votes/types.ts => @types/votes.ts | 0 components/Votes/desktopVoteTable.tsx | 2 +- components/Votes/mobileVoteCards.tsx | 2 +- components/Votes/voteDetailItem.tsx | 2 +- components/Votes/voteTable.tsx | 19 ++++++++----------- components/Votes/voterPopover.tsx | 2 +- hooks/{ => TreasuryVotes}/fetchVotes.ts | 6 +++--- .../TreasuryVotes}/useVoterVotes.ts | 2 +- hooks/{ => TreasuryVotes}/useVotes.ts | 2 +- pages/treasury/[proposal].tsx | 11 ++++++++--- 10 files changed, 25 insertions(+), 23 deletions(-) rename components/Votes/types.ts => @types/votes.ts (100%) rename hooks/{ => TreasuryVotes}/fetchVotes.ts (93%) rename {components/Votes => hooks/TreasuryVotes}/useVoterVotes.ts (98%) rename hooks/{ => TreasuryVotes}/useVotes.ts (92%) diff --git a/components/Votes/types.ts b/@types/votes.ts similarity index 100% rename from components/Votes/types.ts rename to @types/votes.ts diff --git a/components/Votes/desktopVoteTable.tsx b/components/Votes/desktopVoteTable.tsx index 0d8f7726..71566628 100644 --- a/components/Votes/desktopVoteTable.tsx +++ b/components/Votes/desktopVoteTable.tsx @@ -3,7 +3,7 @@ import Image from 'next/image'; import { Box, Flex, Text, Link } from '@livepeer/design-system'; import ArbitrumIcon from '../../public/img/logos/arbitrum.png'; import { formatAddress } from '../../utils/formatAddress'; -import { Vote, SUPPORT } from './types'; +import { Vote, SUPPORT } from '../../@types/votes'; interface DesktopVoteTableProps { votes: Vote[]; diff --git a/components/Votes/mobileVoteCards.tsx b/components/Votes/mobileVoteCards.tsx index b415c548..f4ec4629 100644 --- a/components/Votes/mobileVoteCards.tsx +++ b/components/Votes/mobileVoteCards.tsx @@ -3,7 +3,7 @@ import Image from 'next/image'; import { Card, Flex, Heading, Link, Text, Box } from '@livepeer/design-system'; import ArbitrumIcon from '../../public/img/logos/arbitrum.png'; import { formatAddress } from '../../utils/formatAddress'; -import { Vote, SUPPORT } from './types'; +import { Vote, SUPPORT } from '../../@types/votes'; interface MobileVoteCardsProps { votes: Vote[]; diff --git a/components/Votes/voteDetailItem.tsx b/components/Votes/voteDetailItem.tsx index 9854235d..c494166d 100644 --- a/components/Votes/voteDetailItem.tsx +++ b/components/Votes/voteDetailItem.tsx @@ -4,7 +4,7 @@ import React from 'react'; import Image from 'next/image'; import { Box, Heading, Text, Link } from '@livepeer/design-system'; import ArbitrumIcon from '../../public/img/logos/arbitrum.png'; -import { SUPPORT, Vote } from './types'; +import { SUPPORT, Vote } from '../../@types/votes'; import { formatAddress } from '../../utils/formatAddress'; const lptFormatter = new Intl.NumberFormat('en-US', { diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx index 1e7d8376..d5e1f6f2 100644 --- a/components/Votes/voteTable.tsx +++ b/components/Votes/voteTable.tsx @@ -1,9 +1,9 @@ "use client"; -import React, { useState, useMemo, useCallback } from 'react'; +import React, { useState } from 'react'; import { useWindowSize } from 'react-use'; import Spinner from '@components/Spinner'; -import { useVotes } from '../../hooks/useVotes'; +import { useVotes } from '../../hooks/TreasuryVotes/useVotes'; import { DesktopVoteTable } from './desktopVoteTable'; import { MobileVoteCards } from './mobileVoteCards'; import VoterPopover from './voterPopover'; @@ -36,15 +36,12 @@ export const VoteTable: React.FC = ({ proposalId }) => { 0 ); - const formatWeight = useCallback( - (w: string) => - `${lptFormatter.format(parseFloat(w) / 1e18)} LPT (${ - totalWeight > 0 - ? ((parseFloat(w) / totalWeight) * 100).toFixed(2) - : '0' - }%)`, - [totalWeight] - ); + const formatWeight = (w: string) => + `${lptFormatter.format(parseFloat(w) / 1e18)} LPT (${ + totalWeight > 0 + ? ((parseFloat(w) / totalWeight) * 100).toFixed(2) + : '0' + }%)`; if (loading) return ( diff --git a/components/Votes/voterPopover.tsx b/components/Votes/voterPopover.tsx index fa4b10de..aaa4dad4 100644 --- a/components/Votes/voterPopover.tsx +++ b/components/Votes/voterPopover.tsx @@ -5,7 +5,7 @@ import Spinner from '@components/Spinner'; import { Flex, Text } from '@livepeer/design-system'; import { VoteModal } from './voteModal'; import { VoteDetailItem } from './voteDetailItem'; -import { useVoterVotes } from './useVoterVotes'; +import { useVoterVotes } from '../../hooks/TreasuryVotes/useVoterVotes'; interface VoterPopoverProps { voter: string; diff --git a/hooks/fetchVotes.ts b/hooks/TreasuryVotes/fetchVotes.ts similarity index 93% rename from hooks/fetchVotes.ts rename to hooks/TreasuryVotes/fetchVotes.ts index 3c1697af..c48e0b02 100644 --- a/hooks/fetchVotes.ts +++ b/hooks/TreasuryVotes/fetchVotes.ts @@ -1,5 +1,5 @@ import { ApolloClient, InMemoryCache } from "@apollo/client"; -import { formatAddress } from "../utils/formatAddress"; +import { formatAddress } from "../../utils/formatAddress"; import { CONTRACT_ADDRESS, VOTECAST_TOPIC0, @@ -9,8 +9,8 @@ import { import { ENS_QUERY -} from "../queries/treasuryProposals"; -import { Vote } from '../components/Votes/types'; +} from "../../queries/treasuryProposals"; +import { Vote } from '../../@types/votes'; const createEnsApolloClient = () => new ApolloClient({ diff --git a/components/Votes/useVoterVotes.ts b/hooks/TreasuryVotes/useVoterVotes.ts similarity index 98% rename from components/Votes/useVoterVotes.ts rename to hooks/TreasuryVotes/useVoterVotes.ts index 3720f447..69bcf824 100644 --- a/components/Votes/useVoterVotes.ts +++ b/hooks/TreasuryVotes/useVoterVotes.ts @@ -3,7 +3,7 @@ import { provider, VOTECAST_TOPIC0, contractInterface, CONTRACT_ADDRESS } from ' import { useQuery } from '@apollo/client' import { ethers } from "ethers"; import { GET_PROPOSALS_BY_IDS } from '../../queries/treasuryProposals' -import { Vote } from './types' +import { Vote } from '../../@types/votes' export function useVoterVotes(voter: string) { const [logsLoading, setLogsLoading] = useState(true) diff --git a/hooks/useVotes.ts b/hooks/TreasuryVotes/useVotes.ts similarity index 92% rename from hooks/useVotes.ts rename to hooks/TreasuryVotes/useVotes.ts index 524c04a5..45b94fca 100644 --- a/hooks/useVotes.ts +++ b/hooks/TreasuryVotes/useVotes.ts @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import { fetchVotesFromInfura } from './fetchVotes'; -import { Vote } from '../components/Votes/types'; +import { Vote } from '../../@types/votes'; export function useVotes(proposalId: string) { const [votes, setVotes] = useState([]); diff --git a/pages/treasury/[proposal].tsx b/pages/treasury/[proposal].tsx index e66780b1..a0252f74 100644 --- a/pages/treasury/[proposal].tsx +++ b/pages/treasury/[proposal].tsx @@ -1,6 +1,6 @@ import { getLayout, LAYOUT_MAX_WIDTH } from "@layouts/main"; import { useRouter } from "next/router"; -import React, { useState, useMemo, useCallback } from "react"; +import React, { useState, useMemo, useCallback, useEffect } from "react"; import { abbreviateNumber, fromWei, shortenAddress } from "@lib/utils"; import MarkdownRenderer from "@components/MarkdownRenderer"; import BottomDrawer from "@components/BottomDrawer"; @@ -29,7 +29,7 @@ import { useProposalVotingPowerData, useTreasuryProposalState, } from "../../hooks"; -import { useVotes } from '../../hooks/useVotes'; +import { useVotes } from '../../hooks/TreasuryVotes/useVotes'; import FourZeroFour from "../404"; import { useProtocolQuery, useTreasuryProposalQuery } from "apollo"; import { sentenceCase } from "change-case"; @@ -63,6 +63,7 @@ const formatDateTime = (date: dayjs.Dayjs) => { const Proposal = () => { const router = useRouter(); const { width } = useWindowSize(); + const [isDesktop, setIsDesktop] = useState(false); const { setBottomDrawerOpen } = useExplorerStore(); const { query } = router; @@ -140,6 +141,10 @@ const Proposal = () => { return ; } + useEffect(() => { + setIsDesktop(width >= 768); + }, [width]); + if (!proposal) { return ( <> @@ -645,7 +650,7 @@ const Proposal = () => { - {width > 1200 ? ( + {isDesktop ? ( Date: Sun, 4 May 2025 22:28:26 +0100 Subject: [PATCH 25/37] treasuryProposals.ts file removed and queries moved into queries folder within their own graphql files --- @types/custom.d.ts | 11 +++++ components/Votes/desktopVoteTable.tsx | 2 +- components/Votes/mobileVoteCards.tsx | 2 +- components/Votes/voteDetailItem.tsx | 2 +- hooks/TreasuryVotes/fetchVotes.ts | 6 +-- hooks/TreasuryVotes/useVoterVotes.ts | 4 +- hooks/TreasuryVotes/useVotes.ts | 2 +- {@types => lib/api/types}/votes.ts | 0 next.config.js | 11 +++++ queries/ens.graphql | 12 +++++ queries/proposalsByIds.graphql | 8 +++ queries/treasuryProposals.ts | 71 --------------------------- 12 files changed, 50 insertions(+), 81 deletions(-) rename {@types => lib/api/types}/votes.ts (100%) create mode 100644 queries/ens.graphql create mode 100644 queries/proposalsByIds.graphql delete mode 100644 queries/treasuryProposals.ts diff --git a/@types/custom.d.ts b/@types/custom.d.ts index 4261298f..1499e080 100644 --- a/@types/custom.d.ts +++ b/@types/custom.d.ts @@ -6,3 +6,14 @@ declare module "*.png"; declare module "*.jpg"; declare module "*.jpeg"; declare module "*.gif"; + +declare module "*.graphql" { + import { DocumentNode } from "graphql"; + const doc: DocumentNode; + export default doc; +} +declare module "*.gql" { + import { DocumentNode } from "graphql"; + const doc: DocumentNode; + export default doc; +} \ No newline at end of file diff --git a/components/Votes/desktopVoteTable.tsx b/components/Votes/desktopVoteTable.tsx index 71566628..d98e3268 100644 --- a/components/Votes/desktopVoteTable.tsx +++ b/components/Votes/desktopVoteTable.tsx @@ -3,7 +3,7 @@ import Image from 'next/image'; import { Box, Flex, Text, Link } from '@livepeer/design-system'; import ArbitrumIcon from '../../public/img/logos/arbitrum.png'; import { formatAddress } from '../../utils/formatAddress'; -import { Vote, SUPPORT } from '../../@types/votes'; +import { Vote, SUPPORT } from '../../lib/api/types/votes'; interface DesktopVoteTableProps { votes: Vote[]; diff --git a/components/Votes/mobileVoteCards.tsx b/components/Votes/mobileVoteCards.tsx index f4ec4629..886f69f8 100644 --- a/components/Votes/mobileVoteCards.tsx +++ b/components/Votes/mobileVoteCards.tsx @@ -3,7 +3,7 @@ import Image from 'next/image'; import { Card, Flex, Heading, Link, Text, Box } from '@livepeer/design-system'; import ArbitrumIcon from '../../public/img/logos/arbitrum.png'; import { formatAddress } from '../../utils/formatAddress'; -import { Vote, SUPPORT } from '../../@types/votes'; +import { Vote, SUPPORT } from '../../lib/api/types/votes'; interface MobileVoteCardsProps { votes: Vote[]; diff --git a/components/Votes/voteDetailItem.tsx b/components/Votes/voteDetailItem.tsx index c494166d..8b370890 100644 --- a/components/Votes/voteDetailItem.tsx +++ b/components/Votes/voteDetailItem.tsx @@ -4,7 +4,7 @@ import React from 'react'; import Image from 'next/image'; import { Box, Heading, Text, Link } from '@livepeer/design-system'; import ArbitrumIcon from '../../public/img/logos/arbitrum.png'; -import { SUPPORT, Vote } from '../../@types/votes'; +import { SUPPORT, Vote } from '../../lib/api/types/votes'; import { formatAddress } from '../../utils/formatAddress'; const lptFormatter = new Intl.NumberFormat('en-US', { diff --git a/hooks/TreasuryVotes/fetchVotes.ts b/hooks/TreasuryVotes/fetchVotes.ts index c48e0b02..ee7a9a63 100644 --- a/hooks/TreasuryVotes/fetchVotes.ts +++ b/hooks/TreasuryVotes/fetchVotes.ts @@ -7,10 +7,8 @@ import { contractInterface, } from "@lib/chains"; -import { - ENS_QUERY -} from "../../queries/treasuryProposals"; -import { Vote } from '../../@types/votes'; +import ENS_QUERY from "../../queries/ens.graphql"; +import { Vote } from '../../lib/api/types/votes'; const createEnsApolloClient = () => new ApolloClient({ diff --git a/hooks/TreasuryVotes/useVoterVotes.ts b/hooks/TreasuryVotes/useVoterVotes.ts index 69bcf824..08615020 100644 --- a/hooks/TreasuryVotes/useVoterVotes.ts +++ b/hooks/TreasuryVotes/useVoterVotes.ts @@ -2,8 +2,8 @@ import { useState, useEffect, useMemo } from 'react' import { provider, VOTECAST_TOPIC0, contractInterface, CONTRACT_ADDRESS } from '@lib/chains' import { useQuery } from '@apollo/client' import { ethers } from "ethers"; -import { GET_PROPOSALS_BY_IDS } from '../../queries/treasuryProposals' -import { Vote } from '../../@types/votes' +import GET_PROPOSALS_BY_IDS from "../../queries/proposalsByIds.graphql"; +import { Vote } from '../../lib/api/types/votes' export function useVoterVotes(voter: string) { const [logsLoading, setLogsLoading] = useState(true) diff --git a/hooks/TreasuryVotes/useVotes.ts b/hooks/TreasuryVotes/useVotes.ts index 45b94fca..74ea4c12 100644 --- a/hooks/TreasuryVotes/useVotes.ts +++ b/hooks/TreasuryVotes/useVotes.ts @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import { fetchVotesFromInfura } from './fetchVotes'; -import { Vote } from '../../@types/votes'; +import { Vote } from '../../lib/api/types/votes'; export function useVotes(proposalId: string) { const [votes, setVotes] = useState([]); diff --git a/@types/votes.ts b/lib/api/types/votes.ts similarity index 100% rename from @types/votes.ts rename to lib/api/types/votes.ts diff --git a/next.config.js b/next.config.js index b0fe7017..3854e886 100644 --- a/next.config.js +++ b/next.config.js @@ -33,6 +33,17 @@ const nextConfig = { }, ]; }, + + webpack(config) { + config.module.rules.push({ + test: /\.(graphql|gql)$/, + exclude: /node_modules/, + use: { + loader: "graphql-tag/loader", + }, + }); + return config; + }, }; module.exports = nextConfig; diff --git a/queries/ens.graphql b/queries/ens.graphql new file mode 100644 index 00000000..c466a0a6 --- /dev/null +++ b/queries/ens.graphql @@ -0,0 +1,12 @@ + query getENS($address: String!) { + domains(where: { resolvedAddress: $address } + orderBy: registration__registrationDate + orderDirection: desc + ) { + name + resolvedAddress { + id + } + createdAt + } + } \ No newline at end of file diff --git a/queries/proposalsByIds.graphql b/queries/proposalsByIds.graphql new file mode 100644 index 00000000..ac25b14d --- /dev/null +++ b/queries/proposalsByIds.graphql @@ -0,0 +1,8 @@ +query getProposalsByIds($ids: [String!]!) { + treasuryProposals(where: { id_in: $ids }) { + id + description + voteStart + voteEnd + } + } \ No newline at end of file diff --git a/queries/treasuryProposals.ts b/queries/treasuryProposals.ts deleted file mode 100644 index 978eac76..00000000 --- a/queries/treasuryProposals.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { gql } from "@apollo/client"; - -export const GET_PROPOSALS_VOTES = gql` - query GetProposalsVotes { - treasuryProposals(orderBy: voteStart, orderDirection: desc) { - id - description - targets - values - voteStart - voteEnd - calldatas - proposer { - id - } - } - votes(first: 1000, orderBy: voter, orderDirection: desc) { - choiceID - id - voteStake - voter - poll { - id - proposal - } - } - } -`; - -export const GET_BLOCK_TIMESTAMPS = gql` - query GetBlockTimestamps { - rounds(first: 1000, orderBy: startTimestamp, orderDirection: desc) { - startTimestamp - id - } - } -`; - -export const GET_PROTOCOL_STATS = gql` - query GetProtocolStats { - protocols { - totalActiveStake - } - } -`; - -export const ENS_QUERY = gql` - query getENS($address: String!) { - domains(where: { resolvedAddress: $address } - orderBy: registration__registrationDate - orderDirection: desc - ) { - name - resolvedAddress { - id - } - createdAt - } -} -`; - -export const GET_PROPOSALS_BY_IDS = gql` - query getProposalsByIds($ids: [String!]!) { - treasuryProposals(where: { id_in: $ids }) { - id - description - voteStart - voteEnd - } -} -`; \ No newline at end of file From 8d93c30e22c1a0b65808db66335d919fc86271a1 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Tue, 6 May 2025 20:41:56 +0100 Subject: [PATCH 26/37] treasuryProposals within apollo --- @types/custom.d.ts | 13 +------------ apollo/treasuryProposals.ts | 27 +++++++++++++++++++++++++++ hooks/TreasuryVotes/fetchVotes.ts | 2 +- hooks/TreasuryVotes/useVoterVotes.ts | 2 +- next.config.js | 11 ----------- queries/ens.graphql | 12 ------------ queries/proposalsByIds.graphql | 8 -------- 7 files changed, 30 insertions(+), 45 deletions(-) create mode 100644 apollo/treasuryProposals.ts delete mode 100644 queries/ens.graphql delete mode 100644 queries/proposalsByIds.graphql diff --git a/@types/custom.d.ts b/@types/custom.d.ts index 1499e080..9ce7a96d 100644 --- a/@types/custom.d.ts +++ b/@types/custom.d.ts @@ -5,15 +5,4 @@ declare module "*.svg" { declare module "*.png"; declare module "*.jpg"; declare module "*.jpeg"; -declare module "*.gif"; - -declare module "*.graphql" { - import { DocumentNode } from "graphql"; - const doc: DocumentNode; - export default doc; -} -declare module "*.gql" { - import { DocumentNode } from "graphql"; - const doc: DocumentNode; - export default doc; -} \ No newline at end of file +declare module "*.gif"; \ No newline at end of file diff --git a/apollo/treasuryProposals.ts b/apollo/treasuryProposals.ts new file mode 100644 index 00000000..bda52e56 --- /dev/null +++ b/apollo/treasuryProposals.ts @@ -0,0 +1,27 @@ +import { gql } from "@apollo/client"; + +export const ENS_QUERY = gql` + query getENS($address: String!) { + domains(where: { resolvedAddress: $address } + orderBy: registration__registrationDate + orderDirection: desc + ) { + name + resolvedAddress { + id + } + createdAt + } +} +`; + +export const GET_PROPOSALS_BY_IDS = gql` + query getProposalsByIds($ids: [String!]!) { + treasuryProposals(where: { id_in: $ids }) { + id + description + voteStart + voteEnd + } +} +`; \ No newline at end of file diff --git a/hooks/TreasuryVotes/fetchVotes.ts b/hooks/TreasuryVotes/fetchVotes.ts index ee7a9a63..7d08d029 100644 --- a/hooks/TreasuryVotes/fetchVotes.ts +++ b/hooks/TreasuryVotes/fetchVotes.ts @@ -7,7 +7,7 @@ import { contractInterface, } from "@lib/chains"; -import ENS_QUERY from "../../queries/ens.graphql"; +import { ENS_QUERY } from "../../apollo/treasuryProposals"; import { Vote } from '../../lib/api/types/votes'; const createEnsApolloClient = () => diff --git a/hooks/TreasuryVotes/useVoterVotes.ts b/hooks/TreasuryVotes/useVoterVotes.ts index 08615020..0f273fe4 100644 --- a/hooks/TreasuryVotes/useVoterVotes.ts +++ b/hooks/TreasuryVotes/useVoterVotes.ts @@ -2,7 +2,7 @@ import { useState, useEffect, useMemo } from 'react' import { provider, VOTECAST_TOPIC0, contractInterface, CONTRACT_ADDRESS } from '@lib/chains' import { useQuery } from '@apollo/client' import { ethers } from "ethers"; -import GET_PROPOSALS_BY_IDS from "../../queries/proposalsByIds.graphql"; +import { GET_PROPOSALS_BY_IDS } from "../../apollo/treasuryProposals"; import { Vote } from '../../lib/api/types/votes' export function useVoterVotes(voter: string) { diff --git a/next.config.js b/next.config.js index 3854e886..b0fe7017 100644 --- a/next.config.js +++ b/next.config.js @@ -33,17 +33,6 @@ const nextConfig = { }, ]; }, - - webpack(config) { - config.module.rules.push({ - test: /\.(graphql|gql)$/, - exclude: /node_modules/, - use: { - loader: "graphql-tag/loader", - }, - }); - return config; - }, }; module.exports = nextConfig; diff --git a/queries/ens.graphql b/queries/ens.graphql deleted file mode 100644 index c466a0a6..00000000 --- a/queries/ens.graphql +++ /dev/null @@ -1,12 +0,0 @@ - query getENS($address: String!) { - domains(where: { resolvedAddress: $address } - orderBy: registration__registrationDate - orderDirection: desc - ) { - name - resolvedAddress { - id - } - createdAt - } - } \ No newline at end of file diff --git a/queries/proposalsByIds.graphql b/queries/proposalsByIds.graphql deleted file mode 100644 index ac25b14d..00000000 --- a/queries/proposalsByIds.graphql +++ /dev/null @@ -1,8 +0,0 @@ -query getProposalsByIds($ids: [String!]!) { - treasuryProposals(where: { id_in: $ids }) { - id - description - voteStart - voteEnd - } - } \ No newline at end of file From 8b0f5a84610313a20a035f5d509ddb4854f612a3 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Tue, 6 May 2025 22:29:36 +0100 Subject: [PATCH 27/37] removed useVotes and optimised other hooks --- components/Votes/voteTable.tsx | 11 +++- hooks/TreasuryVotes/fetchVotes.ts | 70 -------------------- hooks/TreasuryVotes/useFetchVotes.ts | 95 ++++++++++++++++++++++++++++ hooks/TreasuryVotes/useVotes.ts | 23 ------- pages/treasury/[proposal].tsx | 4 +- queries/proposalsByIds.graphql | 8 +++ 6 files changed, 113 insertions(+), 98 deletions(-) delete mode 100644 hooks/TreasuryVotes/fetchVotes.ts create mode 100644 hooks/TreasuryVotes/useFetchVotes.ts delete mode 100644 hooks/TreasuryVotes/useVotes.ts create mode 100644 queries/proposalsByIds.graphql diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx index d5e1f6f2..f45e44ea 100644 --- a/components/Votes/voteTable.tsx +++ b/components/Votes/voteTable.tsx @@ -3,12 +3,11 @@ import React, { useState } from 'react'; import { useWindowSize } from 'react-use'; import Spinner from '@components/Spinner'; -import { useVotes } from '../../hooks/TreasuryVotes/useVotes'; import { DesktopVoteTable } from './desktopVoteTable'; import { MobileVoteCards } from './mobileVoteCards'; import VoterPopover from './voterPopover'; import { Text, Flex } from '@livepeer/design-system'; - +import { useFetchVotes } from '../../hooks/TreasuryVotes/useFetchVotes'; interface VoteTableProps { proposalId: string; } @@ -19,7 +18,7 @@ const lptFormatter = new Intl.NumberFormat('en-US', { }); export const VoteTable: React.FC = ({ proposalId }) => { - const { votes, loading } = useVotes(proposalId); + const { votes, loading, error } = useFetchVotes(proposalId); const { width } = useWindowSize(); const isDesktop = width >= 768; @@ -47,6 +46,12 @@ export const VoteTable: React.FC = ({ proposalId }) => { ); + if (error) return ( + + Error loading votes: {error} + + ); + if (!votes.length) return ( No votes found for this proposal. diff --git a/hooks/TreasuryVotes/fetchVotes.ts b/hooks/TreasuryVotes/fetchVotes.ts deleted file mode 100644 index 7d08d029..00000000 --- a/hooks/TreasuryVotes/fetchVotes.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { ApolloClient, InMemoryCache } from "@apollo/client"; -import { formatAddress } from "../../utils/formatAddress"; -import { - CONTRACT_ADDRESS, - VOTECAST_TOPIC0, - provider, - contractInterface, -} from "@lib/chains"; - -import { ENS_QUERY } from "../../apollo/treasuryProposals"; -import { Vote } from '../../lib/api/types/votes'; - -const createEnsApolloClient = () => - new ApolloClient({ - uri: process.env.NEXT_PUBLIC_ENS_API_URI, - cache: new InMemoryCache(), - }); - -export const fetchVotesFromInfura = async (proposalId: string): Promise => { - const ensClient = createEnsApolloClient(); - try { - const logs = await provider.getLogs({ - address: CONTRACT_ADDRESS, - fromBlock: "earliest", - toBlock: "latest", - topics: [VOTECAST_TOPIC0], - }); - - const decodedVotes = logs - .map((log) => { - const decoded = contractInterface.parseLog(log); - return { - transactionHash: log.transactionHash, - voter: decoded?.args.voter.toLowerCase() || "", - choiceID: decoded?.args.support.toString() || "", - proposalId: decoded?.args.proposalId.toString() || "", - weight: decoded?.args.weight.toString() || "0", - reason: decoded?.args.reason || "No reason provided", - }; - }) - .filter((vote) => vote.proposalId === proposalId); - - const uniqueVoters = Array.from(new Set(decodedVotes.map((v) => v.voter))); - const localEnsCache: { [address: string]: string } = {}; - - await Promise.all( - uniqueVoters.map(async (address) => { - try { - const { data } = await ensClient.query({ - query: ENS_QUERY, - variables: { address }, - }); - if (data?.domains?.length > 0) { - localEnsCache[address] = data.domains[0].name; - } - } catch (e) { - console.warn(`Failed to fetch ENS for ${address}`, e); - } - }) - ); - - return decodedVotes.map((vote) => ({ - ...vote, - ensName: localEnsCache[vote.voter] || formatAddress(vote.voter), - })); - } catch (error) { - console.error("Error fetching logs from Infura:", error); - return []; - } -}; \ No newline at end of file diff --git a/hooks/TreasuryVotes/useFetchVotes.ts b/hooks/TreasuryVotes/useFetchVotes.ts new file mode 100644 index 00000000..6e403454 --- /dev/null +++ b/hooks/TreasuryVotes/useFetchVotes.ts @@ -0,0 +1,95 @@ +import { formatAddress } from "../../utils/formatAddress"; +import { + CONTRACT_ADDRESS, + VOTECAST_TOPIC0, + provider, + contractInterface, +} from "@lib/chains"; + +import { Vote } from "../../lib/api/types/votes"; +import { getEnsForAddress } from "@lib/api/ens"; +import { useEffect, useState } from "react"; + +export const useFetchVotes = (proposalId: string) => { + const [votes, setVotes] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + if (!proposalId) { + setVotes([]); + setLoading(false); + return; + } + + const fetchVotes = async () => { + try { + const logs = await provider.getLogs({ + address: CONTRACT_ADDRESS, + fromBlock: "earliest", + toBlock: "latest", + topics: [VOTECAST_TOPIC0], + }); + + const decodedVotes = logs + .map((log) => { + const decoded = contractInterface.parseLog(log); + return { + transactionHash: log.transactionHash, + voter: decoded?.args.voter.toLowerCase() || "", + choiceID: decoded?.args.support.toString() || "", + proposalId: decoded?.args.proposalId.toString() || "", + weight: decoded?.args.weight.toString() || "0", + reason: decoded?.args.reason || "No reason provided", + }; + }) + .filter((vote) => vote.proposalId === proposalId) + + .sort((a, b) => parseFloat(b.weight) - parseFloat(a.weight)); + + const uniqueVoters = Array.from( + new Set(decodedVotes.map((v) => v.voter)) + ); + const localEnsCache: { [address: string]: string } = {}; + + await Promise.all( + uniqueVoters.map(async (address) => { + try { + if (localEnsCache[address]) { + return; + } + const ensAddress = await getEnsForAddress(address); + + if (ensAddress && ensAddress.name) { + localEnsCache[address] = ensAddress.name; + } else { + localEnsCache[address] = formatAddress(address); + } + } catch (e) { + console.warn(`Failed to fetch ENS for ${address}`, e); + } + }) + ); + + setVotes( + decodedVotes.map((vote) => ({ + ...vote, + ensName: localEnsCache[vote.voter] || formatAddress(vote.voter), + })) + ); + } catch (error) { + console.error("Error fetching logs from Infura:", error); + setError("Failed to fetch votes"); + } finally { + setLoading(false); + } + }; + fetchVotes(); + }, [proposalId]); + + return { + votes, + loading, + error, + }; +}; diff --git a/hooks/TreasuryVotes/useVotes.ts b/hooks/TreasuryVotes/useVotes.ts deleted file mode 100644 index 74ea4c12..00000000 --- a/hooks/TreasuryVotes/useVotes.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { useState, useEffect } from 'react'; -import { fetchVotesFromInfura } from './fetchVotes'; -import { Vote } from '../../lib/api/types/votes'; - -export function useVotes(proposalId: string) { - const [votes, setVotes] = useState([]); - const [loading, setLoading] = useState(true); - - useEffect(() => { - if (!proposalId) return; - let cancelled = false; - setLoading(true); - fetchVotesFromInfura(proposalId) - .then(fetched => - !cancelled && - setVotes(fetched.sort((a,b) => parseFloat(b.weight) - parseFloat(a.weight))) - ) - .finally(() => !cancelled && setLoading(false)); - return () => { cancelled = true }; - }, [proposalId]); - - return { votes, loading }; -} diff --git a/pages/treasury/[proposal].tsx b/pages/treasury/[proposal].tsx index a0252f74..1b0e06a3 100644 --- a/pages/treasury/[proposal].tsx +++ b/pages/treasury/[proposal].tsx @@ -29,7 +29,7 @@ import { useProposalVotingPowerData, useTreasuryProposalState, } from "../../hooks"; -import { useVotes } from '../../hooks/TreasuryVotes/useVotes'; +import { useFetchVotes } from '../../hooks/TreasuryVotes/useFetchVotes'; import FourZeroFour from "../404"; import { useProtocolQuery, useTreasuryProposalQuery } from "apollo"; import { sentenceCase } from "change-case"; @@ -83,7 +83,7 @@ const Proposal = () => { const { data: protocolQuery } = useProtocolQuery(); const currentRound = useCurrentRoundData(); - const { votes, loading: votesLoading } = useVotes(proposalId ?? ""); + const { votes, loading: votesLoading } = useFetchVotes(proposalId ?? ""); const [votesOpen, setVotesOpen] = useState(false); const proposal = useMemo(() => { diff --git a/queries/proposalsByIds.graphql b/queries/proposalsByIds.graphql new file mode 100644 index 00000000..353c92ee --- /dev/null +++ b/queries/proposalsByIds.graphql @@ -0,0 +1,8 @@ +query getProposalsByIds($ids: [ID!]!) { + treasuryProposals(where: { id_in: $ids }) { + id + description + voteStart + voteEnd + } +} \ No newline at end of file From 0b4e58ffd6157ae45e38aa14dd1be7dfa3a0d429 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Sat, 10 May 2025 20:40:26 +0100 Subject: [PATCH 28/37] Only fetch Ens name for votes --- components/Votes/desktopVoteTable.tsx | 3 +-- components/Votes/mobileVoteCards.tsx | 3 +-- hooks/TreasuryVotes/useFetchVotes.ts | 6 +++--- lib/api/ens.ts | 12 ++++++++++++ pages/treasury/[proposal].tsx | 15 ++++++++++++++- 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/components/Votes/desktopVoteTable.tsx b/components/Votes/desktopVoteTable.tsx index d98e3268..939ca054 100644 --- a/components/Votes/desktopVoteTable.tsx +++ b/components/Votes/desktopVoteTable.tsx @@ -2,7 +2,6 @@ import React from 'react'; import Image from 'next/image'; import { Box, Flex, Text, Link } from '@livepeer/design-system'; import ArbitrumIcon from '../../public/img/logos/arbitrum.png'; -import { formatAddress } from '../../utils/formatAddress'; import { Vote, SUPPORT } from '../../lib/api/types/votes'; interface DesktopVoteTableProps { @@ -56,7 +55,7 @@ export const DesktopVoteTable: React.FC = ({ votes, count css={{ color: '$green11', textDecoration: 'none', '&:hover': { textDecoration: 'underline' } }} onClick={e => e.stopPropagation()} > - {formatAddress(vote.ensName ?? '') || formatAddress(vote.voter)} + {vote.ensName} diff --git a/components/Votes/mobileVoteCards.tsx b/components/Votes/mobileVoteCards.tsx index 886f69f8..a3909912 100644 --- a/components/Votes/mobileVoteCards.tsx +++ b/components/Votes/mobileVoteCards.tsx @@ -2,7 +2,6 @@ import React from 'react'; import Image from 'next/image'; import { Card, Flex, Heading, Link, Text, Box } from '@livepeer/design-system'; import ArbitrumIcon from '../../public/img/logos/arbitrum.png'; -import { formatAddress } from '../../utils/formatAddress'; import { Vote, SUPPORT } from '../../lib/api/types/votes'; interface MobileVoteCardsProps { @@ -45,7 +44,7 @@ export const MobileVoteCards: React.FC = ({ votes, counts, css={{ color: '$green11', textDecoration: 'none', '&:hover': { textDecoration: 'underline' } }} onClick={e => e.stopPropagation()} > - {formatAddress(vote.ensName ?? '') || formatAddress(vote.voter)} + {vote.ensName} diff --git a/hooks/TreasuryVotes/useFetchVotes.ts b/hooks/TreasuryVotes/useFetchVotes.ts index 6e403454..285dae38 100644 --- a/hooks/TreasuryVotes/useFetchVotes.ts +++ b/hooks/TreasuryVotes/useFetchVotes.ts @@ -7,7 +7,7 @@ import { } from "@lib/chains"; import { Vote } from "../../lib/api/types/votes"; -import { getEnsForAddress } from "@lib/api/ens"; +import { getEnsForVotes } from "@lib/api/ens"; import { useEffect, useState } from "react"; export const useFetchVotes = (proposalId: string) => { @@ -58,7 +58,7 @@ export const useFetchVotes = (proposalId: string) => { if (localEnsCache[address]) { return; } - const ensAddress = await getEnsForAddress(address); + const ensAddress = await getEnsForVotes(address); if (ensAddress && ensAddress.name) { localEnsCache[address] = ensAddress.name; @@ -74,7 +74,7 @@ export const useFetchVotes = (proposalId: string) => { setVotes( decodedVotes.map((vote) => ({ ...vote, - ensName: localEnsCache[vote.voter] || formatAddress(vote.voter), + ensName: localEnsCache[vote.voter], })) ); } catch (error) { diff --git a/lib/api/ens.ts b/lib/api/ens.ts index 12ba7292..a9fa45c3 100644 --- a/lib/api/ens.ts +++ b/lib/api/ens.ts @@ -100,3 +100,15 @@ export const nl2br = (str, is_xhtml = true) => { "$1" + breakTag + "$2" ); }; + +export const getEnsForVotes = async (address: string | null | undefined) => { + const idShort = address?.replace(address?.slice(6, 38), "…"); + + const name = address ? await l1Provider.lookupAddress(address) : null; + + return { + id: address ?? "", + idShort: idShort ?? "", + name, + }; +}; \ No newline at end of file diff --git a/pages/treasury/[proposal].tsx b/pages/treasury/[proposal].tsx index 1b0e06a3..9479e2c9 100644 --- a/pages/treasury/[proposal].tsx +++ b/pages/treasury/[proposal].tsx @@ -101,7 +101,20 @@ const Proposal = () => { const proposerId = useEnsData(proposal?.proposer.id); const votesContent = useCallback(() => { - if (votesLoading) return ; + if (votesLoading) { + return ( + + + + ); + } if (votes.length === 0) return No votes yet.; return ; }, [votesLoading, votes.length, proposal?.id]); From 3c4b760f6501b204e62caea1c3b145e32d4e9206 Mon Sep 17 00:00:00 2001 From: rozzrr Date: Tue, 13 May 2025 22:30:21 +0100 Subject: [PATCH 29/37] more changes --- components/TreasuryVotingWidget/index.tsx | 2 +- components/{ => Votes}/VoteButton/index.tsx | 0 components/Votes/VoteDetail/index.tsx | 114 ++++ components/Votes/VoteModal/index.tsx | 79 +++ .../index.tsx} | 8 +- .../VoteTable/Views/DesktopVoteTable.tsx | 96 ++++ .../Votes/VoteTable/Views/MobileVoteTable.tsx | 70 +++ components/Votes/VoteTable/Views/VoteItem.tsx | 219 +++++++ components/Votes/VoteTable/index.tsx | 93 +++ components/Votes/VotingWidget/index.tsx | 539 ++++++++++++++++++ components/Votes/desktopVoteTable.tsx | 90 --- components/Votes/mobileVoteCards.tsx | 79 --- components/Votes/voteDetailItem.tsx | 91 --- components/Votes/voteModal.tsx | 73 --- components/Votes/voteTable.tsx | 76 --- components/VotingWidget/index.tsx | 532 ----------------- pages/treasury/[proposal].tsx | 2 +- utils/voting.ts | 51 ++ 18 files changed, 1267 insertions(+), 947 deletions(-) rename components/{ => Votes}/VoteButton/index.tsx (100%) create mode 100644 components/Votes/VoteDetail/index.tsx create mode 100644 components/Votes/VoteModal/index.tsx rename components/Votes/{voterPopover.tsx => VotePopover/index.tsx} (79%) create mode 100644 components/Votes/VoteTable/Views/DesktopVoteTable.tsx create mode 100644 components/Votes/VoteTable/Views/MobileVoteTable.tsx create mode 100644 components/Votes/VoteTable/Views/VoteItem.tsx create mode 100644 components/Votes/VoteTable/index.tsx create mode 100644 components/Votes/VotingWidget/index.tsx delete mode 100644 components/Votes/desktopVoteTable.tsx delete mode 100644 components/Votes/mobileVoteCards.tsx delete mode 100644 components/Votes/voteDetailItem.tsx delete mode 100644 components/Votes/voteModal.tsx delete mode 100644 components/Votes/voteTable.tsx delete mode 100644 components/VotingWidget/index.tsx create mode 100644 utils/voting.ts diff --git a/components/TreasuryVotingWidget/index.tsx b/components/TreasuryVotingWidget/index.tsx index fc8f7789..b90bb4b6 100644 --- a/components/TreasuryVotingWidget/index.tsx +++ b/components/TreasuryVotingWidget/index.tsx @@ -5,7 +5,7 @@ import { useAccountAddress } from "hooks"; import numeral from "numeral"; import { useMemo } from "react"; import { abbreviateNumber, fromWei, shortenAddress} from "@lib/utils"; -import VoteButton from "../VoteButton"; +import VoteButton from "../Votes/VoteButton"; import { ProposalVotingPower } from "@lib/api/types/get-treasury-proposal"; import { ProposalExtended } from "@lib/api/treasury"; import QueueExecuteButton from "@components/QueueExecuteButton"; diff --git a/components/VoteButton/index.tsx b/components/Votes/VoteButton/index.tsx similarity index 100% rename from components/VoteButton/index.tsx rename to components/Votes/VoteButton/index.tsx diff --git a/components/Votes/VoteDetail/index.tsx b/components/Votes/VoteDetail/index.tsx new file mode 100644 index 00000000..fdcf1ae2 --- /dev/null +++ b/components/Votes/VoteDetail/index.tsx @@ -0,0 +1,114 @@ +"use client"; + +import React from "react"; +import Image from "next/image"; +import { Box, Heading, Text, Link } from "@livepeer/design-system"; +import ArbitrumIcon from "../../public/img/logos/arbitrum.png"; +import { SUPPORT, Vote } from "@lib/api/types/votes"; +import { formatAddress } from "utils/formatAddress"; + +const lptFormatter = new Intl.NumberFormat("en-US", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, +}); + +function formatLpt(w: string) { + return `${lptFormatter.format(parseFloat(w) / 1e18)} LPT`; +} + +interface VoteDetailItemProps { + vote: Vote; +} + +const Index: React.FC = ({ vote }) => { + const support = SUPPORT[vote.choiceID] || SUPPORT["2"]; + return ( + + + + {vote.proposalTitle} + + + + + + Proposal ID: + {" "} + + {formatAddress(vote.proposalId)} + + + + + + Support: + + + {support.text} + + + + + + Weight: + {" "} + + {formatLpt(vote.weight)} + + + + + + Reason: + {" "} + + {vote.reason || "No reason provided"} + + + + + {vote.transactionHash ? ( + e.stopPropagation()} + css={{ + display: "inline-block", + transition: "transform 0.2s ease", + "&:hover": { transform: "scale(1.3)" }, + }} + > + Arbitrum Icon + + ) : ( + N/A + )} + + + ); +}; + +export default Index; diff --git a/components/Votes/VoteModal/index.tsx b/components/Votes/VoteModal/index.tsx new file mode 100644 index 00000000..8fdc871a --- /dev/null +++ b/components/Votes/VoteModal/index.tsx @@ -0,0 +1,79 @@ +"use client"; + +import React from "react"; +import { createPortal } from "react-dom"; +import { Box, Button } from "@livepeer/design-system"; + +interface VoteModalProps { + onClose: () => void; + children: React.ReactNode; +} + +const Index: React.FC = ({ onClose, children }) => + createPortal( + e.stopPropagation()} + > + e.stopPropagation()} + > + + + + + + {children} + + + , + document.body + ); + +export default Index; diff --git a/components/Votes/voterPopover.tsx b/components/Votes/VotePopover/index.tsx similarity index 79% rename from components/Votes/voterPopover.tsx rename to components/Votes/VotePopover/index.tsx index aaa4dad4..59f87cc8 100644 --- a/components/Votes/voterPopover.tsx +++ b/components/Votes/VotePopover/index.tsx @@ -3,16 +3,16 @@ import React from 'react'; import Spinner from '@components/Spinner'; import { Flex, Text } from '@livepeer/design-system'; -import { VoteModal } from './voteModal'; +import VoteModal from '../VoteModal'; import { VoteDetailItem } from './voteDetailItem'; -import { useVoterVotes } from '../../hooks/TreasuryVotes/useVoterVotes'; +import { useVoterVotes } from '../../../hooks/TreasuryVotes/useVoterVotes'; interface VoterPopoverProps { voter: string; onClose: () => void; } -const VoterPopover: React.FC = ({ voter, onClose }) => { +const Index: React.FC = ({ voter, onClose }) => { const { votes, isLoading } = useVoterVotes(voter); return ( @@ -34,4 +34,4 @@ const VoterPopover: React.FC = ({ voter, onClose }) => { ); }; -export default VoterPopover; +export default Index; diff --git a/components/Votes/VoteTable/Views/DesktopVoteTable.tsx b/components/Votes/VoteTable/Views/DesktopVoteTable.tsx new file mode 100644 index 00000000..eb14fb4d --- /dev/null +++ b/components/Votes/VoteTable/Views/DesktopVoteTable.tsx @@ -0,0 +1,96 @@ +import React from "react"; +import { Box, Flex, Text } from "@livepeer/design-system"; +import { Vote } from "../../../../lib/api/types/votes"; +import { VoteView } from "./VoteItem"; + +export interface VoteTableProps { + votes: Vote[]; + counts: { yes: number; no: number; abstain: number }; + formatWeight: (weight: string) => string; + onSelect: (voter: string) => void; +} + +export const DesktopVoteTable: React.FC = ({ + votes, + counts, + formatWeight, + onSelect, +}) => ( + + + Vote Results + + + Yes ({counts.yes}) + | + No ({counts.no}) + | + + Abstain ({counts.abstain}) + + + + + Click on a vote to view a voter's proposal voting history. + + + + + {["Voter", "Support", "Weight", "Reason", "Vote Txn"].map((label) => ( + + {label} + + ))} + + + + {votes.map((vote) => { + return ( + + ); + })} + + + +); diff --git a/components/Votes/VoteTable/Views/MobileVoteTable.tsx b/components/Votes/VoteTable/Views/MobileVoteTable.tsx new file mode 100644 index 00000000..51054814 --- /dev/null +++ b/components/Votes/VoteTable/Views/MobileVoteTable.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import { Flex, Text, Box } from "@livepeer/design-system"; +import { VoteTableProps } from "./DesktopVoteTable"; +import { VoteView } from "./VoteItem"; + +interface MobileVoteCardsProps extends VoteTableProps {} + +export const MobileVoteCards: React.FC = ({ + votes, + counts, + formatWeight, + onSelect, +}) => ( + + + Vote Results + + + Yes ({counts.yes}) + | + No ({counts.no}) + | + + Abstain ({counts.abstain}) + + + + + Click on a vote to view a voter's proposal voting history. + + + {votes.map((vote) => { + return ( + + ); + })} + +); + diff --git a/components/Votes/VoteTable/Views/VoteItem.tsx b/components/Votes/VoteTable/Views/VoteItem.tsx new file mode 100644 index 00000000..03128fff --- /dev/null +++ b/components/Votes/VoteTable/Views/VoteItem.tsx @@ -0,0 +1,219 @@ +import ArbitrumIcon from "../../public/img/logos/arbitrum.png"; +import { Vote, SUPPORT } from "../../../../lib/api/types/votes"; +import { Card, Heading, Link, Text, Box } from "@livepeer/design-system"; +import Image from "next/image"; + + +interface VoteViewProps { + vote: Vote; + onSelect: (voter: string) => void; + formatWeight: (weight: string) => string; + isMobile?: boolean; + } + + export function VoteView({ vote, onSelect, formatWeight, isMobile }: VoteViewProps) { + return isMobile ? ( + + ) : ( + + ); + } + + function MobileVoteView({ vote, onSelect, formatWeight }: VoteViewProps) { + const support = SUPPORT[vote.choiceID] || SUPPORT["2"]; + return ( + { + if ((e.target as HTMLElement).closest("a")) return; + e.stopPropagation(); + onSelect(vote.voter); + }} + > + + e.stopPropagation()} + > + {vote.ensName} + + + + + Support: + + + {support.text} + + + + + Weight: + {" "} + {formatWeight(vote.weight)} + + + + Reason: + {" "} + {vote.reason} + + + {vote.transactionHash ? ( + e.stopPropagation()} + > + Arbitrum Icon + + ) : ( + N/A + )} + + + ); + } + + function DesktopVoteView({ vote, onSelect, formatWeight }: VoteViewProps) { + const support = SUPPORT[vote.choiceID] || SUPPORT["2"]; + return ( + td": { padding: "$2" }, + }} + onClickCapture={(e) => { + if ((e.target as HTMLElement).closest("a")) return; + e.stopPropagation(); + onSelect(vote.voter); + }} + > + + e.stopPropagation()} + > + {vote.ensName} + + + + {support.text} + + + {formatWeight(vote.weight)} + + + {vote.reason} + + + {vote.transactionHash ? ( + e.stopPropagation()} + css={{ + display: "inline-block", + transition: "transform 0.2s ease", + zIndex: 9999, + position: "relative", + "&:hover": { transform: "scale(1.3)", zIndex: 9999 }, + }} + > + Arbitrum Icon + + ) : ( + N/A + )} + + + ); + } + \ No newline at end of file diff --git a/components/Votes/VoteTable/index.tsx b/components/Votes/VoteTable/index.tsx new file mode 100644 index 00000000..18b8a1e5 --- /dev/null +++ b/components/Votes/VoteTable/index.tsx @@ -0,0 +1,93 @@ +"use client"; + +import React, { useState } from "react"; +import { useWindowSize } from "react-use"; +import Spinner from "@components/Spinner"; +import VoterPopover from "../VotePopover"; +import { DesktopVoteTable } from "./Views/DesktopVoteTable"; +import { MobileVoteCards } from "./Views/MobileVoteTable"; +import { Text, Flex } from "@livepeer/design-system"; +import { useFetchVotes } from "../../../hooks/TreasuryVotes/useFetchVotes"; +import { Vote } from "@lib/api/types/votes"; + +interface VoteTableProps { + proposalId: string; +} + +const lptFormatter = new Intl.NumberFormat("en-US", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, +}); + + + +const countVotes = (votes : Vote[]) => { + return { + yes: votes.filter((v) => v.choiceID === "1").length || 0, + no: votes.filter((v) => v.choiceID === "0").length || 0, + abstain: votes.filter((v) => v.choiceID === "2").length || 0, + }; +} + +const Index: React.FC = ({ proposalId }) => { + const { votes, loading, error } = useFetchVotes(proposalId); + const { width } = useWindowSize(); + const isDesktop = width >= 768; + + const [selectedVoter, setSelectedVoter] = useState(null); + const counts = countVotes(votes); + const totalWeight = votes.reduce((sum, v) => sum + parseFloat(v.weight), 0); + + const formatWeight = (w: string) => + `${lptFormatter.format(parseFloat(w) / 1e18)} LPT (${ + totalWeight > 0 ? ((parseFloat(w) / totalWeight) * 100).toFixed(2) : "0" + }%)`; + + if (loading) + return ( + + + + ); + + if (error) + return ( + + Error loading votes: {error} + + ); + + if (!votes.length) + return ( + + No votes found for this proposal. + + ); + + const VoteListView = isDesktop ? DesktopVoteTable : MobileVoteCards; + + return ( + <> + + {selectedVoter && ( + setSelectedVoter(null)} + /> + )} + + ); +}; + +export default Index; diff --git a/components/Votes/VotingWidget/index.tsx b/components/Votes/VotingWidget/index.tsx new file mode 100644 index 00000000..57a6508b --- /dev/null +++ b/components/Votes/VotingWidget/index.tsx @@ -0,0 +1,539 @@ +import { PollExtended } from "@lib/api/polls"; +import { + Box, + Button, + Dialog, + DialogClose, + DialogContent, + DialogTitle, + Flex, + Heading, + Text, + useSnackbar, +} from "@livepeer/design-system"; +import { Cross1Icon } from "@modulz/radix-icons"; +import dayjs from "dayjs"; +import duration from "dayjs/plugin/duration"; +import { useAccountAddress, usePendingFeesAndStakeData } from "hooks"; +import { useEffect, useMemo, useState } from "react"; +import { CopyToClipboard } from "react-copy-to-clipboard"; +import Check from "../../public/img/check.svg"; +import Copy from "../../public/img/copy.svg"; +import { abbreviateNumber } from "@lib/utils"; +import VoteButton from "../VoteButton"; +import { formatPercent, getVotingPower, VotingResponse } from "utils/voting"; + +dayjs.extend(duration); + +const Index = ({ data }: { data: VotingResponse }) => { + const accountAddress = useAccountAddress(); + + const [modalOpen, setModalOpen] = useState(false); + + const pendingFeesAndStake = usePendingFeesAndStakeData( + data?.myAccount?.delegator?.id + ); + + const votingPower = useMemo( + () => + getVotingPower( + accountAddress ?? "", + data?.myAccount, + data?.vote, + pendingFeesAndStake?.pendingStake + ? pendingFeesAndStake?.pendingStake + : "0" + ), + [accountAddress, data, pendingFeesAndStake] + ); + + const delegateId = useMemo( + () => data?.myAccount?.delegator?.delegate?.id ?? "", + [data] + ); + + return ( + + + + + Do you support LIP-{data?.poll?.attributes?.lip ?? "ERR"}? + + + + + + + + Yes + + + {formatPercent(data.poll.percent.yes)} + + + + + + No + + + {formatPercent(data.poll.percent.no)} + + + + + + + {accountAddress ? ( + + ) : ( + + + + Connect your wallet to vote. + + + )} + + + {data.poll.status === "active" && ( + + )} + + + ); +}; + +export default Index; + +interface ActivePollViewProps { + setModalOpen: (open: boolean) => void; +} +function ActivePollView({ setModalOpen }: ActivePollViewProps) { + return ( + + + Are you an orchestrator?{" "} + setModalOpen(true)} + css={{ color: "$primary11", cursor: "pointer" }} + > + Follow these instructions + {" "} + if you prefer to vote with the Livepeer CLI. + + + ); +} + +interface DisplayFormmatedPollVotesProps { + data: VotingResponse; +} +function DisplayFormmatedPollVotes({ data }: DisplayFormmatedPollVotesProps) { + return ( + + {data.poll.votes.length}{" "} + {`${ + data.poll.votes.length > 1 || data.poll.votes.length === 0 + ? "votes" + : "vote" + }`}{" "} + · {abbreviateNumber(data.poll.stake.voters, 4)} LPT ·{" "} + {data.poll.status !== "active" + ? "Final Results" + : dayjs + .duration(dayjs().unix() - data.poll.estimatedEndTime, "seconds") + .humanize() + " left"} + + ); +} + +interface RenderAccountAddressProps { + data: VotingResponse; + votingPower: string | number; + accountAddress: string; + pendingFeesAndStake: any; + delegateId: string; +} + +function RenderAccountAddress({ + data, + votingPower, + accountAddress, + pendingFeesAndStake, + delegateId, +}: RenderAccountAddressProps) { + return ( + <> + + + + My Delegate Vote{" "} + {delegateId && + `(${delegateId.replace(delegateId?.slice(5, 39), "…")})`} + + + {data?.delegateVote?.choiceID + ? data?.delegateVote?.choiceID + : "N/A"} + + + + + My Vote ({accountAddress.replace(accountAddress.slice(5, 39), "…")}) + + + {data?.vote?.choiceID ? data?.vote?.choiceID : "N/A"} + + + {((!data?.vote?.choiceID && data.poll.status === "active") || + data?.vote?.choiceID) && ( + + + My Voting Power + + + + {abbreviateNumber(votingPower, 4)} LPT ( + {( + (+votingPower / + (data.poll.stake.nonVoters + data.poll.stake.voters)) * + 100 + ).toPrecision(2)} + %) + + + + )} + + {data.poll.status === "active" && ( + + )} + + ); +} + +interface LivepeerCLIInstructionsModalProps { + data: PollExtended; + setModalOpen: (open: boolean) => void; + modalOpen: boolean; +} + +function LivepeerCLIInstructionsModal({ + setModalOpen, + modalOpen, + data, +}: LivepeerCLIInstructionsModalProps) { + const [copied, setCopied] = useState(false); + const [openSnackbar] = useSnackbar(); + + useEffect(() => { + if (!copied) return; + + const timer = setTimeout(() => { + setCopied(false); + }, 5000); + + return () => clearTimeout(timer); // cleanup on re-run or unmount + }, [copied]); + + return ( + + + + + + Livepeer CLI Voting Instructions + + + + + + + + + + + Run the Livepeer CLI and select the option to "Vote on a + poll". When prompted for a contract address, copy and paste + this poll's contract address: + + + {data.id} + { + setCopied(true); + openSnackbar("Copied to clipboard"); + }} + > + + {copied ? ( + + ) : ( + + )} + + + + + + + The Livepeer CLI will prompt you for your vote. Enter 0 to vote + "Yes" or 1 to vote "No". + + + + + Once your vote is confirmed, check back here to see it reflected + in the UI. + + + + + + ); +} + +interface RenderVoteButtonProps { + vote: VotingResponse["vote"]; + poll: PollExtended; + pendingStake: string; +} +function RenderVoteButton({ vote, poll, pendingStake }: RenderVoteButtonProps) { + switch (vote?.choiceID) { + case "Yes": + return ( + 0)} + css={{ mt: "$4", width: "100%" }} + variant="red" + size="4" + choiceId={1} + pollAddress={poll?.id} + > + Change Vote To No + + ); + case "No": + return ( + 0)} + css={{ mt: "$4", width: "100%" }} + size="4" + variant="primary" + choiceId={0} + pollAddress={poll?.id} + > + Change Vote To Yes + + ); + default: + return ( + + 0)} + variant="primary" + choiceId={0} + size="4" + pollAddress={poll?.id} + > + Yes + + 0)} + variant="red" + size="4" + choiceId={1} + pollAddress={poll?.id} + > + No + + + ); + } +} diff --git a/components/Votes/desktopVoteTable.tsx b/components/Votes/desktopVoteTable.tsx deleted file mode 100644 index 939ca054..00000000 --- a/components/Votes/desktopVoteTable.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React from 'react'; -import Image from 'next/image'; -import { Box, Flex, Text, Link } from '@livepeer/design-system'; -import ArbitrumIcon from '../../public/img/logos/arbitrum.png'; -import { Vote, SUPPORT } from '../../lib/api/types/votes'; - -interface DesktopVoteTableProps { - votes: Vote[]; - counts: { yes: number; no: number; abstain: number }; - formatWeight: (weight: string) => string; - onSelect: (voter: string) => void; -} - - -export const DesktopVoteTable: React.FC = ({ votes, counts, formatWeight, onSelect }) => ( - - - Vote Results - - - Yes ({counts.yes}) - | - No ({counts.no}) - | - Abstain ({counts.abstain}) - - - - Click on a vote to view a voter's proposal voting history. - - - - - {['Voter', 'Support', 'Weight', 'Reason', 'Vote Txn'].map(label => ( - - {label} - - ))} - - - - {votes.map(vote => { - const support = SUPPORT[vote.choiceID] || SUPPORT['2']; - return ( - td': { padding: '$2' } }} - onClickCapture={e => { if ((e.target as HTMLElement).closest('a')) return; e.stopPropagation(); onSelect(vote.voter); }} - > - - e.stopPropagation()} - > - {vote.ensName} - - - - {support.text} - - - {formatWeight(vote.weight)} - - - {vote.reason} - - - {vote.transactionHash ? ( - e.stopPropagation()} - css={{ display: 'inline-block', transition: 'transform 0.2s ease', zIndex: 9999, position: 'relative', '&:hover': { transform: 'scale(1.3)', zIndex: 9999 } }} - > - Arbitrum Icon - - ) : ( - N/A - )} - - - ); - })} - - - -); \ No newline at end of file diff --git a/components/Votes/mobileVoteCards.tsx b/components/Votes/mobileVoteCards.tsx deleted file mode 100644 index a3909912..00000000 --- a/components/Votes/mobileVoteCards.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React from 'react'; -import Image from 'next/image'; -import { Card, Flex, Heading, Link, Text, Box } from '@livepeer/design-system'; -import ArbitrumIcon from '../../public/img/logos/arbitrum.png'; -import { Vote, SUPPORT } from '../../lib/api/types/votes'; - -interface MobileVoteCardsProps { - votes: Vote[]; - counts: { yes: number; no: number; abstain: number }; - formatWeight: (weight: string) => string; - onSelect: (voter: string) => void; -} - - -export const MobileVoteCards: React.FC = ({ votes, counts, formatWeight, onSelect }) => ( - - - Vote Results - - - Yes ({counts.yes}) - | - No ({counts.no}) - | - Abstain ({counts.abstain}) - - - - Click on a vote to view a voter's proposal voting history. - - - {votes.map(vote => { - const support = SUPPORT[vote.choiceID] || SUPPORT['2']; - return ( - { if ((e.target as HTMLElement).closest('a')) return; e.stopPropagation(); onSelect(vote.voter); }} - > - - e.stopPropagation()} - > - {vote.ensName} - - - - - Support: - {support.text} - - - Weight: {formatWeight(vote.weight)} - - - Reason: {vote.reason} - - - {vote.transactionHash ? ( - e.stopPropagation()} - > - Arbitrum Icon - - ) : ( - N/A - )} - - - ); - })} - -); \ No newline at end of file diff --git a/components/Votes/voteDetailItem.tsx b/components/Votes/voteDetailItem.tsx deleted file mode 100644 index 8b370890..00000000 --- a/components/Votes/voteDetailItem.tsx +++ /dev/null @@ -1,91 +0,0 @@ -'use client'; - -import React from 'react'; -import Image from 'next/image'; -import { Box, Heading, Text, Link } from '@livepeer/design-system'; -import ArbitrumIcon from '../../public/img/logos/arbitrum.png'; -import { SUPPORT, Vote } from '../../lib/api/types/votes'; -import { formatAddress } from '../../utils/formatAddress'; - -const lptFormatter = new Intl.NumberFormat('en-US', { - minimumFractionDigits: 2, - maximumFractionDigits: 2, -}); - -function formatLpt(w: string) { - return `${lptFormatter.format(parseFloat(w) / 1e18)} LPT`; -} - -interface VoteDetailItemProps { - vote: Vote; -} - -export const VoteDetailItem: React.FC = ({ vote }) => { - const support = SUPPORT[vote.choiceID] || SUPPORT['2']; - return ( - - - - {vote.proposalTitle} - - - - - Proposal ID:{' '} - - {formatAddress(vote.proposalId)} - - - - - - Support: - - - {support.text} - - - - - - Weight:{' '} - - {formatLpt(vote.weight)} - - - - - Reason:{' '} - - {vote.reason || 'No reason provided'} - - - - - {vote.transactionHash ? ( - e.stopPropagation()} - css={{ display: 'inline-block', transition: 'transform 0.2s ease', '&:hover': { transform: 'scale(1.3)' } }} - > - Arbitrum Icon - - ) : ( - N/A - )} - - - ); -}; \ No newline at end of file diff --git a/components/Votes/voteModal.tsx b/components/Votes/voteModal.tsx deleted file mode 100644 index dbc50606..00000000 --- a/components/Votes/voteModal.tsx +++ /dev/null @@ -1,73 +0,0 @@ -'use client'; - -import React from 'react'; -import { createPortal } from 'react-dom'; -import { Box, Button } from '@livepeer/design-system'; - -interface VoteModalProps { - onClose: () => void; - children: React.ReactNode; -} - -export const VoteModal: React.FC = ({ onClose, children }) => - createPortal( - e.stopPropagation()} - > - e.stopPropagation()} - > - - - - - - {children} - - - , - document.body - ); diff --git a/components/Votes/voteTable.tsx b/components/Votes/voteTable.tsx deleted file mode 100644 index f45e44ea..00000000 --- a/components/Votes/voteTable.tsx +++ /dev/null @@ -1,76 +0,0 @@ -"use client"; - -import React, { useState } from 'react'; -import { useWindowSize } from 'react-use'; -import Spinner from '@components/Spinner'; -import { DesktopVoteTable } from './desktopVoteTable'; -import { MobileVoteCards } from './mobileVoteCards'; -import VoterPopover from './voterPopover'; -import { Text, Flex } from '@livepeer/design-system'; -import { useFetchVotes } from '../../hooks/TreasuryVotes/useFetchVotes'; -interface VoteTableProps { - proposalId: string; -} - -const lptFormatter = new Intl.NumberFormat('en-US', { - minimumFractionDigits: 2, - maximumFractionDigits: 2, -}); - -export const VoteTable: React.FC = ({ proposalId }) => { - const { votes, loading, error } = useFetchVotes(proposalId); - const { width } = useWindowSize(); - const isDesktop = width >= 768; - - const [selectedVoter, setSelectedVoter] = useState(null); - - const counts = { - yes: votes.filter(v => v.choiceID === '1').length, - no: votes.filter(v => v.choiceID === '0').length, - abstain: votes.filter(v => v.choiceID === '2').length, - }; - - const totalWeight = votes.reduce( - (sum, v) => sum + parseFloat(v.weight), - 0 - ); - - const formatWeight = (w: string) => - `${lptFormatter.format(parseFloat(w) / 1e18)} LPT (${ - totalWeight > 0 - ? ((parseFloat(w) / totalWeight) * 100).toFixed(2) - : '0' - }%)`; - - if (loading) return ( - - ); - - if (error) return ( - - Error loading votes: {error} - - ); - - if (!votes.length) return ( - - No votes found for this proposal. - - ); - - const VoteListView = isDesktop ? DesktopVoteTable : MobileVoteCards; - - return ( - <> - - {selectedVoter && setSelectedVoter(null)} />} - - ); -}; - -export default VoteTable; diff --git a/components/VotingWidget/index.tsx b/components/VotingWidget/index.tsx deleted file mode 100644 index 808c37b1..00000000 --- a/components/VotingWidget/index.tsx +++ /dev/null @@ -1,532 +0,0 @@ -import { PollExtended } from "@lib/api/polls"; -import { - Box, - Button, - Dialog, - DialogClose, - DialogContent, - DialogTitle, - Flex, - Heading, - Text, - useSnackbar, -} from "@livepeer/design-system"; -import { Cross1Icon } from "@modulz/radix-icons"; -import { AccountQuery, PollChoice } from "apollo"; -import dayjs from "dayjs"; -import duration from "dayjs/plugin/duration"; -import { useAccountAddress, usePendingFeesAndStakeData } from "hooks"; -import numeral from "numeral"; -import { useEffect, useMemo, useState } from "react"; -import { CopyToClipboard } from "react-copy-to-clipboard"; -import { abbreviateNumber, fromWei } from "../../lib/utils"; -import Check from "../../public/img/check.svg"; -import Copy from "../../public/img/copy.svg"; -import VoteButton from "../VoteButton"; - -dayjs.extend(duration); - -type Props = { - poll: PollExtended; - delegateVote: - | { - __typename: "Vote"; - choiceID?: PollChoice; - voteStake: string; - nonVoteStake: string; - } - | undefined - | null; - vote: - | { - __typename: "Vote"; - choiceID?: PollChoice; - voteStake: string; - nonVoteStake: string; - } - | undefined - | null; - myAccount: AccountQuery; -}; - -const formatPercent = (percent: number) => numeral(percent).format("0.0000%"); - -const Index = ({ data }: { data: Props }) => { - const accountAddress = useAccountAddress(); - const [copied, setCopied] = useState(false); - const [modalOpen, setModalOpen] = useState(false); - const [openSnackbar] = useSnackbar(); - - useEffect(() => { - if (copied) { - setTimeout(() => { - setCopied(false); - }, 5000); - } - }, [copied]); - - const pendingFeesAndStake = usePendingFeesAndStakeData( - data?.myAccount?.delegator?.id - ); - - const votingPower = useMemo( - () => - getVotingPower( - accountAddress ?? "", - data?.myAccount, - data?.vote, - pendingFeesAndStake?.pendingStake - ? pendingFeesAndStake?.pendingStake - : "0" - ), - [accountAddress, data, pendingFeesAndStake] - ); - - let delegate: any = null; - if (data?.myAccount?.delegator?.delegate) { - delegate = data?.myAccount?.delegator?.delegate; - } - - return ( - - - - - Do you support LIP-{data?.poll?.attributes?.lip ?? "ERR"}? - - - - - - - - Yes - - - {formatPercent(data.poll.percent.yes)} - - - - - - No - - - {formatPercent(data.poll.percent.no)} - - - - - {data.poll.votes.length}{" "} - {`${ - data.poll.votes.length > 1 || data.poll.votes.length === 0 - ? "votes" - : "vote" - }`}{" "} - · {abbreviateNumber(data.poll.stake.voters, 4)} LPT ·{" "} - {data.poll.status !== "active" - ? "Final Results" - : dayjs - .duration( - dayjs().unix() - data.poll.estimatedEndTime, - "seconds" - ) - .humanize() + " left"} - - - - {accountAddress ? ( - <> - - - - My Delegate Vote{" "} - {delegate && - `(${delegate?.id?.replace( - delegate?.id?.slice(5, 39), - "…" - )})`} - - - {data?.delegateVote?.choiceID - ? data?.delegateVote?.choiceID - : "N/A"} - - - - - My Vote ( - {accountAddress.replace(accountAddress.slice(5, 39), "…")}) - - - {data?.vote?.choiceID ? data?.vote?.choiceID : "N/A"} - - - {((!data?.vote?.choiceID && data.poll.status === "active") || - data?.vote?.choiceID) && ( - - - My Voting Power - - - - {abbreviateNumber(votingPower, 4)} LPT ( - {( - (+votingPower / - (data.poll.stake.nonVoters + - data.poll.stake.voters)) * - 100 - ).toPrecision(2)} - %) - - - - )} - - {data.poll.status === "active" && - data && - renderVoteButton( - data?.myAccount, - data?.vote, - data?.poll, - pendingFeesAndStake?.pendingStake ?? "" - )} - - ) : ( - - - - Connect your wallet to vote. - - - )} - - - {data.poll.status === "active" && ( - - - Are you an orchestrator?{" "} - setModalOpen(true)} - css={{ color: "$primary11", cursor: "pointer" }} - > - Follow these instructions - {" "} - if you prefer to vote with the Livepeer CLI. - - - )} - - - - - - Livepeer CLI Voting Instructions - - - - - - - - - - - Run the Livepeer CLI and select the option to "Vote on a - poll". When prompted for a contract address, copy and paste - this poll's contract address: - - - {data.poll.id} - { - setCopied(true); - openSnackbar("Copied to clipboard"); - }} - > - - {copied ? ( - - ) : ( - - )} - - - - - - - The Livepeer CLI will prompt you for your vote. Enter 0 to vote - "Yes" or 1 to vote "No". - - - - - Once your vote is confirmed, check back here to see it reflected - in the UI. - - - - - - - ); -}; - -export default Index; - -function renderVoteButton( - myAccount: Props["myAccount"], - vote: Props["vote"], - poll: Props["poll"], - pendingStake: string -) { - switch (vote?.choiceID) { - case "Yes": - return ( - 0)} - css={{ mt: "$4", width: "100%" }} - variant="red" - size="4" - choiceId={1} - pollAddress={poll?.id} - > - Change Vote To No - - ); - case "No": - return ( - 0)} - css={{ mt: "$4", width: "100%" }} - size="4" - variant="primary" - choiceId={0} - pollAddress={poll?.id} - > - Change Vote To Yes - - ); - default: - return ( - - 0)} - variant="primary" - choiceId={0} - size="4" - pollAddress={poll?.id} - > - Yes - - 0)} - variant="red" - size="4" - choiceId={1} - pollAddress={poll?.id} - > - No - - - ); - } -} - -function getVotingPower( - accountAddress: string, - myAccount: Props["myAccount"], - vote: Props["vote"], - pendingStake?: string -) { - // if account is a delegate its voting power is its total stake minus its delegators' vote stake (nonVoteStake) - if (accountAddress === myAccount?.delegator?.delegate?.id) { - if (vote?.voteStake) { - return +vote.voteStake - +vote?.nonVoteStake; - } - return ( - +myAccount?.delegator?.delegate?.totalStake - - (vote?.nonVoteStake ? +vote?.nonVoteStake : 0) - ); - } - - return fromWei(pendingStake ? pendingStake : "0"); -} diff --git a/pages/treasury/[proposal].tsx b/pages/treasury/[proposal].tsx index 9479e2c9..8728a9a6 100644 --- a/pages/treasury/[proposal].tsx +++ b/pages/treasury/[proposal].tsx @@ -36,7 +36,7 @@ import { sentenceCase } from "change-case"; import relativeTime from "dayjs/plugin/relativeTime"; import numeral from "numeral"; import { BadgeVariantByState } from "@components/TreasuryProposalRow"; -import VoteList from "@components/Votes/voteTable"; +import VoteList from "@components/Votes/VoteTable"; import TreasuryVotingWidget from "@components/TreasuryVotingWidget"; import { getProposalExtended } from "@lib/api/treasury"; import { decodeFunctionData } from "viem"; diff --git a/utils/voting.ts b/utils/voting.ts new file mode 100644 index 00000000..14634132 --- /dev/null +++ b/utils/voting.ts @@ -0,0 +1,51 @@ +import { PollExtended } from "@lib/api/polls"; +import { fromWei } from "@lib/utils"; +import { AccountQuery, PollChoice } from "apollo"; +import numeral from "numeral"; +export type VotingResponse = { + poll: PollExtended; + delegateVote: + | { + __typename: "Vote"; + choiceID?: PollChoice; + voteStake: string; + nonVoteStake: string; + } + | undefined + | null; + vote: + | { + __typename: "Vote"; + choiceID?: PollChoice; + voteStake: string; + nonVoteStake: string; + } + | undefined + | null; + myAccount: AccountQuery; +}; + +export const formatPercent = (percent: number) => numeral(percent).format("0.0000%"); + + +export function getVotingPower( + accountAddress: string, + myAccount: VotingResponse["myAccount"], + vote: VotingResponse["vote"], + pendingStake?: string + ) { + // if account is a delegate its voting power is its total stake minus its delegators' vote stake (nonVoteStake) + if (accountAddress === myAccount?.delegator?.delegate?.id) { + if (vote?.voteStake) { + return +vote.voteStake - +vote?.nonVoteStake; + } + return ( + +myAccount?.delegator?.delegate?.totalStake - + (vote?.nonVoteStake ? +vote?.nonVoteStake : 0) + ); + } + + return fromWei(pendingStake ? pendingStake : "0"); + } + + \ No newline at end of file From 37eb3986f0116852118dfa373278e7851d8a165e Mon Sep 17 00:00:00 2001 From: rozzrr Date: Fri, 23 May 2025 09:05:18 +0100 Subject: [PATCH 30/37] address import changes and typing --- components/Votes/VoteDetail/index.tsx | 16 +++--------- components/Votes/VoteModal/index.tsx | 2 -- components/Votes/VotePopover/index.tsx | 4 +-- components/Votes/VoteTable/Views/VoteItem.tsx | 10 ++++---- components/Votes/VoteTable/index.tsx | 12 +++------ hooks/TreasuryVotes/useVoterVotes.ts | 25 +++++++++++-------- lib/api/types/votes.ts | 5 ++-- lib/chains.ts | 4 --- lib/{utils.tsx => utils.ts} | 9 +++++++ utils/voting.ts | 2 ++ 10 files changed, 42 insertions(+), 47 deletions(-) rename lib/{utils.tsx => utils.ts} (98%) diff --git a/components/Votes/VoteDetail/index.tsx b/components/Votes/VoteDetail/index.tsx index fdcf1ae2..67dc3b56 100644 --- a/components/Votes/VoteDetail/index.tsx +++ b/components/Votes/VoteDetail/index.tsx @@ -3,25 +3,17 @@ import React from "react"; import Image from "next/image"; import { Box, Heading, Text, Link } from "@livepeer/design-system"; -import ArbitrumIcon from "../../public/img/logos/arbitrum.png"; -import { SUPPORT, Vote } from "@lib/api/types/votes"; +import ArbitrumIcon from "../../../public/img/logos/arbitrum.png"; +import { VOTING_SUPPORT, Vote } from "@lib/api/types/votes"; import { formatAddress } from "utils/formatAddress"; - -const lptFormatter = new Intl.NumberFormat("en-US", { - minimumFractionDigits: 2, - maximumFractionDigits: 2, -}); - -function formatLpt(w: string) { - return `${lptFormatter.format(parseFloat(w) / 1e18)} LPT`; -} +import { formatLpt } from "@lib/utils"; interface VoteDetailItemProps { vote: Vote; } const Index: React.FC = ({ vote }) => { - const support = SUPPORT[vote.choiceID] || SUPPORT["2"]; + const support = VOTING_SUPPORT[vote.choiceID] || VOTING_SUPPORT["2"]; return ( = ({ voter, onClose }) => { ) : votes.length > 0 ? ( votes.map((vote, idx) => ( - + )) ) : ( diff --git a/components/Votes/VoteTable/Views/VoteItem.tsx b/components/Votes/VoteTable/Views/VoteItem.tsx index 03128fff..daeb90d1 100644 --- a/components/Votes/VoteTable/Views/VoteItem.tsx +++ b/components/Votes/VoteTable/Views/VoteItem.tsx @@ -1,5 +1,5 @@ -import ArbitrumIcon from "../../public/img/logos/arbitrum.png"; -import { Vote, SUPPORT } from "../../../../lib/api/types/votes"; +import ArbitrumIcon from "../../../../public/img/logos/arbitrum.png"; +import { Vote, VOTING_SUPPORT } from "../../../../lib/api/types/votes"; import { Card, Heading, Link, Text, Box } from "@livepeer/design-system"; import Image from "next/image"; @@ -28,11 +28,11 @@ interface VoteViewProps { } function MobileVoteView({ vote, onSelect, formatWeight }: VoteViewProps) { - const support = SUPPORT[vote.choiceID] || SUPPORT["2"]; + const support = VOTING_SUPPORT[vote.choiceID] || VOTING_SUPPORT["2"]; return ( { return { yes: votes.filter((v) => v.choiceID === "1").length || 0, @@ -43,7 +37,7 @@ const Index: React.FC = ({ proposalId }) => { totalWeight > 0 ? ((parseFloat(w) / totalWeight) * 100).toFixed(2) : "0" }%)`; - if (loading) + if (loading){ return ( = ({ proposalId }) => { ); - + } if (error) return ( diff --git a/hooks/TreasuryVotes/useVoterVotes.ts b/hooks/TreasuryVotes/useVoterVotes.ts index 0f273fe4..dc424982 100644 --- a/hooks/TreasuryVotes/useVoterVotes.ts +++ b/hooks/TreasuryVotes/useVoterVotes.ts @@ -3,12 +3,12 @@ import { provider, VOTECAST_TOPIC0, contractInterface, CONTRACT_ADDRESS } from ' import { useQuery } from '@apollo/client' import { ethers } from "ethers"; import { GET_PROPOSALS_BY_IDS } from "../../apollo/treasuryProposals"; -import { Vote } from '../../lib/api/types/votes' +import { Vote } from 'apollo/subgraph'; export function useVoterVotes(voter: string) { const [logsLoading, setLogsLoading] = useState(true) const [rawVotes, setRawVotes] = useState([]) - const proposalIds = useMemo(() => Array.from(new Set(rawVotes.map(v => v.proposalId))), [rawVotes]) + const proposalIds = useMemo(() => Array.from(new Set(rawVotes.map(v => v.poll?.id))), [rawVotes]) useEffect(() => { let cancelled = false @@ -21,14 +21,17 @@ export function useVoterVotes(voter: string) { const decoded = logs.map(log => { const args = contractInterface.parseLog(log).args return { - transactionHash: log.transactionHash, - voter: args.voter, - choiceID: args.support.toString(), - proposalId: args.proposalId.toString(), - weight: args.weight.toString(), - reason: args.reason || '', - } - }) + + id: log.transactionHash, + voter: args.voter, + choiceID: args.choiceID.toString(), + poll : args.poll, + registeredTranscoder: args.registeredTranscoder, + voteStake: args.voteStake.toString(), + nonVoteStake: args.nonVoteStake.toString(), + __typename : args.__typename, + } + }) setRawVotes(decoded) } catch(e) { console.error(e) @@ -53,7 +56,7 @@ export function useVoterVotes(voter: string) { }) return rawVotes .map(r => { - const meta = map.get(r.proposalId) ?? { description: '', voteEnd: 0 } + const meta = map.get(r.poll?.id ?? "") ?? { description: '', voteEnd: 0 } const title = (meta.description.split('\n')[0] || '').replace(/^#\s*/, '') || 'Unknown Proposal' return { ...r, endVote: meta.voteEnd, description: meta.description, proposalTitle: title } }) diff --git a/lib/api/types/votes.ts b/lib/api/types/votes.ts index 6500a17a..c5eaee8e 100644 --- a/lib/api/types/votes.ts +++ b/lib/api/types/votes.ts @@ -1,3 +1,4 @@ + export interface Vote { transactionHash?: string; voter: string; @@ -11,10 +12,10 @@ export interface Vote { proposalTitle?: string; } -export const SUPPORT = { +export const VOTING_SUPPORT = { '0': { text: 'No', style: { color: '$red9', fontWeight: 600 } }, '1': { text: 'Yes', style: { color: '$green9', fontWeight: 600 } }, '2': { text: 'Abstain',style: { color: '$yellow9', fontWeight: 600 } }, } as const; -export type SupportKey = keyof typeof SUPPORT; \ No newline at end of file +export type SupportKey = keyof typeof VOTING_SUPPORT; \ No newline at end of file diff --git a/lib/chains.ts b/lib/chains.ts index ee9f786a..96f4933a 100644 --- a/lib/chains.ts +++ b/lib/chains.ts @@ -5,10 +5,6 @@ import * as chain from "@wagmi/core/chains"; import { ethers } from "ethers"; import { Address, - Client, - HttpTransport, - PublicActions, - PublicRpcSchema, createPublicClient, http, } from "viem"; diff --git a/lib/utils.tsx b/lib/utils.ts similarity index 98% rename from lib/utils.tsx rename to lib/utils.ts index 2ea633f7..ee0dae15 100644 --- a/lib/utils.tsx +++ b/lib/utils.ts @@ -5,6 +5,15 @@ import { StakingAction } from "hooks"; import { CHAIN_INFO, DEFAULT_CHAIN_ID, INFURA_NETWORK_URLS } from "lib/chains"; import Numeral from "numeral"; +export const lptFormatter = new Intl.NumberFormat("en-US", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, +}); + +export const formatLpt = (w: string) => { + return `${lptFormatter.format(parseFloat(w) / 1e18)} LPT`; +} + export const provider = new ethers.providers.JsonRpcProvider( INFURA_NETWORK_URLS[DEFAULT_CHAIN_ID] ); diff --git a/utils/voting.ts b/utils/voting.ts index 14634132..2c78cd09 100644 --- a/utils/voting.ts +++ b/utils/voting.ts @@ -25,6 +25,8 @@ export type VotingResponse = { myAccount: AccountQuery; }; + + export const formatPercent = (percent: number) => numeral(percent).format("0.0000%"); From ec61856a49e1d6668974f04aa27295f642ed4451 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Fri, 23 May 2025 12:40:46 +0100 Subject: [PATCH 31/37] useInfuraVoterVotes -Infura used for votes here not subgraph as data isnt available for all votes -Vote reverted back to using interface within lib/api/types/votes --- components/Votes/VotePopover/index.tsx | 6 ++--- ...seVoterVotes.ts => useInfuraVoterVotes.ts} | 26 +++++++++---------- 2 files changed, 15 insertions(+), 17 deletions(-) rename hooks/TreasuryVotes/{useVoterVotes.ts => useInfuraVoterVotes.ts} (78%) diff --git a/components/Votes/VotePopover/index.tsx b/components/Votes/VotePopover/index.tsx index c7250472..4b86e1ab 100644 --- a/components/Votes/VotePopover/index.tsx +++ b/components/Votes/VotePopover/index.tsx @@ -4,8 +4,8 @@ import React from 'react'; import Spinner from '@components/Spinner'; import { Flex, Text } from '@livepeer/design-system'; import VoteModal from '../VoteModal'; -import VoteDetail from '../VoteDetail'; -import { useVoterVotes } from '../../../hooks/TreasuryVotes/useVoterVotes'; +import VoteDetail from '../VoteDetail'; +import { useInfuraVoterVotes } from '../../../hooks/TreasuryVotes/useInfuraVoterVotes'; interface VoterPopoverProps { voter: string; @@ -13,7 +13,7 @@ interface VoterPopoverProps { } const Index: React.FC = ({ voter, onClose }) => { - const { votes, isLoading } = useVoterVotes(voter); + const { votes, isLoading } = useInfuraVoterVotes(voter); return ( diff --git a/hooks/TreasuryVotes/useVoterVotes.ts b/hooks/TreasuryVotes/useInfuraVoterVotes.ts similarity index 78% rename from hooks/TreasuryVotes/useVoterVotes.ts rename to hooks/TreasuryVotes/useInfuraVoterVotes.ts index dc424982..2ee20d7e 100644 --- a/hooks/TreasuryVotes/useVoterVotes.ts +++ b/hooks/TreasuryVotes/useInfuraVoterVotes.ts @@ -3,12 +3,12 @@ import { provider, VOTECAST_TOPIC0, contractInterface, CONTRACT_ADDRESS } from ' import { useQuery } from '@apollo/client' import { ethers } from "ethers"; import { GET_PROPOSALS_BY_IDS } from "../../apollo/treasuryProposals"; -import { Vote } from 'apollo/subgraph'; +import { Vote } from '../../lib/api/types/votes'; -export function useVoterVotes(voter: string) { +export function useInfuraVoterVotes(voter: string) { const [logsLoading, setLogsLoading] = useState(true) const [rawVotes, setRawVotes] = useState([]) - const proposalIds = useMemo(() => Array.from(new Set(rawVotes.map(v => v.poll?.id))), [rawVotes]) + const proposalIds = useMemo(() => Array.from(new Set(rawVotes.map(v => v.proposalId))), [rawVotes]) useEffect(() => { let cancelled = false @@ -22,16 +22,14 @@ export function useVoterVotes(voter: string) { const args = contractInterface.parseLog(log).args return { - id: log.transactionHash, - voter: args.voter, - choiceID: args.choiceID.toString(), - poll : args.poll, - registeredTranscoder: args.registeredTranscoder, - voteStake: args.voteStake.toString(), - nonVoteStake: args.nonVoteStake.toString(), - __typename : args.__typename, - } - }) + transactionHash: log.transactionHash, + voter: args.voter, + choiceID: args.support.toString(), + proposalId: args.proposalId.toString(), + weight: args.weight.toString(), + reason: args.reason || '', + } + }) setRawVotes(decoded) } catch(e) { console.error(e) @@ -56,7 +54,7 @@ export function useVoterVotes(voter: string) { }) return rawVotes .map(r => { - const meta = map.get(r.poll?.id ?? "") ?? { description: '', voteEnd: 0 } + const meta = map.get(r.proposalId) ?? { description: '', voteEnd: 0 } const title = (meta.description.split('\n')[0] || '').replace(/^#\s*/, '') || 'Unknown Proposal' return { ...r, endVote: meta.voteEnd, description: meta.description, proposalTitle: title } }) From db7bb3d3dac44c43239718020de2be2e26af8198 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Wed, 28 May 2025 21:03:06 +0100 Subject: [PATCH 32/37] PR ready --- components/Votes/VotePopover/index.tsx | 2 +- hooks/TreasuryVotes/useInfuraVoterVotes.ts | 6 +++--- hooks/useSwr.tsx | 5 +---- lib/api/types/votes.ts | 4 ++-- lib/axios.ts | 3 +-- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/components/Votes/VotePopover/index.tsx b/components/Votes/VotePopover/index.tsx index 4b86e1ab..5dfc0c3a 100644 --- a/components/Votes/VotePopover/index.tsx +++ b/components/Votes/VotePopover/index.tsx @@ -23,7 +23,7 @@ const Index: React.FC = ({ voter, onClose }) => { ) : votes.length > 0 ? ( votes.map((vote, idx) => ( - + )) ) : ( diff --git a/hooks/TreasuryVotes/useInfuraVoterVotes.ts b/hooks/TreasuryVotes/useInfuraVoterVotes.ts index 2ee20d7e..8b4f240c 100644 --- a/hooks/TreasuryVotes/useInfuraVoterVotes.ts +++ b/hooks/TreasuryVotes/useInfuraVoterVotes.ts @@ -18,7 +18,7 @@ export function useInfuraVoterVotes(voter: string) { const topic = ethers.utils.zeroPad(voter, 32) const logs = await provider.getLogs({ address: CONTRACT_ADDRESS, fromBlock:'earliest', toBlock:'latest', topics: [VOTECAST_TOPIC0, ethers.utils.hexlify(topic)] }) if (cancelled) return - const decoded = logs.map(log => { + const transactions = logs.map(log => { const args = contractInterface.parseLog(log).args return { @@ -27,10 +27,10 @@ export function useInfuraVoterVotes(voter: string) { choiceID: args.support.toString(), proposalId: args.proposalId.toString(), weight: args.weight.toString(), - reason: args.reason || '', + reason: args.reason ?? "", } }) - setRawVotes(decoded) + setRawVotes(transactions) } catch(e) { console.error(e) } finally { diff --git a/hooks/useSwr.tsx b/hooks/useSwr.tsx index bae5ce6c..6c417e9e 100644 --- a/hooks/useSwr.tsx +++ b/hooks/useSwr.tsx @@ -81,10 +81,7 @@ export const useScoreData = (address: string | undefined | null) => { export const useCurrentRoundData = () => { const { data } = useSWR(`/current-round`, { - // refreshInterval: 10000, - refreshInterval: 120000, - revalidateOnFocus: false, - dedupingInterval: 120000, + refreshInterval: 10000, }); return data ?? null; diff --git a/lib/api/types/votes.ts b/lib/api/types/votes.ts index c5eaee8e..d17796d0 100644 --- a/lib/api/types/votes.ts +++ b/lib/api/types/votes.ts @@ -1,11 +1,11 @@ export interface Vote { - transactionHash?: string; + transactionHash: string; voter: string; choiceID: string; proposalId: string; weight: string; - reason: string; + reason?: string; ensName?: string; endVote?: number; description?: string; diff --git a/lib/axios.ts b/lib/axios.ts index d33901fd..35d6946e 100644 --- a/lib/axios.ts +++ b/lib/axios.ts @@ -2,8 +2,7 @@ import { default as defaultAxios } from "axios"; export const axiosClient = defaultAxios.create({ baseURL: "/api", - // timeout: 10000, - timeout: 120000, + timeout: 10000, }); From 18ee2f6ecf02b174431ff72d6168605b3ed9baf0 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Wed, 28 May 2025 21:13:11 +0100 Subject: [PATCH 33/37] yarn restore --- yarn.lock | 876 ++++++++++-------------------------------------------- 1 file changed, 154 insertions(+), 722 deletions(-) diff --git a/yarn.lock b/yarn.lock index d8f3d5cb..5d36753b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,26 +15,7 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@apollo/client@^3.13.1": - version "3.13.5" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.13.5.tgz#588ac60c751c38d50ce0286fcefcc4994aa9c5d6" - integrity sha512-ceHa1lApLAiGmUur4V+G/CrjwVwHYujfB7U5HM++poCgHpfGn6eet8YGM93fgeWjYX85SaqwdZbQk18IVwhRHg== - dependencies: - "@graphql-typed-document-node/core" "^3.1.1" - "@wry/caches" "^1.0.0" - "@wry/equality" "^0.5.6" - "@wry/trie" "^0.5.0" - graphql-tag "^2.12.6" - hoist-non-react-statics "^3.3.2" - optimism "^0.18.0" - prop-types "^15.7.2" - rehackt "^0.1.0" - symbol-observable "^4.0.0" - ts-invariant "^0.10.3" - tslib "^2.3.0" - zen-observable-ts "^1.2.5" - -"@apollo/client@~3.2.5 || ~3.3.0 || ~3.4.0 || ~3.5.0 || ~3.6.0": +"@apollo/client@^3.5.8", "@apollo/client@~3.2.5 || ~3.3.0 || ~3.4.0 || ~3.5.0 || ~3.6.0": version "3.6.9" resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.6.9.tgz#ad0ee2e3a3c92dbed4acd6917b6158a492739d94" integrity sha512-Y1yu8qa2YeaCUBVuw08x8NHenFi0sw2I3KCu7Kw9mDSu86HmmtHJkCAifKVrN2iPgDTW/BbP3EpSV8/EQCcxZA== @@ -3415,13 +3396,6 @@ dependencies: "@types/estree" "*" -"@types/estree-jsx@^1.0.0": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-1.0.5.tgz#858a88ea20f34fe65111f005a689fa1ebf70dc18" - integrity sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg== - dependencies: - "@types/estree" "*" - "@types/estree@*": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" @@ -3432,11 +3406,6 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== -"@types/estree@^1.0.0": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.7.tgz#4158d3105276773d5b7695cd4834b1722e4f37a8" - integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ== - "@types/hast@^2.0.0": version "2.3.4" resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc" @@ -3444,13 +3413,6 @@ dependencies: "@types/unist" "*" -"@types/hast@^3.0.0": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" - integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== - dependencies: - "@types/unist" "*" - "@types/hoist-non-react-statics@^3.0.1": version "3.3.1" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" @@ -3503,13 +3465,6 @@ dependencies: "@types/unist" "*" -"@types/mdast@^4.0.0": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6" - integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== - dependencies: - "@types/unist" "*" - "@types/mdurl@^1.0.0": version "1.0.2" resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9" @@ -3555,7 +3510,7 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.3.tgz#68ada76827b0010d0db071f739314fa429943d0a" integrity sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg== -"@types/prop-types@*": +"@types/prop-types@*", "@types/prop-types@^15.0.0": version "15.7.5" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== @@ -3636,11 +3591,6 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== -"@types/unist@^3.0.0": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" - integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== - "@types/use-sync-external-store@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" @@ -3704,11 +3654,6 @@ "@typescript-eslint/types" "5.30.7" eslint-visitor-keys "^3.3.0" -"@ungap/structured-clone@^1.0.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" - integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== - "@vanilla-extract/css@1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@vanilla-extract/css/-/css-1.9.1.tgz#337b79faa5f8f98915a90c3fe3c30b54be746c09" @@ -4178,13 +4123,6 @@ undici "5.5.1" web-streams-polyfill "^3.2.0" -"@wry/caches@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@wry/caches/-/caches-1.0.1.tgz#8641fd3b6e09230b86ce8b93558d44cf1ece7e52" - integrity sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA== - dependencies: - tslib "^2.3.0" - "@wry/context@^0.6.0": version "0.6.1" resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.6.1.tgz#c3c29c0ad622adb00f6a53303c4f965ee06ebeb2" @@ -4192,13 +4130,6 @@ dependencies: tslib "^2.3.0" -"@wry/context@^0.7.0": - version "0.7.4" - resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.4.tgz#e32d750fa075955c4ab2cfb8c48095e1d42d5990" - integrity sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ== - dependencies: - tslib "^2.3.0" - "@wry/equality@^0.1.2": version "0.1.11" resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.1.11.tgz#35cb156e4a96695aa81a9ecc4d03787bc17f1790" @@ -4213,13 +4144,6 @@ dependencies: tslib "^2.3.0" -"@wry/equality@^0.5.6": - version "0.5.7" - resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.7.tgz#72ec1a73760943d439d56b7b1e9985aec5d497bb" - integrity sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw== - dependencies: - tslib "^2.3.0" - "@wry/trie@^0.3.0": version "0.3.1" resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.3.1.tgz#2279b790f15032f8bcea7fc944d27988e5b3b139" @@ -4227,13 +4151,6 @@ dependencies: tslib "^2.3.0" -"@wry/trie@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.5.0.tgz#11e783f3a53f6e4cd1d42d2d1323f5bc3fa99c94" - integrity sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA== - dependencies: - tslib "^2.3.0" - "@xobotyi/scrollbar-width@^1.9.5": version "1.9.5" resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" @@ -5737,13 +5654,6 @@ detect-node-es@^1.1.0: resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== -devlop@^1.0.0, devlop@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" - integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== - dependencies: - dequal "^2.0.0" - diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -6282,11 +6192,6 @@ estree-util-is-identifier-name@^2.0.0: resolved "https://registry.yarnpkg.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-2.0.1.tgz#cf07867f42705892718d9d89eb2d85eaa8f0fcb5" integrity sha512-rxZj1GkQhY4x1j/CSnybK9cGuMFQYFPLq0iNyopqf14aOVLFtMv7Esika+ObJWPWiOHuMOAHz3YkWoLYYRnzWQ== -estree-util-is-identifier-name@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz#0b5ef4c4ff13508b34dcd01ecfa945f61fce5dbd" - integrity sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg== - estree-util-visit@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/estree-util-visit/-/estree-util-visit-1.1.0.tgz#c0ea7942c40ac7889a77b57a11e92f987744bc6f" @@ -7039,39 +6944,11 @@ hast-util-to-estree@^2.0.0: unist-util-position "^4.0.0" zwitch "^2.0.0" -hast-util-to-jsx-runtime@^2.0.0: - version "2.3.6" - resolved "https://registry.yarnpkg.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz#ff31897aae59f62232e21594eac7ef6b63333e98" - integrity sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg== - dependencies: - "@types/estree" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/unist" "^3.0.0" - comma-separated-tokens "^2.0.0" - devlop "^1.0.0" - estree-util-is-identifier-name "^3.0.0" - hast-util-whitespace "^3.0.0" - mdast-util-mdx-expression "^2.0.0" - mdast-util-mdx-jsx "^3.0.0" - mdast-util-mdxjs-esm "^2.0.0" - property-information "^7.0.0" - space-separated-tokens "^2.0.0" - style-to-js "^1.0.0" - unist-util-position "^5.0.0" - vfile-message "^4.0.0" - hast-util-whitespace@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-2.0.0.tgz#4fc1086467cc1ef5ba20673cb6b03cec3a970f1c" integrity sha512-Pkw+xBHuV6xFeJprJe2BBEoDV+AvQySaz3pPDRUs5PNZEMQjpXJJueqrpcHIXxnWTcAGi/UOCgVShlkY6kLoqg== -hast-util-whitespace@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" - integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== - dependencies: - "@types/hast" "^3.0.0" - header-case@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063" @@ -7106,11 +6983,6 @@ hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react- dependencies: react-is "^16.7.0" -html-url-attributes@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/html-url-attributes/-/html-url-attributes-3.0.1.tgz#83b052cd5e437071b756cd74ae70f708870c2d87" - integrity sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ== - htmlparser2@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" @@ -7235,11 +7107,6 @@ inline-style-parser@0.1.1: resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== -inline-style-parser@0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.4.tgz#f4af5fe72e612839fcd453d989a586566d695f22" - integrity sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== - inline-style-prefixer@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-6.0.1.tgz#c5c0e43ba8831707afc5f5bbfd97edf45c1fa7ae" @@ -8328,15 +8195,15 @@ mdast-util-definitions@^5.0.0: "@types/unist" "^2.0.0" unist-util-visit "^3.0.0" -mdast-util-find-and-replace@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz#70a3174c894e14df722abf43bc250cbae44b11df" - integrity sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg== +mdast-util-find-and-replace@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.2.tgz#cc2b774f7f3630da4bd592f61966fecade8b99b1" + integrity sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw== dependencies: - "@types/mdast" "^4.0.0" + "@types/mdast" "^3.0.0" escape-string-regexp "^5.0.0" - unist-util-is "^6.0.0" - unist-util-visit-parents "^6.0.0" + unist-util-is "^5.0.0" + unist-util-visit-parents "^5.0.0" mdast-util-from-markdown@^1.0.0: version "1.2.0" @@ -8356,88 +8223,63 @@ mdast-util-from-markdown@^1.0.0: unist-util-stringify-position "^3.0.0" uvu "^0.5.0" -mdast-util-from-markdown@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz#4850390ca7cf17413a9b9a0fbefcd1bc0eb4160a" - integrity sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA== - dependencies: - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - mdast-util-to-string "^4.0.0" - micromark "^4.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-decode-string "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - unist-util-stringify-position "^4.0.0" - -mdast-util-gfm-autolink-literal@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz#abd557630337bd30a6d5a4bd8252e1c2dc0875d5" - integrity sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ== +mdast-util-gfm-autolink-literal@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-1.0.3.tgz#67a13abe813d7eba350453a5333ae1bc0ec05c06" + integrity sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA== dependencies: - "@types/mdast" "^4.0.0" + "@types/mdast" "^3.0.0" ccount "^2.0.0" - devlop "^1.0.0" - mdast-util-find-and-replace "^3.0.0" - micromark-util-character "^2.0.0" + mdast-util-find-and-replace "^2.0.0" + micromark-util-character "^1.0.0" -mdast-util-gfm-footnote@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz#7778e9d9ca3df7238cc2bd3fa2b1bf6a65b19403" - integrity sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ== +mdast-util-gfm-footnote@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-1.0.2.tgz#ce5e49b639c44de68d5bf5399877a14d5020424e" + integrity sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ== dependencies: - "@types/mdast" "^4.0.0" - devlop "^1.1.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" + "@types/mdast" "^3.0.0" + mdast-util-to-markdown "^1.3.0" + micromark-util-normalize-identifier "^1.0.0" -mdast-util-gfm-strikethrough@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz#d44ef9e8ed283ac8c1165ab0d0dfd058c2764c16" - integrity sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg== +mdast-util-gfm-strikethrough@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-1.0.3.tgz#5470eb105b483f7746b8805b9b989342085795b7" + integrity sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ== dependencies: - "@types/mdast" "^4.0.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" + "@types/mdast" "^3.0.0" + mdast-util-to-markdown "^1.3.0" -mdast-util-gfm-table@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz#7a435fb6223a72b0862b33afbd712b6dae878d38" - integrity sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg== +mdast-util-gfm-table@^1.0.0: + version "1.0.7" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.7.tgz#3552153a146379f0f9c4c1101b071d70bbed1a46" + integrity sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg== dependencies: - "@types/mdast" "^4.0.0" - devlop "^1.0.0" + "@types/mdast" "^3.0.0" markdown-table "^3.0.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" + mdast-util-from-markdown "^1.0.0" + mdast-util-to-markdown "^1.3.0" -mdast-util-gfm-task-list-item@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz#e68095d2f8a4303ef24094ab642e1047b991a936" - integrity sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ== +mdast-util-gfm-task-list-item@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-1.0.2.tgz#b280fcf3b7be6fd0cc012bbe67a59831eb34097b" + integrity sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ== dependencies: - "@types/mdast" "^4.0.0" - devlop "^1.0.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" + "@types/mdast" "^3.0.0" + mdast-util-to-markdown "^1.3.0" -mdast-util-gfm@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz#2cdf63b92c2a331406b0fb0db4c077c1b0331751" - integrity sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ== +mdast-util-gfm@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/mdast-util-gfm/-/mdast-util-gfm-2.0.2.tgz#e92f4d8717d74bdba6de57ed21cc8b9552e2d0b6" + integrity sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg== dependencies: - mdast-util-from-markdown "^2.0.0" - mdast-util-gfm-autolink-literal "^2.0.0" - mdast-util-gfm-footnote "^2.0.0" - mdast-util-gfm-strikethrough "^2.0.0" - mdast-util-gfm-table "^2.0.0" - mdast-util-gfm-task-list-item "^2.0.0" - mdast-util-to-markdown "^2.0.0" + mdast-util-from-markdown "^1.0.0" + mdast-util-gfm-autolink-literal "^1.0.0" + mdast-util-gfm-footnote "^1.0.0" + mdast-util-gfm-strikethrough "^1.0.0" + mdast-util-gfm-table "^1.0.0" + mdast-util-gfm-task-list-item "^1.0.0" + mdast-util-to-markdown "^1.0.0" mdast-util-mdx-expression@^1.0.0: version "1.2.1" @@ -8450,18 +8292,6 @@ mdast-util-mdx-expression@^1.0.0: mdast-util-from-markdown "^1.0.0" mdast-util-to-markdown "^1.0.0" -mdast-util-mdx-expression@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz#43f0abac9adc756e2086f63822a38c8d3c3a5096" - integrity sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - devlop "^1.0.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - mdast-util-mdx-jsx@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-2.0.2.tgz#087448dc29f6df9b9d9951132f82c20bd378bb68" @@ -8478,24 +8308,6 @@ mdast-util-mdx-jsx@^2.0.0: unist-util-stringify-position "^3.0.0" vfile-message "^3.0.0" -mdast-util-mdx-jsx@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz#fd04c67a2a7499efb905a8a5c578dddc9fdada0d" - integrity sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - ccount "^2.0.0" - devlop "^1.1.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - parse-entities "^4.0.0" - stringify-entities "^4.0.0" - unist-util-stringify-position "^4.0.0" - vfile-message "^4.0.0" - mdast-util-mdx@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mdast-util-mdx/-/mdast-util-mdx-2.0.0.tgz#dd4f6c993cf27da32725e50a04874f595b7b63fb" @@ -8516,26 +8328,6 @@ mdast-util-mdxjs-esm@^1.0.0: mdast-util-from-markdown "^1.0.0" mdast-util-to-markdown "^1.0.0" -mdast-util-mdxjs-esm@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz#019cfbe757ad62dd557db35a695e7314bcc9fa97" - integrity sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - devlop "^1.0.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - -mdast-util-phrasing@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz#7cc0a8dec30eaf04b7b1a9661a92adb3382aa6e3" - integrity sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== - dependencies: - "@types/mdast" "^4.0.0" - unist-util-is "^6.0.0" - mdast-util-to-hast@^12.1.0: version "12.1.1" resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-12.1.1.tgz#89a2bb405eaf3b05eb8bf45157678f35eef5dbca" @@ -8552,21 +8344,6 @@ mdast-util-to-hast@^12.1.0: unist-util-position "^4.0.0" unist-util-visit "^4.0.0" -mdast-util-to-hast@^13.0.0: - version "13.2.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz#5ca58e5b921cc0a3ded1bc02eed79a4fe4fe41f4" - integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA== - dependencies: - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - "@ungap/structured-clone" "^1.0.0" - devlop "^1.0.0" - micromark-util-sanitize-uri "^2.0.0" - trim-lines "^3.0.0" - unist-util-position "^5.0.0" - unist-util-visit "^5.0.0" - vfile "^6.0.0" - mdast-util-to-markdown@^1.0.0, mdast-util-to-markdown@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-1.3.0.tgz#38b6cdc8dc417de642a469c4fc2abdf8c931bd1e" @@ -8580,33 +8357,11 @@ mdast-util-to-markdown@^1.0.0, mdast-util-to-markdown@^1.3.0: unist-util-visit "^4.0.0" zwitch "^2.0.0" -mdast-util-to-markdown@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz#f910ffe60897f04bb4b7e7ee434486f76288361b" - integrity sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA== - dependencies: - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - longest-streak "^3.0.0" - mdast-util-phrasing "^4.0.0" - mdast-util-to-string "^4.0.0" - micromark-util-classify-character "^2.0.0" - micromark-util-decode-string "^2.0.0" - unist-util-visit "^5.0.0" - zwitch "^2.0.0" - mdast-util-to-string@^3.0.0, mdast-util-to-string@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz#56c506d065fbf769515235e577b5a261552d56e9" integrity sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA== -mdast-util-to-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" - integrity sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== - dependencies: - "@types/mdast" "^4.0.0" - mdn-data@2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" @@ -8671,106 +8426,84 @@ micromark-core-commonmark@^1.0.0, micromark-core-commonmark@^1.0.1: micromark-util-types "^1.0.1" uvu "^0.5.0" -micromark-core-commonmark@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz#c691630e485021a68cf28dbc2b2ca27ebf678cd4" - integrity sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg== +micromark-extension-gfm-autolink-literal@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-1.0.5.tgz#5853f0e579bbd8ef9e39a7c0f0f27c5a063a66e7" + integrity sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg== dependencies: - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - micromark-factory-destination "^2.0.0" - micromark-factory-label "^2.0.0" - micromark-factory-space "^2.0.0" - micromark-factory-title "^2.0.0" - micromark-factory-whitespace "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-classify-character "^2.0.0" - micromark-util-html-tag-name "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-resolve-all "^2.0.0" - micromark-util-subtokenize "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-extension-gfm-autolink-literal@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz#6286aee9686c4462c1e3552a9d505feddceeb935" - integrity sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw== + micromark-util-character "^1.0.0" + micromark-util-sanitize-uri "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-extension-gfm-footnote@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.1.2.tgz#05e13034d68f95ca53c99679040bc88a6f92fe2e" + integrity sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q== dependencies: - micromark-util-character "^2.0.0" - micromark-util-sanitize-uri "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" + micromark-core-commonmark "^1.0.0" + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-normalize-identifier "^1.0.0" + micromark-util-sanitize-uri "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + uvu "^0.5.0" -micromark-extension-gfm-footnote@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz#4dab56d4e398b9853f6fe4efac4fc9361f3e0750" - integrity sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw== - dependencies: - devlop "^1.0.0" - micromark-core-commonmark "^2.0.0" - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-sanitize-uri "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-extension-gfm-strikethrough@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz#86106df8b3a692b5f6a92280d3879be6be46d923" - integrity sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw== +micromark-extension-gfm-strikethrough@^1.0.0: + version "1.0.7" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-1.0.7.tgz#c8212c9a616fa3bf47cb5c711da77f4fdc2f80af" + integrity sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw== dependencies: - devlop "^1.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-classify-character "^2.0.0" - micromark-util-resolve-all "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" + micromark-util-chunked "^1.0.0" + micromark-util-classify-character "^1.0.0" + micromark-util-resolve-all "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + uvu "^0.5.0" -micromark-extension-gfm-table@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz#fac70bcbf51fe65f5f44033118d39be8a9b5940b" - integrity sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg== +micromark-extension-gfm-table@^1.0.0: + version "1.0.7" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.7.tgz#dcb46074b0c6254c3fc9cc1f6f5002c162968008" + integrity sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw== dependencies: - devlop "^1.0.0" - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + uvu "^0.5.0" -micromark-extension-gfm-tagfilter@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz#f26d8a7807b5985fba13cf61465b58ca5ff7dc57" - integrity sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg== +micromark-extension-gfm-tagfilter@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-1.0.2.tgz#aa7c4dd92dabbcb80f313ebaaa8eb3dac05f13a7" + integrity sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g== dependencies: - micromark-util-types "^2.0.0" + micromark-util-types "^1.0.0" -micromark-extension-gfm-task-list-item@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz#bcc34d805639829990ec175c3eea12bb5b781f2c" - integrity sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw== +micromark-extension-gfm-task-list-item@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.5.tgz#b52ce498dc4c69b6a9975abafc18f275b9dde9f4" + integrity sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ== dependencies: - devlop "^1.0.0" - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + uvu "^0.5.0" -micromark-extension-gfm@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz#3e13376ab95dd7a5cfd0e29560dfe999657b3c5b" - integrity sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w== - dependencies: - micromark-extension-gfm-autolink-literal "^2.0.0" - micromark-extension-gfm-footnote "^2.0.0" - micromark-extension-gfm-strikethrough "^2.0.0" - micromark-extension-gfm-table "^2.0.0" - micromark-extension-gfm-tagfilter "^2.0.0" - micromark-extension-gfm-task-list-item "^2.0.0" - micromark-util-combine-extensions "^2.0.0" - micromark-util-types "^2.0.0" +micromark-extension-gfm@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm/-/micromark-extension-gfm-2.0.3.tgz#e517e8579949a5024a493e49204e884aa74f5acf" + integrity sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ== + dependencies: + micromark-extension-gfm-autolink-literal "^1.0.0" + micromark-extension-gfm-footnote "^1.0.0" + micromark-extension-gfm-strikethrough "^1.0.0" + micromark-extension-gfm-table "^1.0.0" + micromark-extension-gfm-tagfilter "^1.0.0" + micromark-extension-gfm-task-list-item "^1.0.0" + micromark-util-combine-extensions "^1.0.0" + micromark-util-types "^1.0.0" micromark-extension-mdx-expression@^1.0.0: version "1.0.3" @@ -8844,15 +8577,6 @@ micromark-factory-destination@^1.0.0: micromark-util-symbol "^1.0.0" micromark-util-types "^1.0.0" -micromark-factory-destination@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz#8fef8e0f7081f0474fbdd92deb50c990a0264639" - integrity sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - micromark-factory-label@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-1.0.2.tgz#6be2551fa8d13542fcbbac478258fb7a20047137" @@ -8863,16 +8587,6 @@ micromark-factory-label@^1.0.0: micromark-util-types "^1.0.0" uvu "^0.5.0" -micromark-factory-label@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz#5267efa97f1e5254efc7f20b459a38cb21058ba1" - integrity sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg== - dependencies: - devlop "^1.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - micromark-factory-mdx-expression@^1.0.0: version "1.0.6" resolved "https://registry.yarnpkg.com/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-1.0.6.tgz#917e17d16e6e9c2551f3a862e6a9ebdd22056476" @@ -8895,14 +8609,6 @@ micromark-factory-space@^1.0.0: micromark-util-character "^1.0.0" micromark-util-types "^1.0.0" -micromark-factory-space@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz#36d0212e962b2b3121f8525fc7a3c7c029f334fc" - integrity sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-types "^2.0.0" - micromark-factory-title@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-1.0.2.tgz#7e09287c3748ff1693930f176e1c4a328382494f" @@ -8914,16 +8620,6 @@ micromark-factory-title@^1.0.0: micromark-util-types "^1.0.0" uvu "^0.5.0" -micromark-factory-title@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz#237e4aa5d58a95863f01032d9ee9b090f1de6e94" - integrity sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw== - dependencies: - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - micromark-factory-whitespace@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz#e991e043ad376c1ba52f4e49858ce0794678621c" @@ -8934,16 +8630,6 @@ micromark-factory-whitespace@^1.0.0: micromark-util-symbol "^1.0.0" micromark-util-types "^1.0.0" -micromark-factory-whitespace@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz#06b26b2983c4d27bfcc657b33e25134d4868b0b1" - integrity sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ== - dependencies: - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - micromark-util-character@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-1.1.0.tgz#d97c54d5742a0d9611a68ca0cd4124331f264d86" @@ -8952,14 +8638,6 @@ micromark-util-character@^1.0.0: micromark-util-symbol "^1.0.0" micromark-util-types "^1.0.0" -micromark-util-character@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz#2f987831a40d4c510ac261e89852c4e9703ccda6" - integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== - dependencies: - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - micromark-util-chunked@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz#5b40d83f3d53b84c4c6bce30ed4257e9a4c79d06" @@ -8967,13 +8645,6 @@ micromark-util-chunked@^1.0.0: dependencies: micromark-util-symbol "^1.0.0" -micromark-util-chunked@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz#47fbcd93471a3fccab86cff03847fc3552db1051" - integrity sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA== - dependencies: - micromark-util-symbol "^2.0.0" - micromark-util-classify-character@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz#cbd7b447cb79ee6997dd274a46fc4eb806460a20" @@ -8983,15 +8654,6 @@ micromark-util-classify-character@^1.0.0: micromark-util-symbol "^1.0.0" micromark-util-types "^1.0.0" -micromark-util-classify-character@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz#d399faf9c45ca14c8b4be98b1ea481bced87b629" - integrity sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - micromark-util-combine-extensions@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz#91418e1e74fb893e3628b8d496085639124ff3d5" @@ -9000,14 +8662,6 @@ micromark-util-combine-extensions@^1.0.0: micromark-util-chunked "^1.0.0" micromark-util-types "^1.0.0" -micromark-util-combine-extensions@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz#2a0f490ab08bff5cc2fd5eec6dd0ca04f89b30a9" - integrity sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg== - dependencies: - micromark-util-chunked "^2.0.0" - micromark-util-types "^2.0.0" - micromark-util-decode-numeric-character-reference@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz#dcc85f13b5bd93ff8d2868c3dba28039d490b946" @@ -9015,13 +8669,6 @@ micromark-util-decode-numeric-character-reference@^1.0.0: dependencies: micromark-util-symbol "^1.0.0" -micromark-util-decode-numeric-character-reference@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz#fcf15b660979388e6f118cdb6bf7d79d73d26fe5" - integrity sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw== - dependencies: - micromark-util-symbol "^2.0.0" - micromark-util-decode-string@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-1.0.2.tgz#942252ab7a76dec2dbf089cc32505ee2bc3acf02" @@ -9032,26 +8679,11 @@ micromark-util-decode-string@^1.0.0: micromark-util-decode-numeric-character-reference "^1.0.0" micromark-util-symbol "^1.0.0" -micromark-util-decode-string@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz#6cb99582e5d271e84efca8e61a807994d7161eb2" - integrity sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ== - dependencies: - decode-named-character-reference "^1.0.0" - micromark-util-character "^2.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-encode@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-1.0.1.tgz#2c1c22d3800870ad770ece5686ebca5920353383" integrity sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA== -micromark-util-encode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz#0d51d1c095551cfaac368326963cf55f15f540b8" - integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== - micromark-util-events-to-acorn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-1.1.0.tgz#9891638e201c266484d0af7cd2505d208f73db9d" @@ -9070,11 +8702,6 @@ micromark-util-html-tag-name@^1.0.0: resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.1.0.tgz#eb227118befd51f48858e879b7a419fc0df20497" integrity sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA== -micromark-util-html-tag-name@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz#e40403096481986b41c106627f98f72d4d10b825" - integrity sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA== - micromark-util-normalize-identifier@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz#4a3539cb8db954bbec5203952bfe8cedadae7828" @@ -9082,13 +8709,6 @@ micromark-util-normalize-identifier@^1.0.0: dependencies: micromark-util-symbol "^1.0.0" -micromark-util-normalize-identifier@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz#c30d77b2e832acf6526f8bf1aa47bc9c9438c16d" - integrity sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q== - dependencies: - micromark-util-symbol "^2.0.0" - micromark-util-resolve-all@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz#a7c363f49a0162e931960c44f3127ab58f031d88" @@ -9096,13 +8716,6 @@ micromark-util-resolve-all@^1.0.0: dependencies: micromark-util-types "^1.0.0" -micromark-util-resolve-all@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz#e1a2d62cdd237230a2ae11839027b19381e31e8b" - integrity sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg== - dependencies: - micromark-util-types "^2.0.0" - micromark-util-sanitize-uri@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.0.0.tgz#27dc875397cd15102274c6c6da5585d34d4f12b2" @@ -9112,15 +8725,6 @@ micromark-util-sanitize-uri@^1.0.0: micromark-util-encode "^1.0.0" micromark-util-symbol "^1.0.0" -micromark-util-sanitize-uri@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz#ab89789b818a58752b73d6b55238621b7faa8fd7" - integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-encode "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-subtokenize@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.2.tgz#ff6f1af6ac836f8bfdbf9b02f40431760ad89105" @@ -9131,36 +8735,16 @@ micromark-util-subtokenize@^1.0.0: micromark-util-types "^1.0.0" uvu "^0.5.0" -micromark-util-subtokenize@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz#d8ade5ba0f3197a1cf6a2999fbbfe6357a1a19ee" - integrity sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA== - dependencies: - devlop "^1.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - micromark-util-symbol@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz#b90344db62042ce454f351cf0bebcc0a6da4920e" integrity sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ== -micromark-util-symbol@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz#e5da494e8eb2b071a0d08fb34f6cefec6c0a19b8" - integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== - micromark-util-types@^1.0.0, micromark-util-types@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-1.0.2.tgz#f4220fdb319205812f99c40f8c87a9be83eded20" integrity sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w== -micromark-util-types@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz#f00225f5f5a0ebc3254f96c36b6605c4b393908e" - integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== - micromark@^3.0.0: version "3.0.10" resolved "https://registry.yarnpkg.com/micromark/-/micromark-3.0.10.tgz#1eac156f0399d42736458a14b0ca2d86190b457c" @@ -9184,29 +8768,6 @@ micromark@^3.0.0: micromark-util-types "^1.0.1" uvu "^0.5.0" -micromark@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.2.tgz#91395a3e1884a198e62116e33c9c568e39936fdb" - integrity sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA== - dependencies: - "@types/debug" "^4.0.0" - debug "^4.0.0" - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - micromark-core-commonmark "^2.0.0" - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-combine-extensions "^2.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-encode "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-resolve-all "^2.0.0" - micromark-util-sanitize-uri "^2.0.0" - micromark-util-subtokenize "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - micromatch@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" @@ -9620,16 +9181,6 @@ optimism@^0.16.1: "@wry/context" "^0.6.0" "@wry/trie" "^0.3.0" -optimism@^0.18.0: - version "0.18.1" - resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.18.1.tgz#5cf16847921413dbb0ac809907370388b9c6335f" - integrity sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ== - dependencies: - "@wry/caches" "^1.0.0" - "@wry/context" "^0.7.0" - "@wry/trie" "^0.5.0" - tslib "^2.3.0" - optionator@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" @@ -9982,7 +9533,7 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.0.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -10005,11 +9556,6 @@ property-information@^6.0.0: resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.1.1.tgz#5ca85510a3019726cb9afed4197b7b8ac5926a22" integrity sha512-hrzC564QIl0r0vy4l6MvRLhafmUowhO/O3KgVSoXIbbA2Sz4j8HGpJc6T2cubRVwMwpdiG/vKGfhT4IixmKN9w== -property-information@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/property-information/-/property-information-7.0.0.tgz#3508a6d6b0b8eb3ca6eb2c6623b164d2ed2ab112" - integrity sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg== - proxy-compare@2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/proxy-compare/-/proxy-compare-2.5.1.tgz#17818e33d1653fbac8c2ec31406bce8a2966f600" @@ -10202,27 +9748,36 @@ react-is@^16.10.2, react-is@^16.13.1, react-is@^16.7.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-is@^18.0.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.1.0.tgz#61aaed3096d30eacf2a2127118b5b41387d32a67" + integrity sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg== + react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-markdown@^9.0.3: - version "9.1.0" - resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-9.1.0.tgz#606bd74c6af131ba382a7c1282ff506708ed2e26" - integrity sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw== - dependencies: - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - devlop "^1.0.0" - hast-util-to-jsx-runtime "^2.0.0" - html-url-attributes "^3.0.0" - mdast-util-to-hast "^13.0.0" - remark-parse "^11.0.0" - remark-rehype "^11.0.0" - unified "^11.0.0" - unist-util-visit "^5.0.0" - vfile "^6.0.0" +react-markdown@^8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-8.0.3.tgz#e8aba0d2f5a1b2124d476ee1fff9448a2f57e4b3" + integrity sha512-We36SfqaKoVNpN1QqsZwWSv/OZt5J15LNgTLWynwAN5b265hrQrsjMtlRNwUvS+YyR3yDM8HpTNc4pK9H/Gc0A== + dependencies: + "@types/hast" "^2.0.0" + "@types/prop-types" "^15.0.0" + "@types/unist" "^2.0.0" + comma-separated-tokens "^2.0.0" + hast-util-whitespace "^2.0.0" + prop-types "^15.0.0" + property-information "^6.0.0" + react-is "^18.0.0" + remark-parse "^10.0.0" + remark-rehype "^10.0.0" + space-separated-tokens "^2.0.0" + style-to-object "^0.3.0" + unified "^10.0.0" + unist-util-visit "^4.0.0" + vfile "^5.0.0" react-masonry-css@^1.0.16: version "1.0.16" @@ -10523,11 +10078,6 @@ regexpp@^3.2.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -rehackt@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/rehackt/-/rehackt-0.1.0.tgz#a7c5e289c87345f70da8728a7eb878e5d03c696b" - integrity sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw== - relay-runtime@12.0.0: version "12.0.0" resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-12.0.0.tgz#1e039282bdb5e0c1b9a7dc7f6b9a09d4f4ff8237" @@ -10537,17 +10087,15 @@ relay-runtime@12.0.0: fbjs "^3.0.0" invariant "^2.2.4" -remark-gfm@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-4.0.1.tgz#33227b2a74397670d357bf05c098eaf8513f0d6b" - integrity sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg== +remark-gfm@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-3.0.1.tgz#0b180f095e3036545e9dddac0e8df3fa5cfee54f" + integrity sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig== dependencies: - "@types/mdast" "^4.0.0" - mdast-util-gfm "^3.0.0" - micromark-extension-gfm "^3.0.0" - remark-parse "^11.0.0" - remark-stringify "^11.0.0" - unified "^11.0.0" + "@types/mdast" "^3.0.0" + mdast-util-gfm "^2.0.0" + micromark-extension-gfm "^2.0.0" + unified "^10.0.0" remark-mdx@^2.0.0: version "2.1.2" @@ -10566,16 +10114,6 @@ remark-parse@^10.0.0: mdast-util-from-markdown "^1.0.0" unified "^10.0.0" -remark-parse@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" - integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== - dependencies: - "@types/mdast" "^4.0.0" - mdast-util-from-markdown "^2.0.0" - micromark-util-types "^2.0.0" - unified "^11.0.0" - remark-rehype@^10.0.0: version "10.1.0" resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-10.1.0.tgz#32dc99d2034c27ecaf2e0150d22a6dcccd9a6279" @@ -10586,26 +10124,6 @@ remark-rehype@^10.0.0: mdast-util-to-hast "^12.1.0" unified "^10.0.0" -remark-rehype@^11.0.0: - version "11.1.1" - resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.1.tgz#f864dd2947889a11997c0a2667cd6b38f685bca7" - integrity sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ== - dependencies: - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - mdast-util-to-hast "^13.0.0" - unified "^11.0.0" - vfile "^6.0.0" - -remark-stringify@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-11.0.0.tgz#4c5b01dd711c269df1aaae11743eb7e2e7636fd3" - integrity sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw== - dependencies: - "@types/mdast" "^4.0.0" - mdast-util-to-markdown "^2.0.0" - unified "^11.0.0" - remedial@^1.0.7: version "1.0.8" resolved "https://registry.yarnpkg.com/remedial/-/remedial-1.0.8.tgz#a5e4fd52a0e4956adbaf62da63a5a46a78c578a0" @@ -11235,20 +10753,6 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -style-to-js@^1.0.0: - version "1.1.16" - resolved "https://registry.yarnpkg.com/style-to-js/-/style-to-js-1.1.16.tgz#e6bd6cd29e250bcf8fa5e6591d07ced7575dbe7a" - integrity sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw== - dependencies: - style-to-object "1.0.8" - -style-to-object@1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.8.tgz#67a29bca47eaa587db18118d68f9d95955e81292" - integrity sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g== - dependencies: - inline-style-parser "0.2.4" - style-to-object@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" @@ -11411,11 +10915,6 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -trim-lines@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" - integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== - trough@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/trough/-/trough-2.1.0.tgz#0f7b511a4fde65a46f18477ab38849b22c554876" @@ -11671,19 +11170,6 @@ unified@^10.0.0: trough "^2.0.0" vfile "^5.0.0" -unified@^11.0.0: - version "11.0.5" - resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.5.tgz#f66677610a5c0a9ee90cab2b8d4d66037026d9e1" - integrity sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA== - dependencies: - "@types/unist" "^3.0.0" - bail "^2.0.0" - devlop "^1.0.0" - extend "^3.0.0" - is-plain-obj "^4.0.0" - trough "^2.0.0" - vfile "^6.0.0" - unist-builder@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-3.0.0.tgz#728baca4767c0e784e1e64bb44b5a5a753021a04" @@ -11701,13 +11187,6 @@ unist-util-is@^5.0.0: resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-5.1.1.tgz#e8aece0b102fa9bc097b0fef8f870c496d4a6236" integrity sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ== -unist-util-is@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424" - integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== - dependencies: - "@types/unist" "^3.0.0" - unist-util-position-from-estree@^1.0.0, unist-util-position-from-estree@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/unist-util-position-from-estree/-/unist-util-position-from-estree-1.1.1.tgz#96f4d543dfb0428edc01ebb928570b602d280c4c" @@ -11722,13 +11201,6 @@ unist-util-position@^4.0.0: dependencies: "@types/unist" "^2.0.0" -unist-util-position@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4" - integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== - dependencies: - "@types/unist" "^3.0.0" - unist-util-remove-position@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-4.0.1.tgz#d5b46a7304ac114c8d91990ece085ca7c2c135c8" @@ -11744,13 +11216,6 @@ unist-util-stringify-position@^3.0.0: dependencies: "@types/unist" "^2.0.0" -unist-util-stringify-position@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" - integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== - dependencies: - "@types/unist" "^3.0.0" - unist-util-visit-parents@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-4.1.1.tgz#e83559a4ad7e6048a46b1bdb22614f2f3f4724f2" @@ -11767,14 +11232,6 @@ unist-util-visit-parents@^5.0.0: "@types/unist" "^2.0.0" unist-util-is "^5.0.0" -unist-util-visit-parents@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815" - integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== - dependencies: - "@types/unist" "^3.0.0" - unist-util-is "^6.0.0" - unist-util-visit@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-3.1.0.tgz#9420d285e1aee938c7d9acbafc8e160186dbaf7b" @@ -11793,15 +11250,6 @@ unist-util-visit@^4.0.0: unist-util-is "^5.0.0" unist-util-visit-parents "^5.0.0" -unist-util-visit@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" - integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== - dependencies: - "@types/unist" "^3.0.0" - unist-util-is "^6.0.0" - unist-util-visit-parents "^6.0.0" - universal-cookie@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-4.0.4.tgz#06e8b3625bf9af049569ef97109b4bb226ad798d" @@ -12006,14 +11454,6 @@ vfile-message@^3.0.0: "@types/unist" "^2.0.0" unist-util-stringify-position "^3.0.0" -vfile-message@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181" - integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== - dependencies: - "@types/unist" "^3.0.0" - unist-util-stringify-position "^4.0.0" - vfile@^5.0.0: version "5.3.2" resolved "https://registry.yarnpkg.com/vfile/-/vfile-5.3.2.tgz#b499fbc50197ea50ad3749e9b60beb16ca5b7c54" @@ -12024,14 +11464,6 @@ vfile@^5.0.0: unist-util-stringify-position "^3.0.0" vfile-message "^3.0.0" -vfile@^6.0.0: - version "6.0.3" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.3.tgz#3652ab1c496531852bf55a6bac57af981ebc38ab" - integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== - dependencies: - "@types/unist" "^3.0.0" - vfile-message "^4.0.0" - viem@^1.0.0: version "1.2.6" resolved "https://registry.yarnpkg.com/viem/-/viem-1.2.6.tgz#aa205be5eea4153623ad8cd732fb0477efc55e0f" From afeb09ae91fb65a7a7cf63f0afb5e78282f6b52d Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Wed, 28 May 2025 21:34:52 +0100 Subject: [PATCH 34/37] lint error fix --- components/Votes/VoteTable/Views/DesktopVoteTable.tsx | 2 +- components/Votes/VoteTable/Views/MobileVoteTable.tsx | 2 +- pages/treasury/[proposal].tsx | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/Votes/VoteTable/Views/DesktopVoteTable.tsx b/components/Votes/VoteTable/Views/DesktopVoteTable.tsx index eb14fb4d..a73a7dd5 100644 --- a/components/Votes/VoteTable/Views/DesktopVoteTable.tsx +++ b/components/Votes/VoteTable/Views/DesktopVoteTable.tsx @@ -56,7 +56,7 @@ export const DesktopVoteTable: React.FC = ({ mb: "$2", }} > - Click on a vote to view a voter's proposal voting history. + Click on a vote to view a voters proposal voting history. diff --git a/components/Votes/VoteTable/Views/MobileVoteTable.tsx b/components/Votes/VoteTable/Views/MobileVoteTable.tsx index 51054814..2f722a8b 100644 --- a/components/Votes/VoteTable/Views/MobileVoteTable.tsx +++ b/components/Votes/VoteTable/Views/MobileVoteTable.tsx @@ -51,7 +51,7 @@ export const MobileVoteCards: React.FC = ({ mb: "$2", }} > - Click on a vote to view a voter's proposal voting history. + Click on a vote to view a voters proposal voting history. {votes.map((vote) => { diff --git a/pages/treasury/[proposal].tsx b/pages/treasury/[proposal].tsx index 8728a9a6..6a835007 100644 --- a/pages/treasury/[proposal].tsx +++ b/pages/treasury/[proposal].tsx @@ -85,6 +85,10 @@ const Proposal = () => { const { votes, loading: votesLoading } = useFetchVotes(proposalId ?? ""); const [votesOpen, setVotesOpen] = useState(false); + + useEffect(() => { + setIsDesktop(width >= 768); + }, [width]); const proposal = useMemo(() => { if (!proposalQuery || !state || !protocolQuery || !currentRound) { @@ -154,10 +158,6 @@ const Proposal = () => { return ; } - useEffect(() => { - setIsDesktop(width >= 768); - }, [width]); - if (!proposal) { return ( <> From e51794f4348da2d15131628bca7cf0f10a6d7373 Mon Sep 17 00:00:00 2001 From: rozzrr Date: Thu, 29 May 2025 08:35:14 +0100 Subject: [PATCH 35/37] fixed import and relative import errors --- components/Votes/VotingWidget/index.tsx | 4 ++-- pages/voting/[poll].tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/Votes/VotingWidget/index.tsx b/components/Votes/VotingWidget/index.tsx index 57a6508b..7d739cc1 100644 --- a/components/Votes/VotingWidget/index.tsx +++ b/components/Votes/VotingWidget/index.tsx @@ -17,8 +17,8 @@ import duration from "dayjs/plugin/duration"; import { useAccountAddress, usePendingFeesAndStakeData } from "hooks"; import { useEffect, useMemo, useState } from "react"; import { CopyToClipboard } from "react-copy-to-clipboard"; -import Check from "../../public/img/check.svg"; -import Copy from "../../public/img/copy.svg"; +import Check from "../../../public/img/check.svg"; +import Copy from "../../../public/img/copy.svg"; import { abbreviateNumber } from "@lib/utils"; import VoteButton from "../VoteButton"; import { formatPercent, getVotingPower, VotingResponse } from "utils/voting"; diff --git a/pages/voting/[poll].tsx b/pages/voting/[poll].tsx index e75873dd..f1289189 100644 --- a/pages/voting/[poll].tsx +++ b/pages/voting/[poll].tsx @@ -1,4 +1,4 @@ -import VotingWidget from "@components/VotingWidget"; +import VotingWidget from "@components/Votes/VotingWidget"; import { getLayout, LAYOUT_MAX_WIDTH } from "@layouts/main"; import { useRouter } from "next/router"; import MarkdownRenderer from "@components/MarkdownRenderer"; From fbcd076360aa5b6e05b5feefb73683221b2e6026 Mon Sep 17 00:00:00 2001 From: Liam Doyle <72405789+liamdoyle95@users.noreply.github.com> Date: Fri, 30 May 2025 15:07:07 +0100 Subject: [PATCH 36/37] VoteTx, Padding UI -VoteTx changed from Arbiscan Icon to formatted voteTx address -UI for table heading --- components/Votes/VoteDetail/index.tsx | 20 ++-- .../VoteTable/Views/DesktopVoteTable.tsx | 3 +- components/Votes/VoteTable/Views/VoteItem.tsx | 102 +++++++++++------- pages/treasury/[proposal].tsx | 9 +- 4 files changed, 81 insertions(+), 53 deletions(-) diff --git a/components/Votes/VoteDetail/index.tsx b/components/Votes/VoteDetail/index.tsx index 67dc3b56..f50cb8da 100644 --- a/components/Votes/VoteDetail/index.tsx +++ b/components/Votes/VoteDetail/index.tsx @@ -1,9 +1,8 @@ "use client"; import React from "react"; -import Image from "next/image"; -import { Box, Heading, Text, Link } from "@livepeer/design-system"; -import ArbitrumIcon from "../../../public/img/logos/arbitrum.png"; +import { Box, Heading, Text, Link, Badge } from "@livepeer/design-system"; +import { ArrowTopRightIcon } from "@modulz/radix-icons"; import { VOTING_SUPPORT, Vote } from "@lib/api/types/votes"; import { formatAddress } from "utils/formatAddress"; import { formatLpt } from "@lib/utils"; @@ -85,15 +84,16 @@ const Index: React.FC = ({ vote }) => { css={{ display: "inline-block", transition: "transform 0.2s ease", - "&:hover": { transform: "scale(1.3)" }, + "&:hover": { transform: "scale(1.1)" }, }} > - Arbitrum Icon + + {formatAddress(vote.transactionHash)} + + ) : ( N/A diff --git a/components/Votes/VoteTable/Views/DesktopVoteTable.tsx b/components/Votes/VoteTable/Views/DesktopVoteTable.tsx index a73a7dd5..d14b3cb9 100644 --- a/components/Votes/VoteTable/Views/DesktopVoteTable.tsx +++ b/components/Votes/VoteTable/Views/DesktopVoteTable.tsx @@ -22,7 +22,7 @@ export const DesktopVoteTable: React.FC = ({ = ({ fontSize: "$1", color: "$neutral11", borderBottom: "1px solid $neutral5", + padding: "$2 $3", }} > {label} diff --git a/components/Votes/VoteTable/Views/VoteItem.tsx b/components/Votes/VoteTable/Views/VoteItem.tsx index daeb90d1..f72da9a1 100644 --- a/components/Votes/VoteTable/Views/VoteItem.tsx +++ b/components/Votes/VoteTable/Views/VoteItem.tsx @@ -1,7 +1,9 @@ import ArbitrumIcon from "../../../../public/img/logos/arbitrum.png"; import { Vote, VOTING_SUPPORT } from "../../../../lib/api/types/votes"; -import { Card, Heading, Link, Text, Box } from "@livepeer/design-system"; +import { Card, Heading, Link, Text, Box, Badge } from "@livepeer/design-system"; import Image from "next/image"; +import { ArrowTopRightIcon } from "@modulz/radix-icons"; +import { formatAddress } from "utils/formatAddress"; interface VoteViewProps { @@ -83,24 +85,23 @@ interface VoteViewProps { {vote.transactionHash ? ( e.stopPropagation()} - > - Arbitrum Icon - + href={`https://arbiscan.io/tx/${vote.transactionHash}#eventlog`} + target="_blank" + onClickCapture={(e) => e.stopPropagation()} + css={{ + display: "inline-block", + transition: "transform 0.2s ease", + "&:hover": { transform: "scale(1.1)" }, + }} + > + + {formatAddress(vote.transactionHash)} + + + ) : ( N/A )} @@ -147,18 +148,31 @@ interface VoteViewProps { }} onClick={(e) => e.stopPropagation()} > - {vote.ensName} + + {vote.ensName} + - {support.text} + + {support.text} + - {formatWeight(vote.weight)} + + {formatWeight(vote.weight)} + {vote.transactionHash ? ( e.stopPropagation()} - css={{ - display: "inline-block", - transition: "transform 0.2s ease", - zIndex: 9999, - position: "relative", - "&:hover": { transform: "scale(1.3)", zIndex: 9999 }, - }} - > - Arbitrum Icon - + href={`https://arbiscan.io/tx/${vote.transactionHash}#eventlog`} + target="_blank" + onClickCapture={(e) => e.stopPropagation()} + css={{ + display: "inline-block", + transition: "transform 0.2s ease", + "&:hover": { transform: "scale(1.1)" }, + }} + > + + {formatAddress(vote.transactionHash)} + + + ) : ( N/A )} diff --git a/pages/treasury/[proposal].tsx b/pages/treasury/[proposal].tsx index 6a835007..65c8ef44 100644 --- a/pages/treasury/[proposal].tsx +++ b/pages/treasury/[proposal].tsx @@ -640,7 +640,14 @@ const Proposal = () => { }} > -{votesLoading ? "Loading votes…" : `View Votes (${votes.length})`} + + {votesLoading ? "Loading votes…" : `View Votes (${votes.length})`} + + Date: Sat, 31 May 2025 19:37:46 +0100 Subject: [PATCH 37/37] Table UI align left --- .../Votes/VoteTable/Views/DesktopVoteTable.tsx | 2 +- components/Votes/VoteTable/Views/VoteItem.tsx | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/components/Votes/VoteTable/Views/DesktopVoteTable.tsx b/components/Votes/VoteTable/Views/DesktopVoteTable.tsx index d14b3cb9..20d099e3 100644 --- a/components/Votes/VoteTable/Views/DesktopVoteTable.tsx +++ b/components/Votes/VoteTable/Views/DesktopVoteTable.tsx @@ -66,7 +66,7 @@ export const DesktopVoteTable: React.FC = ({ key={label} as="th" css={{ - textAlign: "center", + textAlign: "left", textTransform: "uppercase", fontSize: "$1", color: "$neutral11", diff --git a/components/Votes/VoteTable/Views/VoteItem.tsx b/components/Votes/VoteTable/Views/VoteItem.tsx index f72da9a1..3c2c2e62 100644 --- a/components/Votes/VoteTable/Views/VoteItem.tsx +++ b/components/Votes/VoteTable/Views/VoteItem.tsx @@ -1,11 +1,8 @@ -import ArbitrumIcon from "../../../../public/img/logos/arbitrum.png"; import { Vote, VOTING_SUPPORT } from "../../../../lib/api/types/votes"; import { Card, Heading, Link, Text, Box, Badge } from "@livepeer/design-system"; -import Image from "next/image"; import { ArrowTopRightIcon } from "@modulz/radix-icons"; import { formatAddress } from "utils/formatAddress"; - interface VoteViewProps { vote: Vote; onSelect: (voter: string) => void; @@ -62,7 +59,7 @@ interface VoteViewProps { {vote.ensName} - + Support: @@ -122,7 +119,7 @@ interface VoteViewProps { position: "relative", zIndex: 2, "&:hover": { backgroundColor: "$neutral4" }, - "& > td": { padding: "$2" }, + "& > td": { padding: "$2 $3" }, }} onClickCapture={(e) => { if ((e.target as HTMLElement).closest("a")) return; @@ -133,7 +130,7 @@ interface VoteViewProps { @@ -177,7 +174,7 @@ interface VoteViewProps {