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 (
+
+ )
+}
+
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
+ }}
+ />
+ onChange('game', '')}
>
- {({ index, style }) => (
- (isOptionClicked.current = true)}
- onClick={() => {
- onChange('game', filteredOptions[index].value)
- setSearchTerm('')
- if (inputRef.current) {
- inputRef.current.blur()
- }
- }}
- >
- {filteredOptions[index].label}
-
- )}
-
+
+
+
+
+ {({ 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
+}