From 2a503d6e4ab56bf2ca4b9ddf1e7df7ade161b978 Mon Sep 17 00:00:00 2001 From: daniyal Date: Wed, 24 Jul 2024 15:13:28 +0500 Subject: [PATCH] feat: completed basic implementation for mod submission --- .env.example | 1 + .gitignore | 2 + package-lock.json | 458 ++++-------------------------------- package.json | 3 - src/components/Inputs.tsx | 25 +- src/components/ModForm.tsx | 366 ++++++++++++++++++++-------- src/controllers/index.ts | 1 + src/controllers/metadata.ts | 12 +- src/controllers/relay.ts | 121 ++++++++++ src/styles/styles.css | 37 +++ src/utils/index.ts | 2 + src/utils/url.ts | 22 ++ src/utils/utils.ts | 36 +++ src/vite-env.d.ts | 9 + 14 files changed, 570 insertions(+), 525 deletions(-) create mode 100644 .env.example create mode 100644 src/controllers/relay.ts create mode 100644 src/utils/url.ts create mode 100644 src/utils/utils.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e9f46fa --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +VITE_APP_RELAY=wss://relay.degmods.com \ No newline at end of file diff --git a/.gitignore b/.gitignore index a547bf3..3b0b403 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ dist-ssr *.njsproj *.sln *.sw? + +.env \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ece2fa4..0a80398 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,9 +19,7 @@ "react-quill": "2.0.0", "react-redux": "9.1.2", "react-router-dom": "^6.24.1", - "react-select": "5.8.0", "react-toastify": "10.0.5", - "react-virtualized-auto-sizer": "1.0.24", "react-window": "1.8.10", "uuid": "10.0.0" }, @@ -30,7 +28,6 @@ "@types/papaparse": "5.3.14", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", - "@types/react-select": "5.0.1", "@types/react-window": "1.8.8", "@types/uuid": "10.0.0", "@typescript-eslint/eslint-plugin": "^7.13.1", @@ -61,6 +58,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "dev": true, "dependencies": { "@babel/highlight": "^7.24.7", "picocolors": "^1.0.0" @@ -121,6 +119,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "dev": true, "dependencies": { "@babel/types": "^7.24.7", "@jridgewell/gen-mapping": "^0.3.5", @@ -160,6 +159,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", + "dev": true, "dependencies": { "@babel/types": "^7.24.7" }, @@ -171,6 +171,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", + "dev": true, "dependencies": { "@babel/template": "^7.24.7", "@babel/types": "^7.24.7" @@ -183,6 +184,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", + "dev": true, "dependencies": { "@babel/types": "^7.24.7" }, @@ -194,6 +196,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "dev": true, "dependencies": { "@babel/traverse": "^7.24.7", "@babel/types": "^7.24.7" @@ -247,6 +250,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "dev": true, "dependencies": { "@babel/types": "^7.24.7" }, @@ -258,6 +262,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -266,6 +271,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -296,6 +302,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", @@ -310,6 +317,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -362,6 +370,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.24.7", "@babel/parser": "^7.24.7", @@ -375,6 +384,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.24.7", "@babel/generator": "^7.24.7", @@ -395,6 +405,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.24.7", "@babel/helper-validator-identifier": "^7.24.7", @@ -426,125 +437,6 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@emotion/babel-plugin": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", - "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", - "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/serialize": "^1.1.2", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, - "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@emotion/cache": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", - "dependencies": { - "@emotion/memoize": "^0.8.1", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/hash": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", - "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" - }, - "node_modules/@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" - }, - "node_modules/@emotion/react": { - "version": "11.11.4", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", - "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", - "hoist-non-react-statics": "^3.3.1" - }, - "peerDependencies": { - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/serialize": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", - "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", - "dependencies": { - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/unitless": "^0.8.1", - "@emotion/utils": "^1.2.1", - "csstype": "^3.0.2" - } - }, - "node_modules/@emotion/sheet": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" - }, - "node_modules/@emotion/unitless": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" - }, - "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", - "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", - "peerDependencies": { - "react": ">=16.8.0" - } - }, - "node_modules/@emotion/utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" - }, - "node_modules/@emotion/weak-memoize": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", - "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -1006,28 +898,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@floating-ui/core": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.4.tgz", - "integrity": "sha512-a4IowK4QkXl4SCWTGUR0INAfEOX3wtsYw3rKK5InQEHMGObkR8Xk44qYQD9P4r6HHw0iIfK6GUKECmY8sTkqRA==", - "dependencies": { - "@floating-ui/utils": "^0.2.4" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.7.tgz", - "integrity": "sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==", - "dependencies": { - "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.4" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.4.tgz", - "integrity": "sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==" - }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -1089,6 +959,7 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -1102,6 +973,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -1110,6 +982,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -1117,12 +990,14 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1693,15 +1568,11 @@ "@types/node": "*" } }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" - }, "node_modules/@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "devOptional": true }, "node_modules/@types/quill": { "version": "1.3.10", @@ -1715,6 +1586,7 @@ "version": "18.3.3", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", + "devOptional": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -1729,24 +1601,6 @@ "@types/react": "*" } }, - "node_modules/@types/react-select": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/react-select/-/react-select-5.0.1.tgz", - "integrity": "sha512-h5Im0AP0dr4AVeHtrcvQrLV+gmPa7SA0AGdxl2jOhtwiE6KgXBFSogWw8az32/nusE6AQHlCOHQWjP1S/+oMWA==", - "deprecated": "This is a stub types definition. react-select provides its own type definitions, so you do not need this installed.", - "dev": true, - "dependencies": { - "react-select": "*" - } - }, - "node_modules/@types/react-transition-group": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", - "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/react-window": { "version": "1.8.8", "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz", @@ -2039,6 +1893,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -2080,20 +1935,6 @@ "node": ">=8" } }, - "node_modules/babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2199,6 +2040,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "engines": { "node": ">=6" } @@ -2236,6 +2078,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -2301,6 +2144,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -2308,7 +2152,8 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/concat-map": { "version": "0.0.1", @@ -2322,21 +2167,6 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, - "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -2360,7 +2190,8 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "devOptional": true }, "node_modules/d": { "version": "1.0.2", @@ -2488,29 +2319,12 @@ "node": ">=6.0.0" } }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, "node_modules/electron-to-chromium": { "version": "1.4.823", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.823.tgz", "integrity": "sha512-4h+oPeAiGQOHFyUJOqpoEcPj/xxlicxBzOErVeYVMMmAiXUXsGpsFd0QXBMaUUbnD8hhSfLf9uw+MlsoIA7j5w==", "dev": true }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -2618,6 +2432,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -3051,11 +2866,6 @@ "node": ">=8" } }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -3225,6 +3035,7 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, "engines": { "node": ">=4" } @@ -3270,6 +3081,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -3332,14 +3144,6 @@ "node": ">= 0.4" } }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dependencies": { - "react-is": "^16.7.0" - } - }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -3368,6 +3172,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -3420,11 +3225,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -3437,20 +3237,6 @@ "node": ">=8" } }, - "node_modules/is-core-module": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", - "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-date-object": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", @@ -3551,6 +3337,7 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, "bin": { "jsesc": "bin/jsesc" }, @@ -3564,11 +3351,6 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3634,11 +3416,6 @@ } ] }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3973,14 +3750,6 @@ "integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==", "optional": true }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-is": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", @@ -4074,6 +3843,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -4081,23 +3851,6 @@ "node": ">=6" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4125,15 +3878,11 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, "engines": { "node": ">=8" } @@ -4141,7 +3890,8 @@ "node_modules/picocolors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true }, "node_modules/picomatch": { "version": "2.3.1", @@ -4211,16 +3961,6 @@ "node": ">= 0.8.0" } }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -4299,11 +4039,6 @@ "react": "^18.3.1" } }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/react-quill": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0.tgz", @@ -4379,31 +4114,6 @@ "react-dom": ">=16.8" } }, - "node_modules/react-select": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.8.0.tgz", - "integrity": "sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==", - "dependencies": { - "@babel/runtime": "^7.12.0", - "@emotion/cache": "^11.4.0", - "@emotion/react": "^11.8.1", - "@floating-ui/dom": "^1.0.1", - "@types/react-transition-group": "^4.4.0", - "memoize-one": "^6.0.0", - "prop-types": "^15.6.0", - "react-transition-group": "^4.3.0", - "use-isomorphic-layout-effect": "^1.1.2" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-select/node_modules/memoize-one": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" - }, "node_modules/react-toastify": { "version": "10.0.5", "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.5.tgz", @@ -4416,30 +4126,6 @@ "react-dom": ">=18" } }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/react-virtualized-auto-sizer": { - "version": "1.0.24", - "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.24.tgz", - "integrity": "sha512-3kCn7N9NEb3FlvJrSHWGQ4iVl+ydQObq2fHMn12i5wbtm74zHOPhz/i64OL3c1S1vi9i2GXtZqNqUJTQ+BnNfg==", - "peerDependencies": { - "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0", - "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0" - } - }, "node_modules/react-window": { "version": "1.8.10", "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz", @@ -4508,26 +4194,11 @@ "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, "engines": { "node": ">=4" } @@ -4713,14 +4384,6 @@ "node": ">=8" } }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", @@ -4754,15 +4417,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" - }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -4770,17 +4429,6 @@ "node": ">=4" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -4791,6 +4439,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, "engines": { "node": ">=4" } @@ -4983,19 +4632,6 @@ "punycode": "^2.1.0" } }, - "node_modules/use-isomorphic-layout-effect": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", - "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/use-sync-external-store": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", @@ -5187,14 +4823,6 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" - } - }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 15f9aec..54f4395 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,7 @@ "react-quill": "2.0.0", "react-redux": "9.1.2", "react-router-dom": "^6.24.1", - "react-select": "5.8.0", "react-toastify": "10.0.5", - "react-virtualized-auto-sizer": "1.0.24", "react-window": "1.8.10", "uuid": "10.0.0" }, @@ -32,7 +30,6 @@ "@types/papaparse": "5.3.14", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", - "@types/react-select": "5.0.1", "@types/react-window": "1.8.8", "@types/uuid": "10.0.0", "@typescript-eslint/eslint-plugin": "^7.13.1", diff --git a/src/components/Inputs.tsx b/src/components/Inputs.tsx index cd8ecd0..55fa741 100644 --- a/src/components/Inputs.tsx +++ b/src/components/Inputs.tsx @@ -1,7 +1,8 @@ -import '../styles/styles.css' -import ReactQuill from 'react-quill' -import '../styles/customQuillStyles.css' import React from 'react' +import ReactQuill from 'react-quill' +import 'react-quill/dist/quill.snow.css' +import '../styles/customQuillStyles.css' +import '../styles/styles.css' const editorFormats = [ 'header', @@ -41,6 +42,7 @@ interface InputFieldProps { name: string inputMode?: 'url' value: string + error?: string onChange: (name: string, value: string) => void } @@ -53,6 +55,7 @@ export const InputField = React.memo( name, inputMode, value, + error, onChange }: InputFieldProps) => { const handleChange = ( @@ -93,11 +96,27 @@ export const InputField = React.memo( onChange={handleChange} /> )} + {error && } ) } ) +type InputErrorProps = { + message: string +} + +export const InputError = ({ message }: InputErrorProps) => { + if (!message) return null + + return ( +
+
+

{message}

+
+ ) +} + interface CheckboxFieldProps { label: string name: string diff --git a/src/components/ModForm.tsx b/src/components/ModForm.tsx index 9d15f22..bb9d81f 100644 --- a/src/components/ModForm.tsx +++ b/src/components/ModForm.tsx @@ -1,16 +1,22 @@ import _ from 'lodash' +import { Event, kinds, UnsignedEvent } from 'nostr-tools' import Papa from 'papaparse' import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import 'react-quill/dist/quill.snow.css' -import { FixedSizeList as List } from 'react-window' - -import { kinds, UnsignedEvent } from 'nostr-tools' import { toast } from 'react-toastify' +import { FixedSizeList as List } from 'react-window' import { v4 as uuidv4 } from 'uuid' import { useAppSelector } from '../hooks' import '../styles/styles.css' -import { now } from '../utils' -import { CheckboxField, InputField } from './Inputs' +import { + isReachable, + isValidImageUrl, + isValidUrl, + log, + LogType, + now +} from '../utils' +import { CheckboxField, InputError, InputField } from './Inputs' +import { RelayController } from '../controllers' interface DownloadUrl { url: string @@ -33,6 +39,18 @@ interface FormState { downloadUrls: DownloadUrl[] } +interface FormErrors { + game?: string + title?: string + body?: string + featuredImageUrl?: string + summary?: string + nsfw?: string + screenshotsUrls?: string[] + tags?: string + downloadUrls?: string[] +} + interface GameOption { value: string label: string @@ -43,8 +61,8 @@ let processedCSV = false export const ModForm = () => { const userState = useAppSelector((state) => state.user) + const [isPublishing, setIsPublishing] = useState(false) const [gameOptions, setGameOptions] = useState([]) - const [formState, setFormState] = useState({ game: '', title: '', @@ -65,6 +83,7 @@ export const ModForm = () => { } ] }) + const [formErrors, setFormErrors] = useState({}) useEffect(() => { if (processedCSV) return @@ -181,6 +200,8 @@ export const ModForm = () => { ) const handlePublish = async () => { + setIsPublishing(true) + let hexPubkey: string if (userState.isAuth && userState.user?.pubkey) { @@ -194,25 +215,30 @@ export const ModForm = () => { return } - if (!validateState()) return + if (!(await validateState())) { + setIsPublishing(false) + return + } const uuid = uuidv4() + const currentTimeStamp = now() const unsignedEvent: UnsignedEvent = { kind: kinds.ClassifiedListing, - created_at: now(), + created_at: currentTimeStamp, pubkey: hexPubkey, content: formState.body, tags: [ ['d', uuid], - // todo: t tag for degmod identifier (use url) + ['t', window.location.host], + ['published_at', currentTimeStamp.toString()], ['game', formState.game], ['title', formState.title], ['featuredImageUrl', formState.featuredImageUrl], ['summary', formState.summary], ['nsfw', formState.nsfw.toString()], ['screenshotsUrls', ...formState.screenshotsUrls], - ['tags', formState.tags], + ['tags', ...formState.tags.split(',')], [ 'downloadUrls', ...formState.downloadUrls.map((downloadUrl) => @@ -222,60 +248,117 @@ export const ModForm = () => { ] } - const signedEvent = await window.nostr?.signEvent(unsignedEvent) - console.log('signedEvent :>> ', signedEvent) + const signedEvent = await window.nostr + ?.signEvent(unsignedEvent) + .catch((err) => { + toast.error('Failed to sign the event!') + log(true, LogType.Error, 'Failed to sign the event!', err) + return null + }) - // todo: publish event + if (!signedEvent) { + setIsPublishing(false) + return + } + + const publishedOnRelays = await RelayController.getInstance().publish( + signedEvent as Event + ) + + console.log('publishedOnRelays :>> ', publishedOnRelays) + + if (!publishedOnRelays) { + toast.error('Failed to publish event!') + setIsPublishing(false) + return + } + + // Handle cases where publishing failed or succeeded + if (publishedOnRelays.length === 0) { + toast.error('Failed to publish event on any relay') + } else { + toast.success( + `Event published successfully to the following relays\n\n${publishedOnRelays.join( + '\n' + )}` + ) + } + + setIsPublishing(false) } - const validateState = () => { + const validateState = async (): Promise => { + const errors: FormErrors = {} + if (formState.game === '') { - toast.error('Game field can not be empty') - return false + errors.game = 'Game field can not be empty' } if (formState.title === '') { - toast.error('Title field can not be empty') - return false + errors.title = 'Title field can not be empty' } if (formState.body === '') { - toast.error('Body field can not be empty') - return false + errors.body = 'Body field can not be empty' } if (formState.featuredImageUrl === '') { - toast.error('FeaturedImageUrl field can not be empty') - return false + errors.featuredImageUrl = 'FeaturedImageUrl field can not be empty' + } else if ( + !isValidImageUrl(formState.featuredImageUrl) || + !(await isReachable(formState.featuredImageUrl)) + ) { + errors.featuredImageUrl = + 'FeaturedImageUrl must be a valid and reachable image URL' } if (formState.summary === '') { - toast.error('Summary field can not be empty') - return false + errors.summary = 'Summary field can not be empty' } - if ( - formState.screenshotsUrls.length === 0 || - formState.screenshotsUrls.every((url) => url === '') - ) { - toast.error('Required at least one screenshot url') - return false + if (formState.screenshotsUrls.length === 0) { + errors.screenshotsUrls = ['Required at least one screenshot url'] + } else { + for (let i = 0; i < formState.screenshotsUrls.length; i++) { + const url = formState.screenshotsUrls[i] + if ( + !isValidUrl(url) || + !isValidImageUrl(url) || + !(await isReachable(url)) + ) { + if (!errors.screenshotsUrls) + errors.screenshotsUrls = Array(formState.screenshotsUrls.length) + + errors.screenshotsUrls![i] = + 'All screenshot URLs must be valid and reachable image URLs' + } + } } if (formState.tags === '') { - toast.error('tags field can not be empty') - return false + errors.tags = 'Tags field can not be empty' } - if ( - formState.downloadUrls.length === 0 || - formState.downloadUrls.every((download) => download.url === '') - ) { - toast.error('Required at least one download url') - return false + if (formState.downloadUrls.length === 0) { + errors.downloadUrls = ['Required at least one download url'] + } else { + for (let i = 0; i < formState.downloadUrls.length; i++) { + const downloadUrl = formState.downloadUrls[i] + if ( + !isValidUrl(downloadUrl.url) || + !(await isReachable(downloadUrl.url)) + ) { + if (!errors.downloadUrls) + errors.downloadUrls = Array(formState.downloadUrls.length) + + errors.downloadUrls![i] = 'Download url must be valid and reachable' + } + } } - return true + setFormErrors(errors) + + return Object.keys(errors).length === 0 } return ( @@ -283,6 +366,7 @@ export const ModForm = () => { @@ -291,6 +375,7 @@ export const ModForm = () => { placeholder='Return the banana mod' name='title' value={formState.title} + error={formErrors.title} onChange={handleInputChange} /> @@ -300,6 +385,7 @@ export const ModForm = () => { placeholder="Here's what this mod is all about" name='body' value={formState.body} + error={formErrors.body} onChange={handleInputChange} /> @@ -311,6 +397,7 @@ export const ModForm = () => { placeholder='Image URL' name='featuredImageUrl' value={formState.featuredImageUrl} + error={formErrors.featuredImageUrl} onChange={handleInputChange} /> { placeholder='This is a quick description of my mod' name='summary' value={formState.summary} + error={formErrors.summary} onChange={handleInputChange} /> { We recommend to upload images to https://nostr.build/

{formState.screenshotsUrls.map((url, index) => ( - + <> + + {formErrors.screenshotsUrls && + formErrors.screenshotsUrls[index] && ( + + )} + ))} + {formState.screenshotsUrls.length === 0 && + formErrors.screenshotsUrls && + formErrors.screenshotsUrls[0] && ( + + )} { placeholder='Tags' name='tags' value={formState.tags} + error={formErrors.tags} onChange={handleInputChange} />
@@ -393,17 +493,38 @@ export const ModForm = () => {

{formState.downloadUrls.map((download, index) => ( - + <> + + {formErrors.downloadUrls && formErrors.downloadUrls[index] && ( + + )} + ))} + + {formState.downloadUrls.length === 0 && + formErrors.downloadUrls && + formErrors.downloadUrls[0] && ( + + )}
-
@@ -412,13 +533,28 @@ export const ModForm = () => { } type DownloadUrlFieldsProps = { index: number - download: DownloadUrl + url: string + hash: string + signatureKey: string + malwareScanLink: string + modVersion: string + customNote: string onUrlChange: (index: number, field: keyof DownloadUrl, value: string) => void onRemove: (index: number) => void } const DownloadUrlFields = React.memo( - ({ index, download, onUrlChange, onRemove }: DownloadUrlFieldsProps) => { + ({ + index, + url, + hash, + signatureKey, + malwareScanLink, + modVersion, + customNote, + onUrlChange, + onRemove + }: DownloadUrlFieldsProps) => { const handleChange = (e: React.ChangeEvent) => { const { name, value } = e.target onUrlChange(index, name as keyof DownloadUrl, value) @@ -432,7 +568,7 @@ const DownloadUrlFields = React.memo( className='inputMain' name='url' placeholder='Download URL' - value={download.url} + value={url} onChange={handleChange} /> @@ -469,7 +605,7 @@ const DownloadUrlFields = React.memo( className='inputMain' name='hash' placeholder='SHA-256 Hash' - value={download.hash} + value={hash} onChange={handleChange} />
@@ -491,7 +627,7 @@ const DownloadUrlFields = React.memo( className='inputMain' placeholder='Signature public key' name='signatureKey' - value={download.signatureKey} + value={signatureKey} onChange={handleChange} />
@@ -513,7 +649,7 @@ const DownloadUrlFields = React.memo( className='inputMain' name='malwareScanLink' placeholder='Malware Scan Link' - value={download.malwareScanLink} + value={malwareScanLink} onChange={handleChange} />
@@ -535,7 +671,7 @@ const DownloadUrlFields = React.memo( className='inputMain' placeholder='Mod version (1.0)' name='modVersion' - value={download.modVersion} + value={modVersion} onChange={handleChange} />
@@ -557,7 +693,7 @@ const DownloadUrlFields = React.memo( className='inputMain' placeholder='Custom note/message' name='customNote' - value={download.customNote} + value={customNote} onChange={handleChange} />
@@ -613,10 +749,16 @@ const ScreenshotUrlFields = React.memo( type GameDropdownProps = { options: GameOption[] selected: string + error?: string onChange: (name: string, value: string) => void } -const GameDropdown = ({ options, selected, onChange }: GameDropdownProps) => { +const GameDropdown = ({ + options, + selected, + error, + onChange +}: GameDropdownProps) => { const [searchTerm, setSearchTerm] = useState('') const [debouncedSearchTerm, setDebouncedSearchTerm] = useState('') const inputRef = useRef(null) @@ -646,48 +788,66 @@ const GameDropdown = ({ options, selected, onChange }: GameDropdownProps) => { can add it.

- handleSearchChange(e.target.value)} - onBlur={() => { - if (!isOptionClicked.current) { - setSearchTerm('') - } - isOptionClicked.current = false - }} - /> -
- + handleSearchChange(e.target.value)} + onBlur={() => { + if (!isOptionClicked.current) { + setSearchTerm('') + } + isOptionClicked.current = false + }} + /> + +
+ + {({ index, style }) => ( +
(isOptionClicked.current = true)} + onClick={() => { + onChange('game', filteredOptions[index].value) + setSearchTerm('') + if (inputRef.current) { + inputRef.current.blur() + } + }} + > + {filteredOptions[index].label} +
+ )} +
+
+ {error && } ) } diff --git a/src/controllers/index.ts b/src/controllers/index.ts index 1402164..a8bfab8 100644 --- a/src/controllers/index.ts +++ b/src/controllers/index.ts @@ -1 +1,2 @@ export * from './metadata' +export * from './relay' diff --git a/src/controllers/metadata.ts b/src/controllers/metadata.ts index 5fb18ed..c1cac3a 100644 --- a/src/controllers/metadata.ts +++ b/src/controllers/metadata.ts @@ -1,4 +1,4 @@ -import NDK, { NDKUser } from '@nostr-dev-kit/ndk' +import NDK, { NDKRelayList, NDKUser } from '@nostr-dev-kit/ndk' import { UserProfile } from '../types/user' import { hexToNpub } from '../utils' @@ -41,4 +41,14 @@ export class MetadataController { return await user.fetchProfile() } + + public findWriteRelays = async (hexKey: string) => { + const ndkRelayList = await NDKRelayList.forUser(hexKey, this.profileNdk) + + if (!ndkRelayList) { + throw new Error(`Couldn't found user's relay list`) + } + + return ndkRelayList.writeRelayUrls + } } diff --git a/src/controllers/relay.ts b/src/controllers/relay.ts new file mode 100644 index 0000000..7eb668e --- /dev/null +++ b/src/controllers/relay.ts @@ -0,0 +1,121 @@ +import { Relay, Event } from 'nostr-tools' +import { log, LogType, timeout } from '../utils' +import { MetadataController } from './metadata' +import _ from 'lodash' + +/** + * Singleton class to manage relay operations. + */ +export class RelayController { + private static instance: RelayController + private debug = true + public connectedRelays: Relay[] = [] + + private constructor() {} + + private connectRelay = async (relayUrl: string) => { + const relay = this.connectedRelays.find( + (relay) => _.trimEnd(relay.url, '/') === _.trimEnd(relayUrl, '/') + ) + if (relay) { + // already connected, skip + return relay + } + + return await Relay.connect(relayUrl) + .then((relay) => { + log(this.debug, LogType.Info, `✅ nostr (${relayUrl}): Connected!`) + this.connectedRelays.push(relay) + return relay + }) + .catch((err) => { + log( + this.debug, + LogType.Error, + `❌ nostr (${relayUrl}): Connection error!`, + err + ) + return null + }) + } + + /** + * Provides the singleton instance of RelayController. + * + * @returns The singleton instance of RelayController. + */ + public static getInstance(): RelayController { + if (!RelayController.instance) { + RelayController.instance = new RelayController() + } + return RelayController.instance + } + + publish = async (event: Event) => { + const appRelayPromise = this.connectRelay(import.meta.env.VITE_APP_RELAY) + + // todo: window.nostr.getRelays() is not implemented yet in nostr-login, implement the logic once its done + + const writeRelaysPromise = MetadataController.getInstance().findWriteRelays( + event.pubkey + ) + + log(this.debug, LogType.Info, `Finding user's write relays`) + const writeRelayUrls = await Promise.race([ + writeRelaysPromise, + timeout() + ]).catch((err) => { + log(this.debug, LogType.Error, err) + return [] as string[] + }) + + const relayPromises = writeRelayUrls.map((relayUrl) => + this.connectRelay(relayUrl) + ) + + await Promise.allSettled([appRelayPromise, ...relayPromises]) + + if (this.connectedRelays.length === 0) { + log(this.debug, LogType.Error, 'No relay is connected!') + + return null + } + + const publishedOnRelays: string[] = [] + + const publishPromises = this.connectedRelays.map((relay) => { + log( + this.debug, + LogType.Info, + `⬆️ nostr (${relay.url}): Sending event:`, + event + ) + + return Promise.race([relay.publish(event), timeout(30000)]) + .then((res) => { + log( + this.debug, + LogType.Info, + `⬆️ nostr (${relay.url}): Publish result:`, + res + ) + + publishedOnRelays.push(relay.url) + }) + .catch((err) => { + log( + this.debug, + LogType.Error, + `❌ nostr (${relay.url}): Publish error!`, + err + ) + }) + }) + + await Promise.allSettled(publishPromises) + + console.log('publishedOnRelays :>> ', publishedOnRelays) + + return publishedOnRelays + } +} diff --git a/src/styles/styles.css b/src/styles/styles.css index 1d911d9..6a49386 100644 --- a/src/styles/styles.css +++ b/src/styles/styles.css @@ -291,6 +291,7 @@ h6 { display: flex; flex-direction: row; grid-gap: 10px; + position: relative; } .inputWrapperMainWrapper { @@ -513,3 +514,39 @@ a:hover { flex-direction: row; grid-gap: 10px; } + +.btnMain.btnMainInsideField:hover { + background: unset; +} + +.btnMain.btnMainInsideField { + position: absolute; + right: 0; + top: 0; + bottom: 0; + border-radius: unset; +} + +.inputMain.inputMainWithBtn { + padding-right: 50px; +} + +.errorMain { + width: 100%; + border-radius: 10px; + padding: 10px; + background: rgba(255, 255, 255, 0.05); + color: rgba(255, 255, 255, 0.5); + display: flex; + flex-direction: row; + grid-gap: 10px; +} + +.errorMainColor { + width: 5px; + border-radius: 2px; + background: tomato; +} + +.errorMainText { +} diff --git a/src/utils/index.ts b/src/utils/index.ts index e69da0f..1dd5f84 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1 +1,3 @@ export * from './nostr' +export * from './url' +export * from './utils' diff --git a/src/utils/url.ts b/src/utils/url.ts new file mode 100644 index 0000000..29b9be0 --- /dev/null +++ b/src/utils/url.ts @@ -0,0 +1,22 @@ +export const isValidUrl = (url: string) => { + try { + new URL(url) + return true + } catch (_) { + return false + } +} + +export const isValidImageUrl = (url: string) => { + const regex = /\.(jpeg|jpg|gif|png)$/ + return regex.test(url) +} + +export const isReachable = async (url: string) => { + try { + const response = await fetch(url, { method: 'HEAD' }) + return response.ok + } catch (error) { + return false + } +} diff --git a/src/utils/utils.ts b/src/utils/utils.ts new file mode 100644 index 0000000..504d101 --- /dev/null +++ b/src/utils/utils.ts @@ -0,0 +1,36 @@ +export enum LogType { + Info = 'info', + Error = 'error', + Warn = 'warn' +} + +/** + * Log function to conditionally log messages to the console + * + * @param isOn boolean or undefined indicating if logging is enabled + * @param type LogType indicating the type of log (info, error, warn) + * @param args unknown[] represents the rest parameters for log messages + */ +export const log = ( + isOn: boolean | undefined, // Flag to determine if logging is enabled + type: LogType, // Type of log (info, error, warn) + ...args: unknown[] // Log messages to be printed +) => { + if (!isOn) return // If logging is not enabled, return early + console[type](...args) // Log the messages to the console with the specified log type +} + +/** + * Creates a promise that rejects with a timeout error after a specified duration. + * @param ms The duration in milliseconds after which the promise should reject. Defaults to 60000 milliseconds (1 minute). + * @returns A promise that rejects with an Error('Timeout') after the specified duration. + */ +export const timeout = (ms: number = 60000) => { + return new Promise((_, reject) => { + // Set a timeout using setTimeout + setTimeout(() => { + // Reject the promise with an Error indicating a timeout + reject(new Error('Timeout')) + }, ms) // Timeout duration in milliseconds + }) +} diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 11f02fe..3e09383 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1 +1,10 @@ /// + +interface ImportMetaEnv { + readonly VITE_APP_RELAY: string + // more env variables... +} + +interface ImportMeta { + readonly env: ImportMetaEnv +}