forked from Qortal/q-tube
Merge pull request #88 from MergeMerc/main
Q-Tube with Support for new Qortal Core Features
This commit is contained in:
257
package-lock.json
generated
257
package-lock.json
generated
@@ -10,16 +10,17 @@
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@mui/icons-material": "^6.3.0",
|
||||
"@mui/lab": "^6.0.0-beta.21",
|
||||
"@mui/material": "^6.3.0",
|
||||
"@mui/icons-material": "^7.1.0",
|
||||
"@mui/lab": "7.0.0-beta.12",
|
||||
"@mui/material": "^7.1.0",
|
||||
"@preact/signals-react": "^2.3.0",
|
||||
"@reduxjs/toolkit": "^2.5.0",
|
||||
"compressorjs": "^1.2.1",
|
||||
"dompurify": "^3.2.3",
|
||||
"jotai": "^2.12.4",
|
||||
"localforage": "^1.10.0",
|
||||
"moment": "^2.30.1",
|
||||
"qapp-core": "^1.0.17",
|
||||
"qapp-core": "^1.0.29",
|
||||
"quill": "^2.0.2",
|
||||
"quill-image-resize-module-react": "^3.0.0",
|
||||
"react": "^19.0.0",
|
||||
@@ -291,12 +292,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
|
||||
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz",
|
||||
"integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@@ -1063,40 +1062,6 @@
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.6.8",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz",
|
||||
"integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==",
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.6.12",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz",
|
||||
"integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.6.0",
|
||||
"@floating-ui/utils": "^0.2.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/react-dom": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz",
|
||||
"integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz",
|
||||
"integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig=="
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||
@@ -1201,52 +1166,23 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/base": {
|
||||
"version": "5.0.0-beta.68",
|
||||
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.68.tgz",
|
||||
"integrity": "sha512-F1JMNeLS9Qhjj3wN86JUQYBtJoXyQvknxlzwNl6eS0ZABo1MiohMONj3/WQzYPSXIKC2bS/ZbyBzdHhi2GnEpA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@floating-ui/react-dom": "^2.1.1",
|
||||
"@mui/types": "^7.2.20",
|
||||
"@mui/utils": "^6.3.0",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"clsx": "^2.1.1",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/core-downloads-tracker": {
|
||||
"version": "6.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.10.tgz",
|
||||
"integrity": "sha512-cblGjlM6+xsptwyaALw8RbRIUoqmKxOqLxlk2LkTDhxqUuql1YSOKKLH3w+Yd2QLz28b7MR65sx1OjsRZUfOSQ==",
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.1.0.tgz",
|
||||
"integrity": "sha512-E0OqhZv548Qdc0PwWhLVA2zmjJZSTvaL4ZhoswmI8NJEC1tpW2js6LLP827jrW9MEiXYdz3QS6+hask83w74yQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/icons-material": {
|
||||
"version": "6.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.10.tgz",
|
||||
"integrity": "sha512-c2KdFl4KZ0QYC+JSDTMCNjcuOL2rVSdIx/beo7FwJDh2e9XqC1MoLCjw6L1Jo40zbArkgJyg3oFORbXcRfgZOA==",
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.1.0.tgz",
|
||||
"integrity": "sha512-1mUPMAZ+Qk3jfgL5ftRR06ATH/Esi0izHl1z56H+df6cwIlCWG66RXciUqeJCttbOXOQ5y2DCjLZI/4t3Yg3LA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0"
|
||||
"@babel/runtime": "^7.27.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -1256,7 +1192,7 @@
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@mui/material": "^6.4.10",
|
||||
"@mui/material": "^7.1.0",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
@@ -1267,15 +1203,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/lab": {
|
||||
"version": "6.0.0-beta.21",
|
||||
"resolved": "https://registry.npmjs.org/@mui/lab/-/lab-6.0.0-beta.21.tgz",
|
||||
"integrity": "sha512-hiFZgTwBNhJMUlEhmqfW4+5wy3C8UF9KFuzSOux6x4kgc9hsC0l+motXcF1Vyh+jhJYGeZ6yUoImqCf9RWzEvw==",
|
||||
"version": "7.0.0-beta.12",
|
||||
"resolved": "https://registry.npmjs.org/@mui/lab/-/lab-7.0.0-beta.12.tgz",
|
||||
"integrity": "sha512-685MmzByCy3Vmb7xI6J8qOQm4l7yqfVTOLwxVmNV1EHBKuJiMuoX4/2vAAEGfNbDeEfWQsp7aBWanYpSWe1iRA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@mui/base": "5.0.0-beta.68",
|
||||
"@mui/system": "^6.3.0",
|
||||
"@mui/types": "^7.2.20",
|
||||
"@mui/utils": "^6.3.0",
|
||||
"@babel/runtime": "^7.27.1",
|
||||
"@mui/system": "^7.1.0",
|
||||
"@mui/types": "^7.4.2",
|
||||
"@mui/utils": "^7.1.0",
|
||||
"clsx": "^2.1.1",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
@@ -1289,8 +1225,8 @@
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@mui/material": "^6.3.0",
|
||||
"@mui/material-pigment-css": "^6.3.0",
|
||||
"@mui/material": "^7.1.0",
|
||||
"@mui/material-pigment-css": "^7.1.0",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
@@ -1311,21 +1247,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/material": {
|
||||
"version": "6.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.10.tgz",
|
||||
"integrity": "sha512-L1B0+Vg9NFjo3NcfODH3bohl6fIkzjyDBHBHb3Al4QI7owaJrFm2sSDyfz++iatzICug6U6q5tHLQrCLO71xkg==",
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-7.1.0.tgz",
|
||||
"integrity": "sha512-ahUJdrhEv+mCp4XHW+tHIEYzZMSRLg8z4AjUOsj44QpD1ZaMxQoVOG2xiHvLFdcsIPbgSRx1bg1eQSheHBgvtg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@mui/core-downloads-tracker": "^6.4.10",
|
||||
"@mui/system": "^6.4.10",
|
||||
"@mui/types": "~7.2.24",
|
||||
"@mui/utils": "^6.4.9",
|
||||
"@babel/runtime": "^7.27.1",
|
||||
"@mui/core-downloads-tracker": "^7.1.0",
|
||||
"@mui/system": "^7.1.0",
|
||||
"@mui/types": "^7.4.2",
|
||||
"@mui/utils": "^7.1.0",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@types/react-transition-group": "^4.4.12",
|
||||
"clsx": "^2.1.1",
|
||||
"csstype": "^3.1.3",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^19.0.0",
|
||||
"react-is": "^19.1.0",
|
||||
"react-transition-group": "^4.4.5"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1338,7 +1275,7 @@
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@mui/material-pigment-css": "^6.4.10",
|
||||
"@mui/material-pigment-css": "^7.1.0",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
@@ -1359,12 +1296,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/private-theming": {
|
||||
"version": "6.4.9",
|
||||
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.9.tgz",
|
||||
"integrity": "sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw==",
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.1.0.tgz",
|
||||
"integrity": "sha512-4Kck4jxhqF6YxNwJdSae1WgDfXVg0lIH6JVJ7gtuFfuKcQCgomJxPvUEOySTFRPz1IZzwz5OAcToskRdffElDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@mui/utils": "^6.4.9",
|
||||
"@babel/runtime": "^7.27.1",
|
||||
"@mui/utils": "^7.1.0",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1385,11 +1323,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/styled-engine": {
|
||||
"version": "6.4.9",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.9.tgz",
|
||||
"integrity": "sha512-qZRWO0cT407NI4ZRjZcH+1SOu8f3JzLHqdMlg52GyEufM9pkSZFnf7xjpwnlvkixcGjco6wLlMD0VB43KRcBuA==",
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.1.0.tgz",
|
||||
"integrity": "sha512-m0mJ0c6iRC+f9hMeRe0W7zZX1wme3oUX0+XTVHjPG7DJz6OdQ6K/ggEOq7ZdwilcpdsDUwwMfOmvO71qDkYd2w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@babel/runtime": "^7.27.1",
|
||||
"@emotion/cache": "^11.13.5",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"@emotion/sheet": "^1.4.0",
|
||||
@@ -1418,15 +1357,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/system": {
|
||||
"version": "6.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.10.tgz",
|
||||
"integrity": "sha512-RyBGQwP3tgo4JEibK+RwVu1a6nQ6y8urMCNsb2aiN/nvTxxumq6P26aoG4GTUf8L4O1sthC4lMXlP4r8ixDkMg==",
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-7.1.0.tgz",
|
||||
"integrity": "sha512-iedAWgRJMCxeMHvkEhsDlbvkK+qKf9me6ofsf7twk/jfT4P1ImVf7Rwb5VubEA0sikrVL+1SkoZM41M4+LNAVA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@mui/private-theming": "^6.4.9",
|
||||
"@mui/styled-engine": "^6.4.9",
|
||||
"@mui/types": "~7.2.24",
|
||||
"@mui/utils": "^6.4.9",
|
||||
"@babel/runtime": "^7.27.1",
|
||||
"@mui/private-theming": "^7.1.0",
|
||||
"@mui/styled-engine": "^7.1.0",
|
||||
"@mui/types": "^7.4.2",
|
||||
"@mui/utils": "^7.1.0",
|
||||
"clsx": "^2.1.1",
|
||||
"csstype": "^3.1.3",
|
||||
"prop-types": "^15.8.1"
|
||||
@@ -1457,9 +1397,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/types": {
|
||||
"version": "7.2.24",
|
||||
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz",
|
||||
"integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==",
|
||||
"version": "7.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.2.tgz",
|
||||
"integrity": "sha512-edRc5JcLPsrlNFYyTPxds+d5oUovuUxnnDtpJUbP6WMeV4+6eaX/mqai1ZIWT62lCOe0nlrON0s9HDiv5en5bA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
@@ -1470,16 +1414,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/utils": {
|
||||
"version": "6.4.9",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.9.tgz",
|
||||
"integrity": "sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg==",
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.1.0.tgz",
|
||||
"integrity": "sha512-/OM3S8kSHHmWNOP+NH9xEtpYSG10upXeQ0wLZnfDgmgadTAk5F4MQfFLyZ5FCRJENB3eRzltMmaNl6UtDnPovw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@mui/types": "~7.2.24",
|
||||
"@babel/runtime": "^7.27.1",
|
||||
"@mui/types": "^7.4.2",
|
||||
"@types/prop-types": "^15.7.14",
|
||||
"clsx": "^2.1.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^19.0.0"
|
||||
"react-is": "^19.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -1962,7 +1907,8 @@
|
||||
"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==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.0.2",
|
||||
@@ -2700,6 +2646,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/dexie": {
|
||||
"version": "4.0.11",
|
||||
"resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.11.tgz",
|
||||
"integrity": "sha512-SOKO002EqlvBYYKQSew3iymBoN2EQ4BDw/3yprjh7kAfFzjBYkaMNa/pZvcA7HSWlcKSQb9XhPe3wKyQ0x4A8A==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/dom-helpers": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
||||
@@ -3568,6 +3520,27 @@
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jotai": {
|
||||
"version": "2.12.4",
|
||||
"resolved": "https://registry.npmjs.org/jotai/-/jotai-2.12.4.tgz",
|
||||
"integrity": "sha512-eFXLJol4oOLM8BS1+QV+XwaYQITG8n1tatBCFl4F5HE3zR5j2WIK8QpMt7VJIYmlogNUZfvB7wjwLoVk+umB9Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=17.0.0",
|
||||
"react": ">=17.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -4066,9 +4039,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/qapp-core": {
|
||||
"version": "1.0.17",
|
||||
"resolved": "https://registry.npmjs.org/qapp-core/-/qapp-core-1.0.17.tgz",
|
||||
"integrity": "sha512-c++yhwAIlg4HhJcp7J2UNBSo/+va7I8GbtmqZ91HNHN2uk5qrwnxIQvTdWXynLvaTVrq16pW7Ry92lTncumGHg==",
|
||||
"version": "1.0.29",
|
||||
"resolved": "https://registry.npmjs.org/qapp-core/-/qapp-core-1.0.29.tgz",
|
||||
"integrity": "sha512-w1tBniw6yFxmOc+1H9584kCwL3mpHpIEtU31vv9FIAwltfRMF0hAidTC2tVcvFqNNYKn7rfvDuhhbgayI/elBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/react-virtual": "^3.13.2",
|
||||
"bloom-filters": "^3.0.4",
|
||||
@@ -4076,6 +4050,7 @@
|
||||
"compressorjs": "^1.2.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"dexie": "^4.0.11",
|
||||
"dompurify": "^3.2.4",
|
||||
"react-dropzone": "^14.3.8",
|
||||
"react-hot-toast": "^2.5.2",
|
||||
@@ -4086,8 +4061,8 @@
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@mui/icons-material": "^6.4.7",
|
||||
"@mui/material": "^6.4.7",
|
||||
"@mui/icons-material": "^7.0.1",
|
||||
"@mui/material": "^7.0.1",
|
||||
"react": "^19.0.0"
|
||||
}
|
||||
},
|
||||
@@ -4300,9 +4275,10 @@
|
||||
}
|
||||
},
|
||||
"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=="
|
||||
"version": "19.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz",
|
||||
"integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-quill-new": {
|
||||
"version": "3.3.3",
|
||||
@@ -4460,11 +4436,6 @@
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz",
|
||||
"integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A=="
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"node_modules/regexp.prototype.flags": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"@reduxjs/toolkit": "^2.5.0",
|
||||
"compressorjs": "^1.2.1",
|
||||
"dompurify": "^3.2.3",
|
||||
"jotai": "^2.12.4",
|
||||
"localforage": "^1.10.0",
|
||||
"moment": "^2.30.1",
|
||||
"qapp-core": "^1.0.29",
|
||||
|
||||
@@ -3,7 +3,6 @@ import { ThemeProvider } from "@mui/material/styles";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Provider, useSelector } from "react-redux";
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
import { persistStore } from "redux-persist";
|
||||
import { PersistGate } from "redux-persist/integration/react";
|
||||
import { subscriptionListFilter } from "./App-Functions.ts";
|
||||
import Notification from "./components/common/Notification/Notification";
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { GlobalProvider } from 'qapp-core'
|
||||
import React from 'react'
|
||||
import { useSelector } from 'react-redux';
|
||||
import { RootState } from './state/store';
|
||||
|
||||
@@ -7,16 +6,16 @@ export const QappCoreWrapper = ({children}) => {
|
||||
const { user } = useSelector((state: RootState) => state.auth);
|
||||
return (
|
||||
<GlobalProvider
|
||||
config={{
|
||||
auth: {
|
||||
authenticateOnMount: false,
|
||||
userAccountInfo: {
|
||||
address: user?.address,
|
||||
publicKey: user?.publicKey
|
||||
}
|
||||
},
|
||||
publicSalt: "usVbeM9YpjGCbLrTcc78YJS0ap1AxDkHAOMZrp3+wDY=",
|
||||
appName: "Q-Tube",
|
||||
config={{
|
||||
auth: {
|
||||
authenticateOnMount: false,
|
||||
userAccountInfo: {
|
||||
address: user?.address,
|
||||
publicKey: user?.publicKey
|
||||
}
|
||||
},
|
||||
publicSalt: "usVbeM9YpjGCbLrTcc78YJS0ap1AxDkHAOMZrp3+wDY=",
|
||||
appName: "Q-Tube",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -40,7 +40,7 @@ export const MultiplePublish = ({
|
||||
const hasStarted = useRef(false);
|
||||
const publish = useCallback(async (pub: any) => {
|
||||
const lengthOfResources = pub?.resources?.length;
|
||||
const lengthOfTimeout = lengthOfResources * 30000;
|
||||
const lengthOfTimeout = lengthOfResources * 1200000; // Time out in QR, Seconds = 20 Minutes
|
||||
return await qortalRequestWithTimeout(pub, lengthOfTimeout);
|
||||
}, []);
|
||||
const [isPublishing, setIsPublishing] = useState(true);
|
||||
|
||||
34
src/components/UserDropDown.tsx
Normal file
34
src/components/UserDropDown.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Avatar, useTheme } from "@mui/material";
|
||||
import { AccountCircleSVG } from "../assets/svgs/AccountCircleSVG";
|
||||
import { menuIconSize } from "../constants/Misc";
|
||||
import {
|
||||
DropdownContainer,
|
||||
DropdownText,
|
||||
AvatarContainer
|
||||
} from "./layout/Navbar/Navbar-styles";
|
||||
|
||||
interface UserDropDownProps {
|
||||
userName: string;
|
||||
handleMyChannelLink: (username: string) => void;
|
||||
popMenuRef: React.RefObject<{ closePopover: () => void }>;
|
||||
}
|
||||
|
||||
export const UserDropDown = ({ userName, handleMyChannelLink, popMenuRef }: UserDropDownProps) => {
|
||||
const theme = useTheme();
|
||||
const userAvatar = `/arbitrary/THUMBNAIL/${userName}/avatar?async=true`;
|
||||
|
||||
return (
|
||||
<DropdownContainer
|
||||
onClick={() => {
|
||||
handleMyChannelLink(userName);
|
||||
popMenuRef.current.closePopover();
|
||||
}}
|
||||
>
|
||||
<Avatar src={userAvatar}>
|
||||
{userName?.charAt(0).toUpperCase()}
|
||||
</Avatar>
|
||||
<DropdownText>{userName}</DropdownText>
|
||||
</DropdownContainer>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -125,20 +125,11 @@ export default function FileElement({
|
||||
try {
|
||||
const { name, service, identifier } = fileInfo;
|
||||
|
||||
const url = `/arbitrary/${service}/${name}/${identifier}`;
|
||||
fetch(url)
|
||||
.then(response => response.blob())
|
||||
.then(async blob => {
|
||||
await qortalRequest({
|
||||
action: "SAVE_FILE",
|
||||
blob,
|
||||
filename: filename,
|
||||
mimeType,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error fetching the video:", error);
|
||||
});
|
||||
await qortalRequest({
|
||||
action: "SAVE_FILE",
|
||||
location: fileInfo,
|
||||
filename: filename,
|
||||
});
|
||||
} catch (error: any) {
|
||||
let notificationObj: any = null;
|
||||
if (typeof error === "string") {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Popover, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { Popover, useMediaQuery, useTheme, Avatar } from "@mui/material";
|
||||
import { AccountCircleSVG } from "../../../../assets/svgs/AccountCircleSVG.tsx";
|
||||
import { headerIconSize, menuIconSize } from "../../../../constants/Misc.ts";
|
||||
import { BlockedNamesModal } from "../../../common/BlockedNamesModal/BlockedNamesModal.tsx";
|
||||
@@ -9,21 +9,27 @@ import {
|
||||
NavbarName,
|
||||
} from "../Navbar-styles.tsx";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import { useRef, useState } from "react";
|
||||
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import PersonOffIcon from "@mui/icons-material/PersonOff";
|
||||
import { RootState } from "../../../../state/store";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { PopMenu, PopMenuRefType } from "../../../common/PopMenu.tsx";
|
||||
|
||||
import { UserDropDown } from "../../../UserDropDown.tsx";
|
||||
import { Names } from "../../../../state/global/names.ts";
|
||||
import { setName } from "../../../../state/features/authSlice.ts";
|
||||
export interface NavBarMenuProps {
|
||||
isShowMenu: boolean;
|
||||
userAvatar: string;
|
||||
userName: string | null;
|
||||
allNames: Names;
|
||||
}
|
||||
|
||||
export const UserMenu = ({
|
||||
isShowMenu,
|
||||
userAvatar,
|
||||
userName,
|
||||
allNames
|
||||
}: NavBarMenuProps) => {
|
||||
const isScreenSmall = !useMediaQuery(`(min-width:600px)`);
|
||||
const theme = useTheme();
|
||||
@@ -32,10 +38,12 @@ export const UserMenu = ({
|
||||
useState<boolean>(false);
|
||||
const popMenuRef = useRef<PopMenuRefType>(null);
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleMyChannelLink = () => {
|
||||
navigate(`/channel/${userName}`);
|
||||
};
|
||||
const handleMyChannelLink = useCallback((switchToName: string) => {
|
||||
dispatch(setName(switchToName));
|
||||
navigate(`/channel/${switchToName}`);
|
||||
}, [navigate]);
|
||||
|
||||
const onCloseBlockedNames = () => {
|
||||
setIsOpenBlockedNamesModal(false);
|
||||
@@ -43,58 +51,30 @@ export const UserMenu = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
{isShowMenu && (
|
||||
{isShowMenu && (
|
||||
<>
|
||||
<PopMenu
|
||||
ref={popMenuRef}
|
||||
MenuHeader={
|
||||
<AvatarContainer>
|
||||
{!isScreenSmall && <NavbarName>{userName}</NavbarName>}
|
||||
{!userAvatar ? (
|
||||
<AccountCircleSVG
|
||||
color={theme.palette.text.primary}
|
||||
width={headerIconSize}
|
||||
height={headerIconSize}
|
||||
/>
|
||||
) : (
|
||||
<img
|
||||
src={userAvatar}
|
||||
alt="User Avatar"
|
||||
width={headerIconSize}
|
||||
height={headerIconSize}
|
||||
style={{
|
||||
borderRadius: "50%",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Avatar src={userAvatar}>
|
||||
{userName?.charAt(0).toUpperCase()}
|
||||
</Avatar>
|
||||
</AvatarContainer>
|
||||
}
|
||||
>
|
||||
<DropdownContainer
|
||||
onClick={() => {
|
||||
handleMyChannelLink();
|
||||
popMenuRef.current.closePopover();
|
||||
}}
|
||||
>
|
||||
{!userAvatar ? (
|
||||
<AccountCircleSVG
|
||||
color={theme.palette.text.primary}
|
||||
width={menuIconSize}
|
||||
height={menuIconSize}
|
||||
|
||||
{
|
||||
allNames.map((name) => (
|
||||
<UserDropDown key={name.name}
|
||||
userName={name.name}
|
||||
handleMyChannelLink={handleMyChannelLink}
|
||||
popMenuRef={popMenuRef}
|
||||
/>
|
||||
) : (
|
||||
<img
|
||||
src={userAvatar}
|
||||
alt="User Avatar"
|
||||
width={menuIconSize}
|
||||
height={menuIconSize}
|
||||
style={{
|
||||
borderRadius: "50%",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<DropdownText>{userName}</DropdownText>
|
||||
</DropdownContainer>
|
||||
))
|
||||
}
|
||||
|
||||
<DropdownContainer
|
||||
onClick={() => {
|
||||
setIsOpenBlockedNamesModal(true);
|
||||
|
||||
@@ -6,16 +6,18 @@ import { PublishMenu } from "./Components/PublishMenu.tsx";
|
||||
import { QtubeLogo } from "./Components/QtubeLogo.tsx";
|
||||
import { UserMenu } from "./Components/UserMenu.tsx";
|
||||
import { CustomAppBar } from "./Navbar-styles";
|
||||
import { Names } from "./../../../state/global/names.ts";
|
||||
|
||||
interface Props {
|
||||
isAuthenticated: boolean;
|
||||
userName: string | null;
|
||||
allNames: Names;
|
||||
userAvatar: string;
|
||||
authenticate: () => void;
|
||||
setTheme: (val: string) => void;
|
||||
}
|
||||
|
||||
const NavBar: React.FC<Props> = ({ isAuthenticated, userName, userAvatar }) => {
|
||||
const NavBar: React.FC<Props> = ({ isAuthenticated, userName, allNames, userAvatar }) => {
|
||||
const isScreenSmall = !useMediaQuery(`(min-width:600px)`);
|
||||
const isSecure = isAuthenticated && !!userName;
|
||||
const gapSize = 10;
|
||||
@@ -45,6 +47,7 @@ const NavBar: React.FC<Props> = ({ isAuthenticated, userName, userAvatar }) => {
|
||||
isShowMenu={isSecure}
|
||||
userAvatar={userAvatar}
|
||||
userName={userName}
|
||||
allNames={allNames}
|
||||
/>
|
||||
<PublishMenu isDisplayed={isSecure} />
|
||||
</Box>
|
||||
|
||||
@@ -6,7 +6,7 @@ export const minPriceSuperDislike = 1;
|
||||
export const titleFormatter = /[\r\n]+/g;
|
||||
export const titleFormatterOnSave = /[\r\n/<>:"'\\*|?]+/g;
|
||||
|
||||
export const videoMaxSize = 2147; // Size in Megabytes (decimal)
|
||||
export const videoMaxSize = 2050; // Size in Megabytes (decimal)
|
||||
export const maxSize = videoMaxSize * 1024 * 1024;
|
||||
|
||||
export const fontSizeExSmall = "60%";
|
||||
|
||||
6
src/global.d.ts
vendored
6
src/global.d.ts
vendored
@@ -1,4 +1,9 @@
|
||||
// src/global.d.ts
|
||||
interface Location {
|
||||
service: string;
|
||||
name: string;
|
||||
identifier?: string;
|
||||
}
|
||||
interface QortalRequestOptions {
|
||||
action: string;
|
||||
name?: string;
|
||||
@@ -38,6 +43,7 @@ interface QortalRequestOptions {
|
||||
excludeBlocked?: boolean;
|
||||
exactMatchNames?: boolean;
|
||||
nameListFilter?: string[];
|
||||
location?: Location;
|
||||
}
|
||||
|
||||
declare function qortalRequest(options: QortalRequestOptions): Promise<any>;
|
||||
|
||||
36
src/hooks/useHandleNameData.tsx
Normal file
36
src/hooks/useHandleNameData.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { namesAtom } from '../state/global/names';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
//import { useGlobal } from 'qapp-core';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { RootState } from '../state/store';
|
||||
|
||||
export const useHandleNameData = () => {
|
||||
const setNames = useSetAtom(namesAtom);
|
||||
const user = useSelector((state: RootState) => state.auth.user);
|
||||
|
||||
const getMyNames = useCallback(async () => {
|
||||
if (!user?.address) return;
|
||||
try {
|
||||
const res = await qortalRequest({
|
||||
action: 'GET_ACCOUNT_NAMES',
|
||||
address: user.address,
|
||||
limit: 0,
|
||||
offset: 0,
|
||||
reverse: false,
|
||||
});
|
||||
setNames(res);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}, [user?.address, setNames]);
|
||||
|
||||
// Initial fetch + interval
|
||||
useEffect(() => {
|
||||
getMyNames();
|
||||
const interval = setInterval(getMyNames, 120_000); // every 2 minutes
|
||||
return () => clearInterval(interval);
|
||||
}, [getMyNames]);
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import ReactDOM from "react-dom/client";
|
||||
import App from "./App";
|
||||
import "./index.css";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
|
||||
interface CustomWindow extends Window {
|
||||
_qdnBase: string;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import { Box } from "@mui/material";
|
||||
import React from "react";
|
||||
import { Box, Tabs, Tab } from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { VideoListComponentLevel } from "../../Home/Components/VideoListComponentLevel.tsx";
|
||||
import { PlayListComponentLevel } from "../../Home/Components/PlayListComponentLevel.tsx";
|
||||
import { ChannelActions } from "../VideoContent/ChannelActions.tsx";
|
||||
import { StyledCardHeaderComment } from "../VideoContent/VideoContent-styles.tsx";
|
||||
import { HeaderContainer, ProfileContainer } from "./Profile-styles.tsx";
|
||||
|
||||
export const IndividualProfile = () => {
|
||||
const { name: channelName } = useParams();
|
||||
const [selectedTab, setSelectedTab] = useState(0);
|
||||
|
||||
const handleTabChange = (_event: React.SyntheticEvent, newValue: number) => {
|
||||
setSelectedTab(newValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<ProfileContainer>
|
||||
@@ -28,7 +34,19 @@ export const IndividualProfile = () => {
|
||||
</StyledCardHeaderComment>
|
||||
</Box>
|
||||
</HeaderContainer>
|
||||
<VideoListComponentLevel />
|
||||
|
||||
{/* Tabs Bar */}
|
||||
<Box sx={{ borderBottom: 1, borderColor: "divider", mb: 2 }}>
|
||||
<Tabs value={selectedTab} onChange={handleTabChange} aria-label="profile tabs">
|
||||
<Tab label="Videos" />
|
||||
<Tab label="Playlists" />
|
||||
</Tabs>
|
||||
</Box>
|
||||
|
||||
{/* Tab Content */}
|
||||
{selectedTab === 0 && <VideoListComponentLevel />}
|
||||
{selectedTab === 1 && <PlayListComponentLevel />}
|
||||
|
||||
</ProfileContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,13 +4,19 @@ import { SubscribeButton } from "../../../components/common/ContentButtons/Subsc
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import { ChannelParams } from "./ChannelName.tsx";
|
||||
import { StyledCardColComment } from "./VideoContent-styles.tsx";
|
||||
import { namesAtom } from '../../../state/global/names';
|
||||
import { useAtom } from "jotai";
|
||||
|
||||
export const ChannelButtons = ({ channelName, sx }: ChannelParams) => {
|
||||
const userName = useSelector((state: RootState) => state.auth.user?.name);
|
||||
|
||||
const [names] = useAtom(namesAtom);
|
||||
const isInNames = names.map((name) => name.name ).includes(channelName);
|
||||
|
||||
//const userName = useSelector((state: RootState) => state.auth.user?.name);
|
||||
// We need to put a change in here to get the currentUser name, not from the Redux state
|
||||
return (
|
||||
<StyledCardColComment sx={{ alignItems: "center", ...sx }}>
|
||||
{channelName !== userName && (
|
||||
{!isInNames && (
|
||||
<>
|
||||
<SubscribeButton subscriberName={channelName} />
|
||||
<FollowButton
|
||||
|
||||
183
src/pages/Home/Components/PlayListComponentLevel.tsx
Normal file
183
src/pages/Home/Components/PlayListComponentLevel.tsx
Normal file
@@ -0,0 +1,183 @@
|
||||
import { Box, useTheme } from "@mui/material";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useParams } from "react-router-dom";
|
||||
import LazyLoad from "../../../components/common/LazyLoad.tsx";
|
||||
import { useFetchVideos } from "../../../hooks/useFetchVideos.tsx";
|
||||
import { Video } from "../../../state/features/videoSlice.ts";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import { queue } from "../../../wrappers/GlobalWrapper.tsx";
|
||||
import { VideoManagerRow } from "./VideoList-styles.tsx";
|
||||
import { useSignal } from "@preact/signals-react";
|
||||
import { PlayListList } from "./PlayListList.tsx";
|
||||
|
||||
interface VideoListProps {
|
||||
mode?: string;
|
||||
}
|
||||
|
||||
export const PlayListComponentLevel = ({ mode }: VideoListProps) => {
|
||||
const { name: paramName } = useParams();
|
||||
|
||||
const firstFetch = useRef(false);
|
||||
const afterFetch = useRef(false);
|
||||
const hashMapVideos = useSelector(
|
||||
(state: RootState) => state.video.hashMapVideos
|
||||
);
|
||||
|
||||
const [videos, setVideos] = React.useState<Video[]>([]);
|
||||
const isLoading = useSignal(true);
|
||||
const { getVideo, checkAndUpdateVideo } = useFetchVideos();
|
||||
// For Pagination
|
||||
const pageRef = useRef(0);
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
const PAGE_SIZE = 20;
|
||||
|
||||
useEffect(() => {
|
||||
firstFetch.current = false;
|
||||
setVideos([]);
|
||||
pageRef.current = 0;
|
||||
setHasMore(true);
|
||||
}, [paramName]);
|
||||
|
||||
// 16-May-2025: Includes Pagination for PlayLists
|
||||
const getVideos = React.useCallback(async () => {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
|
||||
// Query to get a users playlists
|
||||
//'http://192.168.0.43:12391/arbitrary/resources/search?service=PLAYLIST&name=Ice&exactmatchnames=true&limit=20&reverse=true' \
|
||||
|
||||
const offset = pageRef.current * PAGE_SIZE;
|
||||
const url = `/arbitrary/resources/search?mode=ALL&service=PLAYLIST&name=${encodeURIComponent(paramName)}&exactmatchnames=true&limit=20&reverse=true&offset=${offset}`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const responseData = await response.json();
|
||||
|
||||
/* This is the search result for playlists by a specific use
|
||||
[
|
||||
{
|
||||
"name": "Ice",
|
||||
"service": "PLAYLIST",
|
||||
"identifier": "qtube_playlist_the-history-of-qortal_wZ9BtX",
|
||||
"size": 11824,
|
||||
"created": 1747327830726
|
||||
}
|
||||
]
|
||||
*/
|
||||
|
||||
/* This is the result when getting the JSON infomration about a PLAYLIST
|
||||
{ "title": "The History of Qortal",
|
||||
"version": 1,
|
||||
"description": "A timeline of Qortal according to Ice",
|
||||
"htmlDescription": "<p>A timeline of Qortal according to Ice</p>",
|
||||
"image": "data:image/webp;base64,UklGRugqAABXRUJQVlA4WAoAAAAgAAAASwIAFQAGSJuX2Ws5....YY9umRdt3kC5BWqZoIno3+DIAAA",
|
||||
"videos": [
|
||||
{ "identifier": "qtube_vid_q-tube-may-15_32Mf4d_metadata", "name": "Ice", "service": "DOCUMENT", "code": "GLzeC**<p>New In Qortal? 15-May 2025</p><p>Come see new enhancements specific to Q-Tube</p><p><br></p>" },
|
||||
{ "identifier": "qtube_vid_11-why-crowetic-dedicated-to-q_nmHmNo_metadata", "name": "QortalNuggets", "service": "DOCUMENT", "code": "Q3fbb**<p>Ernest asks WHY. Jason answers.</p>" },
|
||||
{ "identifier": "qtube_vid_qortal-conscious-soul-festival_55y2u0_metadata", "name": "ThanksToZen", "service": "DOCUMENT", "code": "Xvwjg**Footage from day 1 of 2" },
|
||||
{ "identifier": "qtube_vid_qortal-at-web3-amsterdamvideo7_1lCJT4_metadata", "name": "igorcoin", "service": "DOCUMENT", "code": "IbRmX**qortal-at-web3-amsterdam_video_720p_eesti keelsete subtiitritega" }
|
||||
],
|
||||
"commentsId": "qtube_playlist__cm_wZ9BtX", "category": 26, "subcategory": "" }
|
||||
*/
|
||||
|
||||
const structureData = responseData.map((video: any): Video => {
|
||||
return {
|
||||
title: video?.metadata?.title,
|
||||
category: video?.metadata?.category,
|
||||
categoryName: video?.metadata?.categoryName,
|
||||
tags: video?.metadata?.tags || [],
|
||||
description: video?.metadata?.description,
|
||||
created: video?.created,
|
||||
updated: video?.updated,
|
||||
service: `PLAYLIST`,
|
||||
user: video.name,
|
||||
videoImage: "",
|
||||
id: video.identifier,
|
||||
};
|
||||
});
|
||||
|
||||
// Pre-Pagination
|
||||
//setVideos((prev) => {
|
||||
// const copiedVideos: Video[] = [...prev];
|
||||
// structureData.forEach((video: Video) => {
|
||||
// const index = prev.findIndex((p) => p.id === video.id);
|
||||
// if (index !== -1) {
|
||||
// copiedVideos[index] = video;
|
||||
// } else {
|
||||
// copiedVideos.push(video);
|
||||
// }
|
||||
// });
|
||||
// return copiedVideos;
|
||||
//});
|
||||
|
||||
setVideos(prev => {
|
||||
const updatedVideos = [...prev];
|
||||
|
||||
structureData.forEach(video => {
|
||||
const exists = updatedVideos.some(v => v.id === video.id);
|
||||
if (!exists) {
|
||||
updatedVideos.push(video);
|
||||
}
|
||||
});
|
||||
return updatedVideos;
|
||||
});
|
||||
|
||||
// If fewer than PAGE_SIZE results, we've reached the end
|
||||
if (structureData.length < PAGE_SIZE) {
|
||||
setHasMore(false);
|
||||
} else {
|
||||
pageRef.current += 1;
|
||||
}
|
||||
|
||||
for (const content of structureData) {
|
||||
if (content.user && content.id) {
|
||||
const res = checkAndUpdateVideo(content);
|
||||
if (res) {
|
||||
queue.push(() => getVideo(content.user, content.id, content));
|
||||
}
|
||||
}
|
||||
}
|
||||
isLoading.value = false;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
isLoading.value = false;
|
||||
}
|
||||
}, [checkAndUpdateVideo, getVideo, hashMapVideos, paramName]);
|
||||
|
||||
const getVideosHandlerMount = React.useCallback(async () => {
|
||||
if (firstFetch.current) return;
|
||||
firstFetch.current = true;
|
||||
await getVideos();
|
||||
afterFetch.current = true;
|
||||
}, [getVideos]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!firstFetch.current) {
|
||||
getVideosHandlerMount();
|
||||
}
|
||||
}, [getVideosHandlerMount]);
|
||||
|
||||
return (
|
||||
<VideoManagerRow>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<PlayListList videos={videos} />
|
||||
<LazyLoad
|
||||
onLoadMore={hasMore ? getVideos : undefined}
|
||||
isLoading={isLoading.value}
|
||||
/>
|
||||
</Box>
|
||||
</VideoManagerRow>
|
||||
);
|
||||
};
|
||||
311
src/pages/Home/Components/PlayListList.tsx
Normal file
311
src/pages/Home/Components/PlayListList.tsx
Normal file
@@ -0,0 +1,311 @@
|
||||
import BlockIcon from "@mui/icons-material/Block";
|
||||
import EditIcon from "@mui/icons-material/Edit";
|
||||
import { Avatar, Box, Tooltip, Typography, useTheme } from "@mui/material";
|
||||
import { useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { PlaylistSVG } from "../../../assets/svgs/PlaylistSVG.tsx";
|
||||
import ResponsiveImage from "../../../components/ResponsiveImage.tsx";
|
||||
import { fontSizeSmall, minDuration } from "../../../constants/Misc.ts";
|
||||
import {
|
||||
blockUser,
|
||||
setEditPlaylist,
|
||||
setEditVideo,
|
||||
Video,
|
||||
} from "../../../state/features/videoSlice.ts";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import { formatTime } from "../../../utils/numberFunctions.ts";
|
||||
import { formatDate } from "../../../utils/time.ts";
|
||||
import { VideoCardImageContainer } from "./VideoCardImageContainer.tsx";
|
||||
import {
|
||||
BlockIconContainer,
|
||||
BottomParent,
|
||||
IconsBox,
|
||||
NameContainer,
|
||||
VideoCard,
|
||||
VideoCardCol,
|
||||
VideoCardContainer,
|
||||
VideoCardName,
|
||||
VideoCardTitle,
|
||||
VideoUploadDate,
|
||||
} from "./VideoList-styles.tsx";
|
||||
import ContextMenuResource from '../../../components/common/ContextMenu/ContextMenuResource'
|
||||
|
||||
interface VideoListProps {
|
||||
videos: Video[];
|
||||
}
|
||||
export const PlayListList = ({ videos }: VideoListProps) => {
|
||||
const [showIcons, setShowIcons] = useState(null);
|
||||
|
||||
const hashMapVideos = useSelector(
|
||||
(state: RootState) => state.video.hashMapVideos
|
||||
);
|
||||
|
||||
// ToDo: This needs to be updated for names
|
||||
const username = useSelector((state: RootState) => state.auth?.user?.name);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const theme = useTheme();
|
||||
|
||||
const blockUserFunc = async (user: string) => {
|
||||
if (user === "Q-Tube") return;
|
||||
|
||||
try {
|
||||
const response = await qortalRequest({
|
||||
action: "ADD_LIST_ITEMS",
|
||||
list_name: "blockedNames",
|
||||
items: [user],
|
||||
});
|
||||
|
||||
if (response === true) {
|
||||
dispatch(blockUser(user));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<VideoCardContainer>
|
||||
{videos.map((video: any) => {
|
||||
const fullId = video ? `${video.id}-${video.user}` : undefined;
|
||||
const existingVideo = hashMapVideos[fullId];
|
||||
let hasHash = false;
|
||||
let videoObj = video;
|
||||
if (existingVideo) {
|
||||
videoObj = existingVideo;
|
||||
hasHash = true;
|
||||
}
|
||||
// nb. this prevents showing metadata for a video which
|
||||
// belongs to a different user
|
||||
if (
|
||||
videoObj?.user &&
|
||||
videoObj?.videoReference?.name &&
|
||||
videoObj.user != videoObj.videoReference.name
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (hasHash && !videoObj?.videoImage && !videoObj?.image) {
|
||||
return null;
|
||||
}
|
||||
const isPlaylist = videoObj?.service === "PLAYLIST";
|
||||
|
||||
if (isPlaylist) {
|
||||
return (
|
||||
<VideoCardCol
|
||||
key={videoObj.id}
|
||||
onMouseEnter={() => setShowIcons(videoObj.id)}
|
||||
onMouseLeave={() => setShowIcons(null)}
|
||||
>
|
||||
<IconsBox
|
||||
sx={{
|
||||
opacity: showIcons === videoObj.id ? 1 : 0,
|
||||
zIndex: 2,
|
||||
}}
|
||||
>
|
||||
{videoObj?.user === username && (
|
||||
<Tooltip title="Edit playlist" placement="top">
|
||||
<BlockIconContainer>
|
||||
<EditIcon
|
||||
onClick={() => {
|
||||
dispatch(setEditPlaylist(videoObj));
|
||||
}}
|
||||
/>
|
||||
</BlockIconContainer>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{videoObj?.user !== username && (
|
||||
<Tooltip title="Block user content" placement="top">
|
||||
<BlockIconContainer>
|
||||
<BlockIcon
|
||||
onClick={() => {
|
||||
blockUserFunc(videoObj?.user);
|
||||
}}
|
||||
/>
|
||||
</BlockIconContainer>
|
||||
</Tooltip>
|
||||
)}
|
||||
</IconsBox>
|
||||
|
||||
<VideoCard
|
||||
sx={{
|
||||
cursor: !hasHash && "default",
|
||||
}}
|
||||
onClick={() => {
|
||||
if (!hasHash) return;
|
||||
navigate(`/playlist/${videoObj?.user}/${videoObj?.id}`);
|
||||
}}
|
||||
>
|
||||
<ResponsiveImage
|
||||
src={videoObj?.image}
|
||||
width={266}
|
||||
height={150}
|
||||
style={{
|
||||
maxHeight: "50%",
|
||||
}}
|
||||
/>
|
||||
<VideoCardTitle>{videoObj?.title}</VideoCardTitle>
|
||||
<BottomParent>
|
||||
<NameContainer
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
navigate(`/channel/${videoObj?.user}`);
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
sx={{ height: 24, width: 24 }}
|
||||
src={`/arbitrary/THUMBNAIL/${videoObj?.user}/qortal_avatar`}
|
||||
alt={`${videoObj?.user}'s avatar`}
|
||||
/>
|
||||
<VideoCardName
|
||||
sx={{
|
||||
":hover": {
|
||||
textDecoration: "underline",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{videoObj?.user}
|
||||
</VideoCardName>
|
||||
|
||||
{videoObj?.created && (
|
||||
<VideoUploadDate>
|
||||
{formatDate(videoObj.created)}
|
||||
</VideoUploadDate>
|
||||
)}
|
||||
</NameContainer>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
position: "absolute",
|
||||
bottom: "5px",
|
||||
right: "5px",
|
||||
}}
|
||||
>
|
||||
<PlaylistSVG
|
||||
color={theme.palette.text.primary}
|
||||
height="36px"
|
||||
width="36px"
|
||||
/>
|
||||
</Box>
|
||||
</BottomParent>
|
||||
</VideoCard>
|
||||
</VideoCardCol>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ContextMenuResource
|
||||
name={video.user}
|
||||
service="VIDEO"
|
||||
identifier={video.id}
|
||||
link={`qortal://APP/Q-Tube/video/${encodeURIComponent(video.user)}/${encodeURIComponent(video.id)}`}
|
||||
>
|
||||
<VideoCardCol
|
||||
key={videoObj.id}
|
||||
onMouseEnter={() => setShowIcons(videoObj.id)}
|
||||
onMouseLeave={() => setShowIcons(null)}
|
||||
>
|
||||
<IconsBox
|
||||
sx={{
|
||||
opacity: showIcons === videoObj.id ? 1 : 0,
|
||||
zIndex: 2,
|
||||
}}
|
||||
>
|
||||
{videoObj?.user === username && (
|
||||
<Tooltip title="Edit video properties" placement="top">
|
||||
<BlockIconContainer>
|
||||
<EditIcon
|
||||
onClick={() => {
|
||||
dispatch(setEditVideo(videoObj));
|
||||
}}
|
||||
/>
|
||||
</BlockIconContainer>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{videoObj?.user !== username && (
|
||||
<Tooltip title="Block user content" placement="top">
|
||||
<BlockIconContainer>
|
||||
<BlockIcon
|
||||
onClick={() => {
|
||||
blockUserFunc(videoObj?.user);
|
||||
}}
|
||||
/>
|
||||
</BlockIconContainer>
|
||||
</Tooltip>
|
||||
)}
|
||||
</IconsBox>
|
||||
<VideoCard
|
||||
onClick={() => {
|
||||
navigate(`/video/${videoObj?.user}/${videoObj?.id}`);
|
||||
}}
|
||||
>
|
||||
{videoObj?.duration > minDuration && (
|
||||
<Box
|
||||
position="absolute"
|
||||
right={0}
|
||||
bottom={0}
|
||||
bgcolor="#202020"
|
||||
zIndex={999}
|
||||
>
|
||||
<Typography color="white">
|
||||
{formatTime(videoObj.duration)}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
<VideoCardImageContainer
|
||||
width={266}
|
||||
height={150}
|
||||
videoImage={videoObj.videoImage}
|
||||
frameImages={videoObj?.extracts || []}
|
||||
/>
|
||||
<Tooltip
|
||||
title={videoObj.title}
|
||||
placement="top"
|
||||
slotProps={{ tooltip: { sx: { fontSize: fontSizeSmall } } }}
|
||||
>
|
||||
<VideoCardTitle>{videoObj.title}</VideoCardTitle>
|
||||
</Tooltip>
|
||||
<BottomParent>
|
||||
<NameContainer
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
navigate(`/channel/${videoObj?.user}`);
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
sx={{ height: 24, width: 24 }}
|
||||
src={`/arbitrary/THUMBNAIL/${videoObj?.user}/qortal_avatar`}
|
||||
alt={`${videoObj?.user}'s avatar`}
|
||||
/>
|
||||
<VideoCardName
|
||||
sx={{
|
||||
":hover": {
|
||||
textDecoration: "underline",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{videoObj?.user}
|
||||
</VideoCardName>
|
||||
</NameContainer>
|
||||
{videoObj?.created && (
|
||||
<Box sx={{ flexDirection: "row", width: "100%" }}>
|
||||
<VideoUploadDate sx={{ display: "inline" }}>
|
||||
{formatDate(videoObj.created)}
|
||||
</VideoUploadDate>
|
||||
</Box>
|
||||
)}
|
||||
</BottomParent>
|
||||
</VideoCard>
|
||||
</VideoCardCol>
|
||||
</ContextMenuResource>
|
||||
);
|
||||
})}
|
||||
</VideoCardContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlayListList;
|
||||
@@ -68,6 +68,7 @@ export const VideoList = ({ videos }: VideoListProps) => {
|
||||
return (
|
||||
<VideoCardContainer>
|
||||
{videos.map((video: any) => {
|
||||
//key = video.id;
|
||||
const fullId = video ? `${video.id}-${video.user}` : undefined;
|
||||
const existingVideo = hashMapVideos[fullId];
|
||||
let hasHash = false;
|
||||
|
||||
@@ -28,11 +28,23 @@ export const VideoListComponentLevel = ({ mode }: VideoListProps) => {
|
||||
const [videos, setVideos] = React.useState<Video[]>([]);
|
||||
const isLoading = useSignal(true);
|
||||
const { getVideo, checkAndUpdateVideo } = useFetchVideos();
|
||||
// For Pagination
|
||||
const pageRef = useRef(0);
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
const PAGE_SIZE = 20;
|
||||
|
||||
useEffect(() => {
|
||||
firstFetch.current = false;
|
||||
setVideos([]);
|
||||
pageRef.current = 0;
|
||||
setHasMore(true);
|
||||
}, [paramName]);
|
||||
|
||||
const getVideos = React.useCallback(async () => {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
const offset = videos.length;
|
||||
const offset = pageRef.current * PAGE_SIZE;
|
||||
console.log('getVideos ParamName:', paramName);
|
||||
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QTUBE_VIDEO_BASE}&limit=20&includemetadata=false&reverse=true&excludeblocked=true&name=${paramName}&exactmatchnames=true&offset=${offset}`;
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
@@ -57,16 +69,24 @@ export const VideoListComponentLevel = ({ mode }: VideoListProps) => {
|
||||
};
|
||||
});
|
||||
|
||||
const copiedVideos: Video[] = [...videos];
|
||||
structureData.forEach((video: Video) => {
|
||||
const index = videos.findIndex(p => p.id === video.id);
|
||||
if (index !== -1) {
|
||||
copiedVideos[index] = video;
|
||||
} else {
|
||||
copiedVideos.push(video);
|
||||
}
|
||||
setVideos(prev => {
|
||||
const updatedVideos = [...prev];
|
||||
|
||||
structureData.forEach(video => {
|
||||
const exists = updatedVideos.some(v => v.id === video.id);
|
||||
if (!exists) {
|
||||
updatedVideos.push(video);
|
||||
}
|
||||
});
|
||||
return updatedVideos;
|
||||
});
|
||||
setVideos(copiedVideos);
|
||||
|
||||
// If fewer than PAGE_SIZE results, we've reached the end
|
||||
if (structureData.length < PAGE_SIZE) {
|
||||
setHasMore(false);
|
||||
} else {
|
||||
pageRef.current += 1;
|
||||
}
|
||||
|
||||
for (const content of structureData) {
|
||||
if (content.user && content.id) {
|
||||
@@ -81,20 +101,20 @@ export const VideoListComponentLevel = ({ mode }: VideoListProps) => {
|
||||
console.log(error);
|
||||
isLoading.value = false;
|
||||
}
|
||||
}, [videos, hashMapVideos]);
|
||||
|
||||
const getVideosHandlerMount = React.useCallback(async () => {
|
||||
if (firstFetch.current) return;
|
||||
firstFetch.current = true;
|
||||
await getVideos();
|
||||
afterFetch.current = true;
|
||||
}, [getVideos]);
|
||||
}, [checkAndUpdateVideo, getVideo, hashMapVideos, paramName]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchVideos = async () => {
|
||||
firstFetch.current = true;
|
||||
console.log("Running useEffect: " + paramName);
|
||||
await getVideos();
|
||||
afterFetch.current = true;
|
||||
};
|
||||
|
||||
if (!firstFetch.current) {
|
||||
getVideosHandlerMount();
|
||||
fetchVideos();
|
||||
}
|
||||
}, [getVideosHandlerMount]);
|
||||
}, [paramName, getVideos]);
|
||||
|
||||
return (
|
||||
<VideoManagerRow>
|
||||
@@ -107,7 +127,10 @@ export const VideoListComponentLevel = ({ mode }: VideoListProps) => {
|
||||
}}
|
||||
>
|
||||
<VideoList videos={videos} />
|
||||
<LazyLoad onLoadMore={getVideos} isLoading={isLoading.value}></LazyLoad>
|
||||
<LazyLoad
|
||||
onLoadMore={hasMore ? getVideos : undefined}
|
||||
isLoading={isLoading.value}
|
||||
/>
|
||||
</Box>
|
||||
</VideoManagerRow>
|
||||
);
|
||||
|
||||
@@ -19,9 +19,14 @@ export const authSlice = createSlice({
|
||||
addUser: (state, action) => {
|
||||
state.user = action.payload;
|
||||
},
|
||||
setName: (state, action) => {
|
||||
state.user.name = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { addUser } = authSlice.actions;
|
||||
export const { setName } = authSlice.actions;
|
||||
|
||||
|
||||
export default authSlice.reducer;
|
||||
8
src/state/global/names.ts
Normal file
8
src/state/global/names.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { atom } from 'jotai';
|
||||
export interface Name {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type Names = Name[];
|
||||
|
||||
export const namesAtom = atom<Names>([]);
|
||||
@@ -41,6 +41,14 @@ export const getAccountNames = async (
|
||||
return emptyNamesFilled.length > 0 ? emptyNamesFilled : [namelessAddress];
|
||||
};
|
||||
|
||||
export const getPrimaryAccountName = async (address: string) => {
|
||||
const primaryName = (await qortalRequest({
|
||||
action: "GET_PRIMARY_NAME",
|
||||
address,
|
||||
})) as string | null;
|
||||
return primaryName ?? "";
|
||||
}
|
||||
|
||||
export const searchTransactions = async (params: TransactionSearchParams) => {
|
||||
return (await qortalRequest({
|
||||
action: "SEARCH_TRANSACTIONS",
|
||||
|
||||
@@ -29,6 +29,10 @@ import ConsentModal from "../components/common/ConsentModal";
|
||||
import { useFetchSuperLikes } from "../hooks/useFetchSuperLikes";
|
||||
import { SUPER_LIKE_BASE } from "../constants/Identifiers.ts";
|
||||
import { minPriceSuperLike } from "../constants/Misc.ts";
|
||||
import { useHandleNameData } from './../hooks/useHandleNameData.tsx';
|
||||
import { namesAtom } from './../state/global/names';
|
||||
import { useAtom } from 'jotai';
|
||||
import { getPrimaryAccountName } from "../utils/qortalRequestFunctions.ts";
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
@@ -47,24 +51,26 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
||||
const user = useSelector((state: RootState) => state.auth.user);
|
||||
const { addSuperlikeRawDataGetToList } = useFetchSuperLikes();
|
||||
const interval = useRef<any>(null);
|
||||
|
||||
useHandleNameData();
|
||||
|
||||
const videoPlaying = useSelector(
|
||||
(state: RootState) => state.global.videoPlaying
|
||||
);
|
||||
|
||||
const username = useMemo(() => {
|
||||
if (!user?.name) return "";
|
||||
|
||||
return user.name;
|
||||
}, [user]);
|
||||
|
||||
const [names] = useAtom(namesAtom);
|
||||
|
||||
const getAvatar = React.useCallback(
|
||||
async (author: string) => {
|
||||
try {
|
||||
const url = await qortalRequest({
|
||||
action: "GET_QDN_RESOURCE_URL",
|
||||
name: author,
|
||||
service: "THUMBNAIL",
|
||||
identifier: "qortal_avatar",
|
||||
});
|
||||
|
||||
const url = `/arbitrary/THUMBNAIL/${author}/qortal_avatar`;
|
||||
|
||||
if (url) {
|
||||
setUserAvatar(url);
|
||||
dispatch(
|
||||
@@ -89,27 +95,13 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
||||
|
||||
const { isLoadingGlobal } = useSelector((state: RootState) => state.global);
|
||||
|
||||
async function getNameInfo(address: string) {
|
||||
const response = await qortalRequest({
|
||||
action: "GET_ACCOUNT_NAMES",
|
||||
address: address,
|
||||
});
|
||||
const nameData = response;
|
||||
|
||||
if (nameData?.length > 0) {
|
||||
return nameData[0].name;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
const askForAccountInformation = React.useCallback(async () => {
|
||||
try {
|
||||
const account = await qortalRequest({
|
||||
action: "GET_USER_ACCOUNT",
|
||||
});
|
||||
|
||||
const name = await getNameInfo(account.address);
|
||||
const name = await getPrimaryAccountName(account.address);
|
||||
dispatch(addUser({ ...account, name }));
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -223,6 +215,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
||||
setTheme={(val: string) => setTheme(val)}
|
||||
isAuthenticated={!!user?.name}
|
||||
userName={user?.name || ""}
|
||||
allNames={names}
|
||||
userAvatar={userAvatar}
|
||||
authenticate={askForAccountInformation}
|
||||
/>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 3000
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 3000
|
||||
},
|
||||
base: "",
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user