diff --git a/package-lock.json b/package-lock.json index 8b327c5..57f7473 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,27 +1,21 @@ { "name": "qapp-core", - "version": "1.0.11", + "version": "1.0.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "qapp-core", - "version": "1.0.11", + "version": "1.0.13", "license": "MIT", "dependencies": { - "@emotion/react": "^11.14.0", - "@emotion/styled": "^11.14.0", - "@mui/icons-material": "^6.4.7", - "@mui/material": "^6.4.7", "@tanstack/react-virtual": "^3.13.2", - "@types/react": "^19.0.10", "bloom-filters": "^3.0.4", "buffer": "^6.0.3", "compressorjs": "^1.2.1", "crypto-js": "^4.2.0", "dayjs": "^1.11.13", "dompurify": "^3.2.4", - "react": "^19.0.0", "react-dropzone": "^14.3.8", "react-hot-toast": "^2.5.2", "react-intersection-observer": "^9.16.0", @@ -29,15 +23,29 @@ "zustand": "^4.3.2" }, "devDependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@mui/icons-material": "^6.4.7", + "@mui/material": "^6.4.7", "@types/crypto-js": "^4.2.2", + "@types/react": "^19.0.10", + "react": "^19.0.0", "tsup": "^8.4.0", "typescript": "^5.2.0" + }, + "peerDependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@mui/icons-material": "^6.4.7", + "@mui/material": "^6.4.7", + "react": "^19.0.0" } }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", @@ -51,6 +59,7 @@ "version": "7.26.9", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", + "dev": true, "dependencies": { "@babel/parser": "^7.26.9", "@babel/types": "^7.26.9", @@ -66,6 +75,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -78,6 +88,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -86,6 +97,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -94,6 +106,7 @@ "version": "7.26.9", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", + "dev": true, "dependencies": { "@babel/types": "^7.26.9" }, @@ -108,6 +121,7 @@ "version": "7.26.9", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", + "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -119,6 +133,7 @@ "version": "7.26.9", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/parser": "^7.26.9", @@ -132,6 +147,7 @@ "version": "7.26.9", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.9", @@ -149,6 +165,7 @@ "version": "7.26.9", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", + "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -161,6 +178,7 @@ "version": "11.13.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", @@ -179,6 +197,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -187,6 +206,7 @@ "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "dev": true, "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", @@ -198,12 +218,14 @@ "node_modules/@emotion/hash": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", - "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "dev": true }, "node_modules/@emotion/is-prop-valid": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "dev": true, "dependencies": { "@emotion/memoize": "^0.9.0" } @@ -211,12 +233,14 @@ "node_modules/@emotion/memoize": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", - "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "dev": true }, "node_modules/@emotion/react": { "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "dev": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -240,6 +264,7 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "dev": true, "dependencies": { "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", @@ -251,12 +276,14 @@ "node_modules/@emotion/sheet": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", - "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "dev": true }, "node_modules/@emotion/styled": { "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "dev": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -278,12 +305,14 @@ "node_modules/@emotion/unitless": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", - "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==" + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "dev": true }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "dev": true, "peerDependencies": { "react": ">=16.8.0" } @@ -291,12 +320,14 @@ "node_modules/@emotion/utils": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", - "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "dev": true }, "node_modules/@emotion/weak-memoize": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", - "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "dev": true }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.1", @@ -719,6 +750,7 @@ "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -732,6 +764,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" } @@ -740,6 +773,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" } @@ -747,12 +781,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" @@ -762,6 +798,7 @@ "version": "6.4.7", "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.7.tgz", "integrity": "sha512-XjJrKFNt9zAKvcnoIIBquXyFyhfrHYuttqMsoDS7lM7VwufYG4fAPw4kINjBFg++fqXM2BNAuWR9J7XVIuKIKg==", + "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" @@ -771,6 +808,7 @@ "version": "6.4.7", "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.7.tgz", "integrity": "sha512-Rk8cs9ufQoLBw582Rdqq7fnSXXZTqhYRbpe1Y5SAz9lJKZP3CIdrj0PfG8HJLGw1hrsHFN/rkkm70IDzhJsG1g==", + "dev": true, "dependencies": { "@babel/runtime": "^7.26.0" }, @@ -796,6 +834,7 @@ "version": "6.4.7", "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.7.tgz", "integrity": "sha512-K65StXUeGAtFJ4ikvHKtmDCO5Ab7g0FZUu2J5VpoKD+O6Y3CjLYzRi+TMlI3kaL4CL158+FccMoOd/eaddmeRQ==", + "dev": true, "dependencies": { "@babel/runtime": "^7.26.0", "@mui/core-downloads-tracker": "^6.4.7", @@ -844,6 +883,7 @@ "version": "6.4.6", "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.6.tgz", "integrity": "sha512-T5FxdPzCELuOrhpA2g4Pi6241HAxRwZudzAuL9vBvniuB5YU82HCmrARw32AuCiyTfWzbrYGGpZ4zyeqqp9RvQ==", + "dev": true, "dependencies": { "@babel/runtime": "^7.26.0", "@mui/utils": "^6.4.6", @@ -870,6 +910,7 @@ "version": "6.4.6", "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.6.tgz", "integrity": "sha512-vSWYc9ZLX46be5gP+FCzWVn5rvDr4cXC5JBZwSIkYk9xbC7GeV+0kCvB8Q6XLFQJy+a62bbqtmdwS4Ghi9NBlQ==", + "dev": true, "dependencies": { "@babel/runtime": "^7.26.0", "@emotion/cache": "^11.13.5", @@ -903,6 +944,7 @@ "version": "6.4.7", "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.7.tgz", "integrity": "sha512-7wwc4++Ak6tGIooEVA9AY7FhH2p9fvBMORT4vNLMAysH3Yus/9B9RYMbrn3ANgsOyvT3Z7nE+SP8/+3FimQmcg==", + "dev": true, "dependencies": { "@babel/runtime": "^7.26.0", "@mui/private-theming": "^6.4.6", @@ -942,6 +984,7 @@ "version": "7.2.21", "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.21.tgz", "integrity": "sha512-6HstngiUxNqLU+/DPqlUJDIPbzUBxIVHb1MmXP0eTWDIROiCR2viugXpEif0PPe2mLqqakPzzRClWAnK+8UJww==", + "dev": true, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -955,6 +998,7 @@ "version": "6.4.6", "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.6.tgz", "integrity": "sha512-43nZeE1pJF2anGafNydUcYFPtHwAqiBiauRtaMvurdrZI3YrUjHkAu43RBsxef7OFtJMXGiHFvq43kb7lig0sA==", + "dev": true, "dependencies": { "@babel/runtime": "^7.26.0", "@mui/types": "^7.2.21", @@ -994,6 +1038,7 @@ "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -1286,17 +1331,20 @@ "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==" + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "dev": true }, "node_modules/@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "dev": true }, "node_modules/@types/react": { "version": "19.0.10", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==", + "devOptional": true, "dependencies": { "csstype": "^3.0.2" } @@ -1305,6 +1353,7 @@ "version": "4.4.12", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "dev": true, "peerDependencies": { "@types/react": "*" } @@ -1362,6 +1411,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dev": true, "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", @@ -1488,6 +1538,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" } @@ -1511,6 +1562,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, "engines": { "node": ">=6" } @@ -1563,12 +1615,14 @@ "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==" + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -1584,6 +1638,7 @@ "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, "engines": { "node": ">= 6" } @@ -1626,6 +1681,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, "dependencies": { "ms": "^2.1.3" }, @@ -1642,6 +1698,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dev": true, "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" @@ -1671,6 +1728,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -1719,6 +1777,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "engines": { "node": ">=10" }, @@ -1754,7 +1813,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==" + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true }, "node_modules/foreground-child": { "version": "3.3.1", @@ -1790,6 +1850,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -1818,6 +1879,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" } @@ -1834,6 +1896,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -1845,6 +1908,7 @@ "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==", + "dev": true, "dependencies": { "react-is": "^16.7.0" } @@ -1852,7 +1916,8 @@ "node_modules/hoist-non-react-statics/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==" + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true }, "node_modules/ieee754": { "version": "1.2.1", @@ -1877,6 +1942,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -1892,6 +1958,7 @@ "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" } @@ -1899,7 +1966,8 @@ "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==" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true }, "node_modules/is-blob": { "version": "2.1.0", @@ -1938,6 +2006,7 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, "dependencies": { "hasown": "^2.0.2" }, @@ -1996,6 +2065,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, "bin": { "jsesc": "bin/jsesc" }, @@ -2006,7 +2076,8 @@ "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==" + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true }, "node_modules/lilconfig": { "version": "3.1.3", @@ -2023,7 +2094,8 @@ "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==" + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true }, "node_modules/load-tsconfig": { "version": "0.2.5", @@ -2094,7 +2166,8 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, "node_modules/mz": { "version": "2.7.0", @@ -2145,6 +2218,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" }, @@ -2156,6 +2230,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -2181,7 +2256,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==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/path-scurry": { "version": "1.11.1", @@ -2203,6 +2279,7 @@ "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" } @@ -2210,7 +2287,8 @@ "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true }, "node_modules/picomatch": { "version": "4.0.2", @@ -2398,12 +2476,14 @@ "node_modules/react-is": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", - "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==" + "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==", + "dev": true }, "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==", + "dev": true, "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", @@ -2436,12 +2516,14 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", @@ -2679,7 +2761,8 @@ "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "dev": true }, "node_modules/sucrase": { "version": "3.35.0", @@ -2707,6 +2790,7 @@ "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==", + "dev": true, "engines": { "node": ">= 0.4" }, diff --git a/package.json b/package.json index 9ff53a2..9a68faf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "qapp-core", - "version": "1.0.12", + "version": "1.0.13", "description": "Qortal's core React library with global state, UI components, and utilities", "main": "dist/index.js", "module": "dist/index.mjs", @@ -15,34 +15,41 @@ "dist" ], "scripts": { - "build": "tsup src/index.ts --format esm,cjs --dts", + "build": "tsup", "prepare": "npm run build", "clean": "rm -rf dist" }, "dependencies": { - "@emotion/react": "^11.14.0", - "@emotion/styled": "^11.14.0", - "@mui/icons-material": "^6.4.7", - "@mui/material": "^6.4.7", "@tanstack/react-virtual": "^3.13.2", - "@types/react": "^19.0.10", "bloom-filters": "^3.0.4", "buffer": "^6.0.3", "compressorjs": "^1.2.1", "crypto-js": "^4.2.0", "dayjs": "^1.11.13", "dompurify": "^3.2.4", - "react": "^19.0.0", "react-dropzone": "^14.3.8", "react-hot-toast": "^2.5.2", "react-intersection-observer": "^9.16.0", "short-unique-id": "^5.2.0", "zustand": "^4.3.2" }, + "peerDependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@mui/icons-material": "^6.4.7", + "@mui/material": "^6.4.7", + "react": "^19.0.0" +}, "devDependencies": { "@types/crypto-js": "^4.2.2", "tsup": "^8.4.0", - "typescript": "^5.2.0" + "typescript": "^5.2.0", + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@mui/icons-material": "^6.4.7", + "@mui/material": "^6.4.7", + "react": "^19.0.0", + "@types/react": "^19.0.10" }, "repository": { "type": "git", diff --git a/src/components/IndexManager/IndexManager.tsx b/src/components/IndexManager/IndexManager.tsx index 6687043..d6d6a43 100644 --- a/src/components/IndexManager/IndexManager.tsx +++ b/src/components/IndexManager/IndexManager.tsx @@ -1,25 +1,115 @@ import { + Avatar, Box, + Breadcrumbs, + Button, ButtonBase, + Card, Dialog, + DialogActions, DialogContent, + DialogContentText, DialogTitle, + Divider, + FormControlLabel, IconButton, + Radio, + RadioGroup, + TextField, Typography, } from "@mui/material"; -import React, { useState } from "react"; -import { useIndexStore } from "../../state/indexes"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; +import { IndexCategory, useIndexStore } from "../../state/indexes"; import CloseIcon from "@mui/icons-material/Close"; -import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'; -export const IndexManager = () => { +import ArrowBackIosIcon from "@mui/icons-material/ArrowBackIos"; +import { hashWordWithoutPublicSalt } from "../../utils/encryption"; +import { usePublish } from "../../hooks/usePublish"; +import { + dismissToast, + showError, + showLoading, + showSuccess, +} from "../../utils/toast"; +import { objectToBase64 } from "../../utils/base64"; +import CheckCircleIcon from "@mui/icons-material/CheckCircle"; +import { OptionsManager } from "../OptionsManager"; +import ShortUniqueId from "short-unique-id"; +import { Spacer } from "../../common/Spacer"; +import { useModal } from "../useModal"; +import { createAvatarLink } from "../../utils/qortal"; +import { extractComponents } from "../../utils/text"; +import NavigateNextIcon from "@mui/icons-material/NavigateNext"; + +const uid = new ShortUniqueId({ length: 10, dictionary: "alphanum" }); + +interface PropsMode { + link: string; + name: string; + mode: number; + setMode: (mode: number) => void; + username: string; +} + +interface PropsIndexManager { + username: string | null; +} + +const cleanString = (str: string) => str.replace(/\s{2,}/g, ' ').trim().toLocaleLowerCase(); + +export const IndexManager = ({ username }: PropsIndexManager) => { const open = useIndexStore((state) => state.open); const setOpen = useIndexStore((state) => state.setOpen); + const [title, setTitle] = useState(""); + const [description, setDescription] = useState(""); + const [hasMetadata, setHasMetadata] = useState(false); const [mode, setMode] = useState(1); - + const publish = usePublish(); const handleClose = () => { setOpen(null); setMode(1); + setTitle(""); + setDescription(""); + setHasMetadata(false); }; + + const getMetadata = useCallback(async (name: string, link: string) => { + try { + const identifierWithoutHash = name + link; + const identifier = await hashWordWithoutPublicSalt( + identifierWithoutHash, + 20 + ); + const rawData = await publish.fetchPublish( + { + name: name, + service: "METADATA", + identifier, + }, + "JSON" + ); + + if ( + rawData?.resource && + rawData?.resource?.data?.title && + rawData?.resource?.data?.description + ) { + setHasMetadata(true); + setDescription(rawData?.resource?.data?.description); + setTitle(rawData?.resource?.data?.title); + } + } catch (error) {} + }, []); + + useEffect(() => { + if (open?.name && open?.link) { + setTimeout(() => { + getMetadata(open?.name, open?.link); + }, 100); + } + }, [open?.link, open?.name]); + + if (!open || !username) return null; + return ( { > + {mode === 1 && ( + + )} + {mode === 2 && ( + + )} + {/* {mode === 3 && ( + + )} */} + {mode === 4 && ( + + )} + + ); +}; + +interface PropsEntryMode extends PropsMode { + hasMetadata: boolean; +} + +const EntryMode = ({ + link, + name, + mode, + setMode, + username, + hasMetadata, +}: PropsEntryMode) => { + return ( + <> { flexDirection: "column", gap: "20px", width: "100%", - alignItems: 'flex-start' + alignItems: "flex-start", }} > - setMode(1)}> - {mode === 1 && ( - <> - - - Create new index - - - - - Your indices - - - setMode(4)} - > - - Add metadata - - - - )} - - {mode === 4 && } + setMode(2)} + > + + Create new index + + + {/* + + Your indices + + */} + setMode(4)} + > + + Add metadata + {hasMetadata && } + + - + ); }; -const AddMetadata = () => { - return hello; +interface PropsAddMetadata extends PropsMode { + title: string; + description: string; + setTitle: (val: string) => void; + setDescription: (val: string) => void; +} + +const AddMetadata = ({ + link, + name, + mode, + setMode, + username, + title, + description, + setDescription, + setTitle, +}: PropsAddMetadata) => { + const publish = usePublish(); + + const disableButton = !title.trim() || !description.trim() || !name || !link; + + const createMetadata = async () => { + const loadId = showLoading("Publishing metadata..."); + try { + const identifierWithoutHash = name + link; + const identifier = await hashWordWithoutPublicSalt( + identifierWithoutHash, + 20 + ); + const objectToPublish = { + title, + description, + }; + const toBase64 = await objectToBase64(objectToPublish); + const res = await qortalRequest({ + action: "PUBLISH_QDN_RESOURCE", + service: "METADATA", + identifier: identifier, + base64: toBase64, + }); + + if (res?.signature) { + showSuccess("Successfully published metadata"); + publish.updatePublish( + { + identifier, + service: "METADATA", + name: username, + }, + objectToPublish + ); + } + } catch (error) { + const message = + error instanceof Error ? error.message : "Failed to publish metadata"; + showError(message); + } finally { + dismissToast(loadId); + } + }; + + const res = extractComponents(link || ""); + const appName = res?.name || ""; + + return ( + <> + + + setMode(1)}> + + + Example of how it could look like: + + + + + + } + aria-label="breadcrumb" + > + + {res?.service} + + + {appName} + + + {res?.path} + + + + + + + {title} + + + + {description} + + + + + + Title + setTitle(e.target.value)} + size="small" + placeholder="Add a title for the link" + slotProps={{ + htmlInput: { maxLength: 50 }, + }} + fullWidth + helperText={ + = 50 ? "error" : "text.secondary"} + > + {title.length}/{50} characters + + } + /> + + + Description + setDescription(e.target.value)} + size="small" + placeholder="Add a description for the link" + slotProps={{ + htmlInput: { maxLength: 120 }, + }} + helperText={ + = 120 ? "error" : "text.secondary"} + > + {description.length}/{120} characters + + } + /> + + + + + + + + ); }; + +interface PropsCreateIndex extends PropsMode { + category: IndexCategory; + rootName: string; +} + +const CreateIndex = ({ + link, + name, + mode, + setMode, + username, + category, + rootName, +}: PropsCreateIndex) => { + const [terms, setTerms] = useState([]); + const publish = usePublish(); + const [size, setSize] = useState(0); + const [fullSize, setFullSize] = useState(0); + const { isShow, onCancel, onOk, show } = useModal(); + const [recommendedIndices, setRecommendedIndices] = useState([]) + const [recommendedSelection, setRecommendedSelection] = useState("") + + + const objectToPublish = useMemo(() => { + if(recommendedSelection){ + return [ + { + n: name, + t: cleanString(recommendedSelection), + c: category, + l: link, + } + ] + } + return terms?.map((term) => { + return { + n: name, + t: cleanString(term), + c: category, + l: link, + }; + }); + }, [name, terms, category, link, recommendedSelection]); + const objectToCalculateSize = useMemo(() => { + return [ + { + n: name, + t: "", + c: category, + l: link, + }, + ]; + }, [name, category, link]); + + const shouldRecommendMax = !recommendedSelection && terms?.length === 1 && 230 - size > 50; + const recommendedSize = 230 - size; + + useEffect(() => { + const getSize = async (data: any, data2: any) => { + try { + const toBase64 = await objectToBase64(data); + const size = toBase64?.length; + setSize(size); + const toBase642 = await objectToBase64(data2); + const size2 = toBase642?.length; + setFullSize(size2); + } catch (error) {} + }; + getSize(objectToCalculateSize, objectToPublish); + }, [objectToCalculateSize, objectToPublish]); + + const getRecommendedIndices = useCallback(async (nameParam: string, linkParam: string, rootNameParam: string)=> { + try { + const hashedRootName = await hashWordWithoutPublicSalt(rootNameParam, 20); + const hashedLink = await hashWordWithoutPublicSalt(linkParam, 20); + const identifier = `idx-${hashedRootName}-${hashedLink}-`; + const res = await fetch(`/arbitrary/indices/${nameParam}/${identifier}`) + const data = await res.json() + setRecommendedIndices(data) + } catch (error) { + + } + }, []) + useEffect(()=> { + if(name && link && rootName){ + getRecommendedIndices(name, link, rootName) + } + }, [name, link, rootName, getRecommendedIndices]) + const disableButton = (terms.length === 0 && !recommendedSelection) || !name || !link; + + const createIndex = async () => { + const loadId = showLoading("Publishing index..."); + try { + const hashedRootName = await hashWordWithoutPublicSalt(rootName, 20); + const hashedLink = await hashWordWithoutPublicSalt(link, 20); + const randomUid = uid.rnd(); + const identifier = `idx-${hashedRootName}-${hashedLink}-${randomUid}`; + const toBase64 = await objectToBase64(objectToPublish); + const res = await qortalRequest({ + action: "PUBLISH_QDN_RESOURCE", + service: "JSON", + identifier: identifier, + base64: toBase64, + }); + + if (res?.signature) { + showSuccess("Successfully published index"); + publish.updatePublish( + { + identifier, + service: "JSON", + name: username, + }, + objectToPublish + ); + setTerms([]); + } + } catch (error) { + const message = + error instanceof Error ? error.message : "Failed to publish index"; + showError(message); + } finally { + dismissToast(loadId); + } + }; + + const handleChange = (event: React.ChangeEvent) => { + setRecommendedSelection((event.target as HTMLInputElement).value); + }; + + + return ( + <> + + + setMode(1)}> + + + {recommendedIndices?.length > 0 && ( + <> + Recommended Indices + + {recommendedIndices?.map((ri: any, i)=> { + return } label={ri?.term} /> + })} + + + + )} + + + + + + } label="Add search term" /> + + + {!recommendedSelection && ( + { + try { + if (terms?.length === 1 && termsNew?.length === 2) { + await show({ + message: "", + }); + } + setTerms(termsNew); + } catch (error) { + //error + } + }} + /> + )} + + + 230 ? "visible" : "hidden", + }} + > + It is recommended to keep your term character count below{" "} + {recommendedSize} characters + + + + + + + + + + Adding multiple indices + + + + Subsequent indices will keep your publish fees lower, but they will + have less strength in future search results. + + + + + + + + + ); +}; + + +const YourIndices = ({ + link, + name, + mode, + setMode, + username, + category, + rootName, + }: PropsCreateIndex) => { + const [terms, setTerms] = useState([]); + const publish = usePublish(); + const [size, setSize] = useState(0); + const [fullSize, setFullSize] = useState(0); + const { isShow, onCancel, onOk, show } = useModal(); + const [recommendedIndices, setRecommendedIndices] = useState([]) + const [recommendedSelection, setRecommendedSelection] = useState("") + const objectToPublish = useMemo(() => { + if(recommendedSelection){ + return [ + { + n: name, + t: recommendedSelection.toLocaleLowerCase(), + c: category, + l: link, + } + ] + } + return terms?.map((term) => { + return { + n: name, + t: term.toLocaleLowerCase(), + c: category, + l: link, + }; + }); + }, [name, terms, category, link, recommendedSelection]); + const objectToCalculateSize = useMemo(() => { + return [ + { + n: name, + t: "", + c: category, + l: link, + }, + ]; + }, [name, category, link]); + + const shouldRecommendMax = !recommendedSelection && terms?.length === 1 && 230 - size > 50; + const recommendedSize = 230 - size; + + useEffect(() => { + const getSize = async (data: any, data2: any) => { + try { + const toBase64 = await objectToBase64(data); + const size = toBase64?.length; + setSize(size); + const toBase642 = await objectToBase64(data2); + const size2 = toBase642?.length; + setFullSize(size2); + } catch (error) {} + }; + getSize(objectToCalculateSize, objectToPublish); + }, [objectToCalculateSize, objectToPublish]); + + const getYourIndices = useCallback(async (nameParam: string, linkParam: string, rootNameParam: string)=> { + try { + const hashedRootName = await hashWordWithoutPublicSalt(rootNameParam, 20); + const hashedLink = await hashWordWithoutPublicSalt(linkParam, 20); + const identifier = `idx-${hashedRootName}-${hashedLink}-`; + const res = await fetch(`/arbitrary/indices/${nameParam}/${identifier}`) + const data = await res.json() + setRecommendedIndices(data) + } catch (error) { + + } + }, []) + useEffect(()=> { + if(username && link && rootName){ + getYourIndices(username, link, rootName) + } + }, [username, link, rootName, getYourIndices]) + const disableButton = (terms.length === 0 && !recommendedSelection) || !name || !link; + + const createIndex = async () => { + const loadId = showLoading("Publishing index..."); + try { + const hashedRootName = await hashWordWithoutPublicSalt(rootName, 20); + const hashedLink = await hashWordWithoutPublicSalt(link, 20); + const randomUid = uid.rnd(); + const identifier = `idx-${hashedRootName}-${hashedLink}-${randomUid}`; + const toBase64 = await objectToBase64(objectToPublish); + const res = await qortalRequest({ + action: "PUBLISH_QDN_RESOURCE", + service: "JSON", + identifier: identifier, + base64: toBase64, + }); + + if (res?.signature) { + showSuccess("Successfully published index"); + publish.updatePublish( + { + identifier, + service: "JSON", + name: username, + }, + objectToPublish + ); + setTerms([]); + } + } catch (error) { + const message = + error instanceof Error ? error.message : "Failed to publish index"; + showError(message); + } finally { + dismissToast(loadId); + } + }; + + const handleChange = (event: React.ChangeEvent) => { + setRecommendedSelection((event.target as HTMLInputElement).value); + }; + + + return ( + <> + + + setMode(1)}> + + + {recommendedIndices?.length > 0 && ( + <> + Recommended Indices + + {recommendedIndices?.map((ri: any, i)=> { + return } label={ri?.term} /> + })} + + + + )} + + + + + + } label="Add search term" /> + + + {!recommendedSelection && ( + { + try { + if (terms?.length === 1 && termsNew?.length === 2) { + await show({ + message: "", + }); + } + setTerms(termsNew); + } catch (error) { + //error + } + }} + /> + )} + + + 230 ? "visible" : "hidden", + }} + > + It is recommended to keep your term character count below{" "} + {recommendedSize} characters + + + + + + + + + + Adding multiple indices + + + + Subsequent indices will keep your publish fees lower, but they will + have less strength in future search results. + + + + + + + + + ); + }; \ No newline at end of file diff --git a/src/components/OptionsManager.tsx b/src/components/OptionsManager.tsx new file mode 100644 index 0000000..65c6264 --- /dev/null +++ b/src/components/OptionsManager.tsx @@ -0,0 +1,89 @@ +import { useState } from "react"; +import { TextField, Button, Chip, Box, Stack } from "@mui/material"; + +interface PropsOptionsManager { + items: string[]; + setItems: (items: string[]) => void; + label?: string; + maxLength: number; + onlyStrings?: boolean; +} + +export function OptionsManager({ + items, + setItems, + label = "Keyword", + maxLength, + onlyStrings, +}: PropsOptionsManager) { + const [inputValue, setInputValue] = useState(""); + const [editIndex, setEditIndex] = useState(null); + + const handleAddOrUpdateItem = () => { + const trimmed = inputValue.trim(); + if (!trimmed) return; + + let value: string; + if (onlyStrings) { + value = trimmed; + } else { + value = isNaN(Number(trimmed)) ? trimmed : String(Number(trimmed)); + } + + if (editIndex !== null) { + const updatedItems = [...items]; + updatedItems[editIndex] = value; + setItems(updatedItems); + setEditIndex(null); + } else { + if (maxLength && items.length >= maxLength) return; + if (!items.includes(value)) { + setItems([...items, value]); + } + } + + setInputValue(""); + }; + + const handleDeleteItem = (index: number) => { + setItems(items.filter((_, i) => i !== index)); + }; + + const handleEditItem = (index: number) => { + setInputValue(items[index]); + setEditIndex(index); + }; + + return ( + + + setInputValue(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") handleAddOrUpdateItem(); + }} + sx={{ width: "240px" }} + /> + + + + + {items.map((item, index) => ( + handleDeleteItem(index)} + onClick={() => handleEditItem(index)} + sx={{ margin: 0.5 }} + /> + ))} + + + ); +} diff --git a/src/components/useModal.tsx b/src/components/useModal.tsx new file mode 100644 index 0000000..0d18e9d --- /dev/null +++ b/src/components/useModal.tsx @@ -0,0 +1,58 @@ +import { useRef, useState } from 'react'; + +interface State { + isShow: boolean; +} + +interface Message { + message?: string; +} + +interface PromiseHandlers { + resolve: (value: T) => void; + reject: () => void; +} + +export const useModal = () => { + const [state, setState] = useState({ isShow: false }); + const [message, setMessage] = useState({ + message: "" + }); + + const promiseConfig = useRef | null>(null); + + const show = (data: Message): Promise => { + setMessage(data); + return new Promise((resolve, reject) => { + promiseConfig.current = { resolve, reject }; + setState({ isShow: true }); + }); + }; + + const hide = () => { + setState({ isShow: false }); + setMessage({ message: "" }); + }; + + const onOk = (payload: T) => { + if (promiseConfig.current) { + promiseConfig.current.resolve(payload); + } + hide(); + }; + + const onCancel = () => { + if (promiseConfig.current) { + promiseConfig.current.reject(); + } + hide(); + }; + + return { + show, + onOk, + onCancel, + isShow: state.isShow, + message, + }; +}; diff --git a/src/context/GlobalProvider.tsx b/src/context/GlobalProvider.tsx index 3e0a63b..3e486af 100644 --- a/src/context/GlobalProvider.tsx +++ b/src/context/GlobalProvider.tsx @@ -32,7 +32,7 @@ interface GlobalProviderProps { appName: string; publicSalt: string }; - toastStyle: CSSProperties + toastStyle?: CSSProperties } // ✅ Create Context with Proper Type @@ -44,6 +44,7 @@ const GlobalContext = createContext(null); export const GlobalProvider = ({ children, config, toastStyle = {} }: GlobalProviderProps) => { // ✅ Call hooks and pass in options dynamically const auth = useAuth(config?.auth || {}); + const appInfo = useAppInfo(config.appName, config?.publicSalt) const lists = useResources() const identifierOperations = useIdentifiers(config.publicSalt, config.appName) @@ -51,6 +52,8 @@ export const GlobalProvider = ({ children, config, toastStyle = {} }: GlobalProv const indexOperations = useIndexes() // ✅ Merge all hooks into a single `contextValue` const contextValue = useMemo(() => ({ auth, lists, appInfo, identifierOperations, localStorageOperations, indexOperations }), [auth, lists, appInfo, identifierOperations, localStorageOperations]); + + return ( - + + {children} ); diff --git a/src/hooks/useIndexes.tsx b/src/hooks/useIndexes.tsx index 8aa28df..c1e49d6 100644 --- a/src/hooks/useIndexes.tsx +++ b/src/hooks/useIndexes.tsx @@ -4,11 +4,13 @@ import { OpenIndex, useIndexStore } from "../state/indexes"; export const useIndexes = () => { const setOpen = useIndexStore((state) => state.setOpen); const openPageIndexManager = useCallback( - ({ link, name }: OpenIndex) => { - if(!link || !name) return + ({ link, name, category, rootName }: OpenIndex) => { + if(!link || !name || !category) return setOpen({ name, link, + category, + rootName }); }, [setOpen] diff --git a/src/index.ts b/src/index.ts index 146862c..b992a1f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ export { useResourceStatus } from './hooks/useResourceStatus'; export { Spacer } from './common/Spacer'; import './index.css' +export { IndexCategory } from './state/indexes'; export { hashWordWithoutPublicSalt } from './utils/encryption'; export { createAvatarLink } from './utils/qortal'; export { objectToBase64 } from './utils/base64'; diff --git a/src/state/indexes.ts b/src/state/indexes.ts index 08ff4e8..77f4913 100644 --- a/src/state/indexes.ts +++ b/src/state/indexes.ts @@ -1,8 +1,23 @@ import { create } from "zustand"; +export enum IndexCategory { + 'PUBLIC_PAGE_VIDEO' = 1, + 'PUBLIC_PAGE_AUDIO' = 2, + 'PUBLIC_PAGE_IMAGE' = 3, + 'PUBLIC_PAGE_PDF' = 4, + 'PUBLIC_PAGE_OTHER' = 5, + 'PUBLIC_RESOURCE_VIDEO' = 6, + 'PUBLIC_RESOURCE_AUDIO' = 7, + 'PUBLIC_RESOURCE_IMAGE' = 8, + 'PUBLIC_RESOURCE_PDF' = 9, + 'PUBLIC_RESOURCE_OTHER' = 10, +} + export interface OpenIndex { link: string name: string + category: IndexCategory + rootName: string } interface IndexState { diff --git a/src/utils/encryption.ts b/src/utils/encryption.ts index bcacd34..35faa5f 100644 --- a/src/utils/encryption.ts +++ b/src/utils/encryption.ts @@ -17,36 +17,39 @@ export enum EnumCollisionStrength { ENTITY_LABEL = 6, } -export async function hashWord( - word: string, - collisionStrength: number, - publicSalt: string -): Promise { - const saltedWord = publicSalt + word; - try { - if (!crypto?.subtle?.digest) throw new Error("Web Crypto not available"); +// Custom URL-safe replacements (reserving '-' and '_') +const safeBase64 = (base64: string): string => + base64 + .replace(/\+/g, ".") // Replace '+' with '.' (URL-safe) + .replace(/\//g, "~") // Replace '/' with '~' (URL-safe) + .replace(/_/g, "!") // Replace '_' with '!' if needed (optional) + .replace(/=+$/, ""); // Remove padding - const encoded = new TextEncoder().encode(saltedWord); - const hashBuffer = await crypto.subtle.digest("SHA-256", encoded); - return Buffer.from(hashBuffer) - .toString("base64") - .replace(/\+/g, "-") - .replace(/\//g, "_") - .replace(/=+$/, "") - .slice(0, collisionStrength); - } catch (err) { - const hash = SHA256(saltedWord); - const base64 = EncBase64.stringify(hash); + export async function hashWord( + word: string, + collisionStrength: number, + publicSalt: string + ): Promise { + const saltedWord = publicSalt + word; + + try { + if (!crypto?.subtle?.digest) throw new Error("Web Crypto not available"); + + const encoded = new TextEncoder().encode(saltedWord); + const hashBuffer = await crypto.subtle.digest("SHA-256", encoded); + const base64 = Buffer.from(hashBuffer).toString("base64"); + + return safeBase64(base64).slice(0, collisionStrength); + } catch (err) { + const hash = SHA256(saltedWord); + const base64 = EncBase64.stringify(hash); + + return safeBase64(base64).slice(0, collisionStrength); + } + } - return base64 - .replace(/\+/g, "-") - .replace(/\//g, "_") - .replace(/=+$/, "") - .slice(0, collisionStrength); - } -} export async function hashWordWithoutPublicSalt( word: string, @@ -57,22 +60,14 @@ export async function hashWordWithoutPublicSalt( const encoded = new TextEncoder().encode(word); const hashBuffer = await crypto.subtle.digest("SHA-256", encoded); + const base64 = Buffer.from(hashBuffer).toString("base64"); - return Buffer.from(hashBuffer) - .toString("base64") - .replace(/\+/g, "-") - .replace(/\//g, "_") - .replace(/=+$/, "") - .slice(0, collisionStrength); + return safeBase64(base64).slice(0, collisionStrength); } catch (err) { const hash = SHA256(word); const base64 = EncBase64.stringify(hash); - return base64 - .replace(/\+/g, "-") - .replace(/\//g, "_") - .replace(/=+$/, "") - .slice(0, collisionStrength); + return safeBase64(base64).slice(0, collisionStrength); } } diff --git a/tsup.config.ts b/tsup.config.ts new file mode 100644 index 0000000..4368e14 --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['esm', 'cjs'], + dts: true, + external: [ + 'react', + '@mui/material', + '@mui/system', + '@emotion/react', + '@emotion/styled', + ], +});