mirror of
https://git.vectorsigma.ru/public/tubearchivist.git
synced 2026-08-05 00:29:40 +00:00
split between userConfig and userAccount
This commit is contained in:
424
frontend/package-lock.json
generated
424
frontend/package-lock.json
generated
@@ -28,7 +28,33 @@
|
|||||||
"prettier": "3.4.2",
|
"prettier": "3.4.2",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"typescript-eslint": "^8.21.0",
|
"typescript-eslint": "^8.21.0",
|
||||||
"vite": "^6.0.11"
|
"vite": "^6.0.11",
|
||||||
|
"vite-plugin-checker": "^0.8.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,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-validator-identifier": "^7.25.9",
|
||||||
|
"js-tokens": "^4.0.0",
|
||||||
|
"picocolors": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-validator-identifier": {
|
||||||
|
"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,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
@@ -1516,6 +1542,32 @@
|
|||||||
"url": "https://github.com/sponsors/epoberezkin"
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ansi-escapes": {
|
||||||
|
"version": "4.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
|
||||||
|
"integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"type-fest": "^0.21.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ansi-styles": {
|
"node_modules/ansi-styles": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
@@ -1531,6 +1583,20 @@
|
|||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/anymatch": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"normalize-path": "^3.0.0",
|
||||||
|
"picomatch": "^2.0.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/argparse": {
|
"node_modules/argparse": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
@@ -1544,6 +1610,19 @@
|
|||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/binary-extensions": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||||
@@ -1593,6 +1672,44 @@
|
|||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chokidar": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"anymatch": "~3.1.2",
|
||||||
|
"braces": "~3.0.2",
|
||||||
|
"glob-parent": "~5.1.2",
|
||||||
|
"is-binary-path": "~2.1.0",
|
||||||
|
"is-glob": "~4.0.1",
|
||||||
|
"normalize-path": "~3.0.0",
|
||||||
|
"readdirp": "~3.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8.10.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chokidar/node_modules/glob-parent": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"is-glob": "^4.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
@@ -1611,6 +1728,16 @@
|
|||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/commander": {
|
||||||
|
"version": "8.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
|
||||||
|
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/concat-map": {
|
"node_modules/concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
@@ -2090,6 +2217,21 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/fs-extra": {
|
||||||
|
"version": "11.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
||||||
|
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^6.0.1",
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
@@ -2130,6 +2272,13 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/graceful-fs": {
|
||||||
|
"version": "4.2.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/graphemer": {
|
"node_modules/graphemer": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
||||||
@@ -2180,6 +2329,19 @@
|
|||||||
"node": ">=0.8.19"
|
"node": ">=0.8.19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-binary-path": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"binary-extensions": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-extglob": {
|
"node_modules/is-extglob": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
@@ -2218,6 +2380,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/js-tokens": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||||
@@ -2251,6 +2420,19 @@
|
|||||||
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
|
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/jsonfile": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/keyv": {
|
"node_modules/keyv": {
|
||||||
"version": "4.5.4",
|
"version": "4.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||||
@@ -2366,6 +2548,29 @@
|
|||||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/normalize-path": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/npm-run-path": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"path-key": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/optionator": {
|
"node_modules/optionator": {
|
||||||
"version": "0.9.4",
|
"version": "0.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||||
@@ -2611,6 +2816,19 @@
|
|||||||
"react-dom": ">=18"
|
"react-dom": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/readdirp": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"picomatch": "^2.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/resolve-from": {
|
"node_modules/resolve-from": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||||
@@ -2753,6 +2971,19 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/strip-ansi": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/strip-json-comments": {
|
"node_modules/strip-json-comments": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
|
||||||
@@ -2778,6 +3009,13 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-invariant": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/to-regex-range": {
|
"node_modules/to-regex-range": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
@@ -2822,6 +3060,19 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/type-fest": {
|
||||||
|
"version": "0.21.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
|
||||||
|
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "(MIT OR CC0-1.0)",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.7.3",
|
"version": "5.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
|
||||||
@@ -2859,6 +3110,16 @@
|
|||||||
"typescript": ">=4.8.4 <5.8.0"
|
"typescript": ">=4.8.4 <5.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/universalify": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/uri-js": {
|
"node_modules/uri-js": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||||
@@ -2941,6 +3202,167 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vite-plugin-checker": {
|
||||||
|
"version": "0.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vite-plugin-checker/-/vite-plugin-checker-0.8.0.tgz",
|
||||||
|
"integrity": "sha512-UA5uzOGm97UvZRTdZHiQVYFnd86AVn8EVaD4L3PoVzxH+IZSfaAw14WGFwX9QS23UW3lV/5bVKZn6l0w+q9P0g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/code-frame": "^7.12.13",
|
||||||
|
"ansi-escapes": "^4.3.0",
|
||||||
|
"chalk": "^4.1.1",
|
||||||
|
"chokidar": "^3.5.1",
|
||||||
|
"commander": "^8.0.0",
|
||||||
|
"fast-glob": "^3.2.7",
|
||||||
|
"fs-extra": "^11.1.0",
|
||||||
|
"npm-run-path": "^4.0.1",
|
||||||
|
"strip-ansi": "^6.0.0",
|
||||||
|
"tiny-invariant": "^1.1.0",
|
||||||
|
"vscode-languageclient": "^7.0.0",
|
||||||
|
"vscode-languageserver": "^7.0.0",
|
||||||
|
"vscode-languageserver-textdocument": "^1.0.1",
|
||||||
|
"vscode-uri": "^3.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.16"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@biomejs/biome": ">=1.7",
|
||||||
|
"eslint": ">=7",
|
||||||
|
"meow": "^9.0.0",
|
||||||
|
"optionator": "^0.9.1",
|
||||||
|
"stylelint": ">=13",
|
||||||
|
"typescript": "*",
|
||||||
|
"vite": ">=2.0.0",
|
||||||
|
"vls": "*",
|
||||||
|
"vti": "*",
|
||||||
|
"vue-tsc": "~2.1.6"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@biomejs/biome": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"eslint": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"meow": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"optionator": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"stylelint": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"vls": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"vti": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"vue-tsc": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vscode-jsonrpc": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0 || >=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vscode-languageclient": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"semver": "^7.3.4",
|
||||||
|
"vscode-languageserver-protocol": "3.16.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"vscode": "^1.52.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vscode-languageclient/node_modules/brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vscode-languageclient/node_modules/minimatch": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vscode-languageserver": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"vscode-languageserver-protocol": "3.16.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"installServerIntoExtension": "bin/installServerIntoExtension"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vscode-languageserver-protocol": {
|
||||||
|
"version": "3.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz",
|
||||||
|
"integrity": "sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"vscode-jsonrpc": "6.0.0",
|
||||||
|
"vscode-languageserver-types": "3.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vscode-languageserver-textdocument": {
|
||||||
|
"version": "1.0.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz",
|
||||||
|
"integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/vscode-languageserver-types": {
|
||||||
|
"version": "3.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz",
|
||||||
|
"integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/vscode-uri": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"prettier": "3.4.2",
|
"prettier": "3.4.2",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"typescript-eslint": "^8.21.0",
|
"typescript-eslint": "^8.21.0",
|
||||||
"vite": "^6.0.11"
|
"vite": "^6.0.11",
|
||||||
|
"vite-plugin-checker": "^0.8.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,6 @@
|
|||||||
import { SortByType, SortOrderType, ViewLayoutType } from '../../pages/Home';
|
import { SortByType, SortOrderType, ViewLayoutType } from '../../pages/Home';
|
||||||
import APIClient from '../../functions/APIClient';
|
import APIClient from '../../functions/APIClient';
|
||||||
|
|
||||||
export type UserMeType = {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
is_superuser: boolean;
|
|
||||||
is_staff: boolean;
|
|
||||||
groups: [];
|
|
||||||
user_permissions: [];
|
|
||||||
last_login: string;
|
|
||||||
config: UserConfigType;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ColourVariants = 'dark.css' | 'light.css' | 'matrix.css' | 'midnight.css';
|
export type ColourVariants = 'dark.css' | 'light.css' | 'matrix.css' | 'midnight.css';
|
||||||
|
|
||||||
export type UserConfigType = {
|
export type UserConfigType = {
|
||||||
@@ -33,7 +22,7 @@ export type UserConfigType = {
|
|||||||
const updateUserConfig = async (config: Partial<UserConfigType>): Promise<UserConfigType> => {
|
const updateUserConfig = async (config: Partial<UserConfigType>): Promise<UserConfigType> => {
|
||||||
return APIClient('/api/user/me/', {
|
return APIClient('/api/user/me/', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: { config: config },
|
body: config,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
8
frontend/src/api/loader/loadUserAccount.ts
Normal file
8
frontend/src/api/loader/loadUserAccount.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import APIClient from '../../functions/APIClient';
|
||||||
|
import { UserAccountType } from '../../pages/Base';
|
||||||
|
|
||||||
|
const loadUserAccount = async (): Promise<UserAccountType> => {
|
||||||
|
return APIClient('/api/user/account/');
|
||||||
|
};
|
||||||
|
|
||||||
|
export default loadUserAccount;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { UserMeType } from '../actions/updateUserConfig';
|
import { UserConfigType } from '../actions/updateUserConfig';
|
||||||
import APIClient from '../../functions/APIClient';
|
import APIClient from '../../functions/APIClient';
|
||||||
|
|
||||||
const loadUserMeConfig = async (): Promise<UserMeType> => {
|
const loadUserMeConfig = async (): Promise<UserConfigType> => {
|
||||||
return APIClient('/api/user/me/');
|
return APIClient('/api/user/me/');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ type ChannelListProps = {
|
|||||||
|
|
||||||
const ChannelList = ({ channelList, refreshChannelList }: ChannelListProps) => {
|
const ChannelList = ({ channelList, refreshChannelList }: ChannelListProps) => {
|
||||||
const { userConfig } = useUserConfigStore();
|
const { userConfig } = useUserConfigStore();
|
||||||
const viewLayout = userConfig.config.view_style_channel;
|
const viewLayout = userConfig.view_style_channel;
|
||||||
|
|
||||||
if (!channelList || channelList.length === 0) {
|
if (!channelList || channelList.length === 0) {
|
||||||
return <p>No channels found.</p>;
|
return <p>No channels found.</p>;
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ type DownloadListItemProps = {
|
|||||||
|
|
||||||
const DownloadListItem = ({ download, setRefresh }: DownloadListItemProps) => {
|
const DownloadListItem = ({ download, setRefresh }: DownloadListItemProps) => {
|
||||||
const { userConfig } = useUserConfigStore();
|
const { userConfig } = useUserConfigStore();
|
||||||
const view = userConfig.config.view_style_downloads;
|
const view = userConfig.view_style_downloads;
|
||||||
const showIgnored = userConfig.config.show_ignored_only;
|
const showIgnored = userConfig.show_ignored_only;
|
||||||
|
|
||||||
const [hideDownload, setHideDownload] = useState(false);
|
const [hideDownload, setHideDownload] = useState(false);
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ const EmbeddableVideoPlayer = ({ videoId }: EmbeddableVideoPlayerProps) => {
|
|||||||
setRefresh(false);
|
setRefresh(false);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [videoId, refresh]);
|
}, [videoId, refresh]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import iconListView from '/img/icon-listview.svg';
|
|||||||
import { SortByType, SortOrderType } from '../pages/Home';
|
import { SortByType, SortOrderType } from '../pages/Home';
|
||||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
import { useUserConfigStore } from '../stores/UserConfigStore';
|
||||||
import { ViewStyles } from '../configuration/constants/ViewStyle';
|
import { ViewStyles } from '../configuration/constants/ViewStyle';
|
||||||
|
import updateUserConfig, { UserConfigType } from '../api/actions/updateUserConfig';
|
||||||
|
|
||||||
type FilterbarProps = {
|
type FilterbarProps = {
|
||||||
hideToggleText: string;
|
hideToggleText: string;
|
||||||
@@ -15,9 +16,14 @@ type FilterbarProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Filterbar = ({ hideToggleText, viewStyleName, showSort = true }: FilterbarProps) => {
|
const Filterbar = ({ hideToggleText, viewStyleName, showSort = true }: FilterbarProps) => {
|
||||||
const { userConfig, setPartialConfig } = useUserConfigStore();
|
const { userConfig, setUserConfig } = useUserConfigStore();
|
||||||
const [showHidden, setShowHidden] = useState(false);
|
const [showHidden, setShowHidden] = useState(false);
|
||||||
const isGridView = userConfig.config.view_style_home === ViewStyles.grid;
|
const isGridView = userConfig.view_style_home === ViewStyles.grid;
|
||||||
|
|
||||||
|
const handleUserConfigUpdate = async (config: Partial<UserConfigType>) => {
|
||||||
|
const updatedUserConfig = await updateUserConfig(config);
|
||||||
|
setUserConfig(updatedUserConfig);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="view-controls three">
|
<div className="view-controls three">
|
||||||
@@ -27,13 +33,13 @@ const Filterbar = ({ hideToggleText, viewStyleName, showSort = true }: Filterbar
|
|||||||
<input
|
<input
|
||||||
id="hide_watched"
|
id="hide_watched"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={userConfig.config.hide_watched}
|
checked={userConfig.hide_watched}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
setPartialConfig({ hide_watched: !userConfig.config.hide_watched });
|
handleUserConfigUpdate({ hide_watched: !userConfig.hide_watched });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{userConfig.config.hide_watched ? (
|
{userConfig.hide_watched ? (
|
||||||
<label htmlFor="" className="onbtn">
|
<label htmlFor="" className="onbtn">
|
||||||
On
|
On
|
||||||
</label>
|
</label>
|
||||||
@@ -52,9 +58,9 @@ const Filterbar = ({ hideToggleText, viewStyleName, showSort = true }: Filterbar
|
|||||||
<select
|
<select
|
||||||
name="sort_by"
|
name="sort_by"
|
||||||
id="sort"
|
id="sort"
|
||||||
value={userConfig.config.sort_by}
|
value={userConfig.sort_by}
|
||||||
onChange={event => {
|
onChange={event => {
|
||||||
setPartialConfig({ sort_by: event.target.value as SortByType });
|
handleUserConfigUpdate({ sort_by: event.target.value as SortByType });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<option value="published">date published</option>
|
<option value="published">date published</option>
|
||||||
@@ -67,9 +73,9 @@ const Filterbar = ({ hideToggleText, viewStyleName, showSort = true }: Filterbar
|
|||||||
<select
|
<select
|
||||||
name="sort_order"
|
name="sort_order"
|
||||||
id="sort-order"
|
id="sort-order"
|
||||||
value={userConfig.config.sort_order}
|
value={userConfig.sort_order}
|
||||||
onChange={event => {
|
onChange={event => {
|
||||||
setPartialConfig({ sort_order: event.target.value as SortOrderType });
|
handleUserConfigUpdate({ sort_order: event.target.value as SortOrderType });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<option value="asc">asc</option>
|
<option value="asc">asc</option>
|
||||||
@@ -90,22 +96,22 @@ const Filterbar = ({ hideToggleText, viewStyleName, showSort = true }: Filterbar
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{userConfig.config.grid_items !== undefined && isGridView && (
|
{userConfig.grid_items !== undefined && isGridView && (
|
||||||
<div className="grid-count">
|
<div className="grid-count">
|
||||||
{userConfig.config.grid_items < 7 && (
|
{userConfig.grid_items < 7 && (
|
||||||
<img
|
<img
|
||||||
src={iconAdd}
|
src={iconAdd}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPartialConfig({ grid_items: userConfig.config.grid_items + 1 });
|
handleUserConfigUpdate({ grid_items: userConfig.grid_items + 1 });
|
||||||
}}
|
}}
|
||||||
alt="grid plus row"
|
alt="grid plus row"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{userConfig.config.grid_items > 3 && (
|
{userConfig.grid_items > 3 && (
|
||||||
<img
|
<img
|
||||||
src={iconSubstract}
|
src={iconSubstract}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPartialConfig({ grid_items: userConfig.config.grid_items - 1 });
|
handleUserConfigUpdate({ grid_items: userConfig.grid_items - 1 });
|
||||||
}}
|
}}
|
||||||
alt="grid minus row"
|
alt="grid minus row"
|
||||||
/>
|
/>
|
||||||
@@ -115,14 +121,14 @@ const Filterbar = ({ hideToggleText, viewStyleName, showSort = true }: Filterbar
|
|||||||
<img
|
<img
|
||||||
src={iconGridView}
|
src={iconGridView}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPartialConfig({ [viewStyleName]: 'grid' });
|
handleUserConfigUpdate({ [viewStyleName]: 'grid' });
|
||||||
}}
|
}}
|
||||||
alt="grid view"
|
alt="grid view"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
src={iconListView}
|
src={iconListView}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPartialConfig({ [viewStyleName]: 'list' });
|
handleUserConfigUpdate({ [viewStyleName]: 'list' });
|
||||||
}}
|
}}
|
||||||
alt="list view"
|
alt="list view"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -93,8 +93,10 @@ const GoogleCast = ({ video, setRefresh, onWatchStateChanged }: GoogleCastProps)
|
|||||||
const [isConnected, setIsConnected] = useState(false);
|
const [isConnected, setIsConnected] = useState(false);
|
||||||
|
|
||||||
const setup = useCallback(() => {
|
const setup = useCallback(() => {
|
||||||
const cast = globalThis.cast;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const chrome = globalThis.chrome;
|
const cast = (globalThis as any).cast;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const chrome = (globalThis as any).chrome;
|
||||||
|
|
||||||
cast.framework.CastContext.getInstance().setOptions({
|
cast.framework.CastContext.getInstance().setOptions({
|
||||||
receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID, // Use built in receiver app on cast device, see https://developers.google.com/cast/docs/styled_receiver if you want to be able to add a theme, splash screen or watermark. Has a $5 one time fee.
|
receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID, // Use built in receiver app on cast device, see https://developers.google.com/cast/docs/styled_receiver if you want to be able to add a theme, splash screen or watermark. Has a $5 one time fee.
|
||||||
@@ -132,8 +134,10 @@ const GoogleCast = ({ video, setRefresh, onWatchStateChanged }: GoogleCastProps)
|
|||||||
}, [setRefresh, video]);
|
}, [setRefresh, video]);
|
||||||
|
|
||||||
const startPlayback = useCallback(() => {
|
const startPlayback = useCallback(() => {
|
||||||
const chrome = globalThis.chrome;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const cast = globalThis.cast;
|
const chrome = (globalThis as any).chrome;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const cast = (globalThis as any).cast;
|
||||||
const castSession = cast.framework.CastContext.getInstance().getCurrentSession();
|
const castSession = cast.framework.CastContext.getInstance().getCurrentSession();
|
||||||
|
|
||||||
const mediaUrl = video?.media_url;
|
const mediaUrl = video?.media_url;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ type PlaylistListProps = {
|
|||||||
|
|
||||||
const PlaylistList = ({ playlistList, setRefresh }: PlaylistListProps) => {
|
const PlaylistList = ({ playlistList, setRefresh }: PlaylistListProps) => {
|
||||||
const { userConfig } = useUserConfigStore();
|
const { userConfig } = useUserConfigStore();
|
||||||
const viewLayout = userConfig.config.view_style_playlist;
|
const viewLayout = userConfig.view_style_playlist;
|
||||||
|
|
||||||
if (!playlistList || playlistList.length === 0) {
|
if (!playlistList || playlistList.length === 0) {
|
||||||
return <p>No playlists found.</p>;
|
return <p>No playlists found.</p>;
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ const Subtitles = ({ subtitles }: SubtitlesProp) => {
|
|||||||
const handleTimeUpdate =
|
const handleTimeUpdate =
|
||||||
(
|
(
|
||||||
youtubeId: string,
|
youtubeId: string,
|
||||||
duration: number,
|
|
||||||
watched: boolean,
|
watched: boolean,
|
||||||
sponsorBlock?: SponsorBlockType,
|
sponsorBlock?: SponsorBlockType,
|
||||||
setSponsorSegmentSkipped?: Dispatch<SetStateAction<SponsorSegmentsSkippedType>>,
|
setSponsorSegmentSkipped?: Dispatch<SetStateAction<SponsorSegmentsSkippedType>>,
|
||||||
@@ -337,7 +336,6 @@ const VideoPlayer = ({
|
|||||||
}}
|
}}
|
||||||
onTimeUpdate={handleTimeUpdate(
|
onTimeUpdate={handleTimeUpdate(
|
||||||
videoId,
|
videoId,
|
||||||
duration,
|
|
||||||
watched,
|
watched,
|
||||||
sponsorBlock,
|
sponsorBlock,
|
||||||
setSkippedSegments,
|
setSkippedSegments,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const ColourConstant = {
|
|||||||
|
|
||||||
const useColours = () => {
|
const useColours = () => {
|
||||||
const { userConfig } = useUserConfigStore();
|
const { userConfig } = useUserConfigStore();
|
||||||
const stylesheet = userConfig?.config.stylesheet;
|
const stylesheet = userConfig?.stylesheet;
|
||||||
|
|
||||||
switch (stylesheet) {
|
switch (stylesheet) {
|
||||||
case ColourConstant.Dark:
|
case ColourConstant.Dark:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
import { useUserAccountStore } from '../stores/UserAccountStore';
|
||||||
|
|
||||||
const useIsAdmin = () => {
|
const useIsAdmin = () => {
|
||||||
const { userConfig } = useUserConfigStore();
|
const { userAccount } = useUserAccountStore();
|
||||||
const isAdmin = userConfig?.is_staff || userConfig?.is_superuser;
|
const isAdmin = userAccount?.is_staff || userAccount?.is_superuser;
|
||||||
|
|
||||||
return isAdmin;
|
return isAdmin;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import ChannelVideo from './pages/ChannelVideo';
|
|||||||
import ChannelPlaylist from './pages/ChannelPlaylist';
|
import ChannelPlaylist from './pages/ChannelPlaylist';
|
||||||
import ChannelAbout from './pages/ChannelAbout';
|
import ChannelAbout from './pages/ChannelAbout';
|
||||||
import Download from './pages/Download';
|
import Download from './pages/Download';
|
||||||
|
import loadUserAccount from './api/loader/loadUserAccount';
|
||||||
|
|
||||||
const router = createBrowserRouter(
|
const router = createBrowserRouter(
|
||||||
[
|
[
|
||||||
@@ -41,8 +42,9 @@ const router = createBrowserRouter(
|
|||||||
const authData = await auth.json();
|
const authData = await auth.json();
|
||||||
|
|
||||||
const userConfig = await loadUserMeConfig();
|
const userConfig = await loadUserMeConfig();
|
||||||
|
const userAccount = await loadUserAccount();
|
||||||
|
|
||||||
return { userConfig, auth: authData };
|
return { userConfig, userAccount, auth: authData };
|
||||||
},
|
},
|
||||||
element: <Base />,
|
element: <Base />,
|
||||||
errorElement: <ErrorPage />,
|
errorElement: <ErrorPage />,
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { Outlet, useLoaderData, useLocation, useSearchParams } from 'react-router-dom';
|
import { Outlet, useLoaderData, useLocation, useSearchParams } from 'react-router-dom';
|
||||||
import Footer from '../components/Footer';
|
import Footer from '../components/Footer';
|
||||||
import useColours from '../configuration/colours/useColours';
|
import useColours from '../configuration/colours/useColours';
|
||||||
import { UserMeType } from '../api/actions/updateUserConfig';
|
import { UserConfigType } from '../api/actions/updateUserConfig';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import Navigation from '../components/Navigation';
|
import Navigation from '../components/Navigation';
|
||||||
import { useAuthStore } from '../stores/AuthDataStore';
|
import { useAuthStore } from '../stores/AuthDataStore';
|
||||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
import { useUserConfigStore } from '../stores/UserConfigStore';
|
||||||
|
import { useUserAccountStore } from '../stores/UserAccountStore';
|
||||||
|
|
||||||
export type TaUpdateType = {
|
export type TaUpdateType = {
|
||||||
version?: string;
|
version?: string;
|
||||||
@@ -19,8 +20,19 @@ export type AuthenticationType = {
|
|||||||
ta_update: TaUpdateType;
|
ta_update: TaUpdateType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UserAccountType = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
is_superuser: boolean;
|
||||||
|
is_staff: boolean;
|
||||||
|
groups: [];
|
||||||
|
user_permissions: [];
|
||||||
|
last_login: string;
|
||||||
|
};
|
||||||
|
|
||||||
type BaseLoaderData = {
|
type BaseLoaderData = {
|
||||||
userConfig: UserMeType;
|
userConfig: UserConfigType;
|
||||||
|
userAccount: UserAccountType;
|
||||||
auth: AuthenticationType;
|
auth: AuthenticationType;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,7 +44,8 @@ export type OutletContextType = {
|
|||||||
const Base = () => {
|
const Base = () => {
|
||||||
const { setAuth } = useAuthStore();
|
const { setAuth } = useAuthStore();
|
||||||
const { setUserConfig } = useUserConfigStore();
|
const { setUserConfig } = useUserConfigStore();
|
||||||
const { userConfig, auth } = useLoaderData() as BaseLoaderData;
|
const { setUserAccount } = useUserAccountStore();
|
||||||
|
const { userConfig, userAccount, auth } = useLoaderData() as BaseLoaderData;
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
@@ -46,6 +59,7 @@ const Base = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAuth(auth);
|
setAuth(auth);
|
||||||
setUserConfig(userConfig);
|
setUserConfig(userConfig);
|
||||||
|
setUserAccount(userAccount);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ const ChannelAbout = () => {
|
|||||||
<div className="info-box">
|
<div className="info-box">
|
||||||
<div className="info-box-item">
|
<div className="info-box-item">
|
||||||
<h2>Channel Customization</h2>
|
<h2>Channel Customization</h2>
|
||||||
{userConfig.config.show_help_text && (
|
{userConfig.show_help_text && (
|
||||||
<div className="help-text">
|
<div className="help-text">
|
||||||
<ul>
|
<ul>
|
||||||
<li>Overwrite the download format over the format set globally.</li>
|
<li>Overwrite the download format over the format set globally.</li>
|
||||||
@@ -334,7 +334,7 @@ const ChannelAbout = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="info-box-item">
|
<div className="info-box-item">
|
||||||
<h2>Page Size Overrides</h2>
|
<h2>Page Size Overrides</h2>
|
||||||
{userConfig.config.show_help_text && (
|
{userConfig.show_help_text && (
|
||||||
<div className="help-text">
|
<div className="help-text">
|
||||||
<p>
|
<p>
|
||||||
Disable standard videos, shorts, or streams for this channel by setting their
|
Disable standard videos, shorts, or streams for this channel by setting their
|
||||||
|
|||||||
@@ -10,10 +10,11 @@ import { PlaylistsResponseType } from './Playlists';
|
|||||||
import iconGridView from '/img/icon-gridview.svg';
|
import iconGridView from '/img/icon-gridview.svg';
|
||||||
import iconListView from '/img/icon-listview.svg';
|
import iconListView from '/img/icon-listview.svg';
|
||||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
import { useUserConfigStore } from '../stores/UserConfigStore';
|
||||||
|
import updateUserConfig, { UserConfigType } from '../api/actions/updateUserConfig';
|
||||||
|
|
||||||
const ChannelPlaylist = () => {
|
const ChannelPlaylist = () => {
|
||||||
const { channelId } = useParams();
|
const { channelId } = useParams();
|
||||||
const { userConfig, setPartialConfig } = useUserConfigStore();
|
const { userConfig, setUserConfig } = useUserConfigStore();
|
||||||
const { currentPage, setCurrentPage } = useOutletContext() as OutletContextType;
|
const { currentPage, setCurrentPage } = useOutletContext() as OutletContextType;
|
||||||
|
|
||||||
const [refreshPlaylists, setRefreshPlaylists] = useState(false);
|
const [refreshPlaylists, setRefreshPlaylists] = useState(false);
|
||||||
@@ -23,8 +24,13 @@ const ChannelPlaylist = () => {
|
|||||||
const playlistList = playlistsResponse?.data;
|
const playlistList = playlistsResponse?.data;
|
||||||
const pagination = playlistsResponse?.paginate;
|
const pagination = playlistsResponse?.paginate;
|
||||||
|
|
||||||
const view = userConfig.config.view_style_playlist;
|
const view = userConfig.view_style_playlist;
|
||||||
const showSubedOnly = userConfig.config.show_subed_only;
|
const showSubedOnly = userConfig.show_subed_only;
|
||||||
|
|
||||||
|
const handleUserConfigUpdate = async (config: Partial<UserConfigType>) => {
|
||||||
|
const updatedUserConfig = await updateUserConfig(config);
|
||||||
|
setUserConfig(updatedUserConfig);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
@@ -52,7 +58,7 @@ const ChannelPlaylist = () => {
|
|||||||
<input
|
<input
|
||||||
checked={showSubedOnly}
|
checked={showSubedOnly}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
setPartialConfig({ show_subed_only: !showSubedOnly });
|
handleUserConfigUpdate({ show_subed_only: !showSubedOnly });
|
||||||
setRefreshPlaylists(true);
|
setRefreshPlaylists(true);
|
||||||
}}
|
}}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -73,14 +79,14 @@ const ChannelPlaylist = () => {
|
|||||||
<img
|
<img
|
||||||
src={iconGridView}
|
src={iconGridView}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPartialConfig({ view_style_playlist: 'grid' });
|
handleUserConfigUpdate({ view_style_playlist: 'grid' });
|
||||||
}}
|
}}
|
||||||
alt="grid view"
|
alt="grid view"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
src={iconListView}
|
src={iconListView}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPartialConfig({ view_style_playlist: 'list' });
|
handleUserConfigUpdate({ view_style_playlist: 'list' });
|
||||||
}}
|
}}
|
||||||
alt="list view"
|
alt="list view"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -49,10 +49,10 @@ const ChannelVideo = ({ videoType }: ChannelVideoProps) => {
|
|||||||
const hasVideos = videoResponse?.data?.length !== 0;
|
const hasVideos = videoResponse?.data?.length !== 0;
|
||||||
const showEmbeddedVideo = videoId !== null;
|
const showEmbeddedVideo = videoId !== null;
|
||||||
|
|
||||||
const view = userConfig.config.view_style_home;
|
const view = userConfig.view_style_home;
|
||||||
const isGridView = view === ViewStyles.grid;
|
const isGridView = view === ViewStyles.grid;
|
||||||
const gridView = isGridView ? `boxed-${userConfig.config.grid_items}` : '';
|
const gridView = isGridView ? `boxed-${userConfig.grid_items}` : '';
|
||||||
const gridViewGrid = isGridView ? `grid-${userConfig.config.grid_items}` : '';
|
const gridViewGrid = isGridView ? `grid-${userConfig.grid_items}` : '';
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
@@ -60,9 +60,9 @@ const ChannelVideo = ({ videoType }: ChannelVideoProps) => {
|
|||||||
const videos = await loadVideoListByFilter({
|
const videos = await loadVideoListByFilter({
|
||||||
channel: channelId,
|
channel: channelId,
|
||||||
page: currentPage,
|
page: currentPage,
|
||||||
watch: userConfig.config.hide_watched ? 'unwatched' : undefined,
|
watch: userConfig.hide_watched ? 'unwatched' : undefined,
|
||||||
sort: userConfig.config.sort_by,
|
sort: userConfig.sort_by,
|
||||||
order: userConfig.config.sort_order,
|
order: userConfig.sort_order,
|
||||||
type: videoType,
|
type: videoType,
|
||||||
});
|
});
|
||||||
const channelAggs = await loadChannelAggs(channelId);
|
const channelAggs = await loadChannelAggs(channelId);
|
||||||
@@ -74,9 +74,9 @@ const ChannelVideo = ({ videoType }: ChannelVideoProps) => {
|
|||||||
})();
|
})();
|
||||||
}, [
|
}, [
|
||||||
refresh,
|
refresh,
|
||||||
userConfig.config.sort_by,
|
userConfig.sort_by,
|
||||||
userConfig.config.sort_order,
|
userConfig.sort_order,
|
||||||
userConfig.config.hide_watched,
|
userConfig.hide_watched,
|
||||||
currentPage,
|
currentPage,
|
||||||
channelId,
|
channelId,
|
||||||
pagination?.current_page,
|
pagination?.current_page,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import Button from '../components/Button';
|
|||||||
import updateBulkChannelSubscriptions from '../api/actions/updateBulkChannelSubscriptions';
|
import updateBulkChannelSubscriptions from '../api/actions/updateBulkChannelSubscriptions';
|
||||||
import useIsAdmin from '../functions/useIsAdmin';
|
import useIsAdmin from '../functions/useIsAdmin';
|
||||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
import { useUserConfigStore } from '../stores/UserConfigStore';
|
||||||
|
import updateUserConfig, { UserConfigType } from '../api/actions/updateUserConfig';
|
||||||
|
|
||||||
type ChannelOverwritesType = {
|
type ChannelOverwritesType = {
|
||||||
download_format?: string;
|
download_format?: string;
|
||||||
@@ -48,7 +49,7 @@ type ChannelsListResponse = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Channels = () => {
|
const Channels = () => {
|
||||||
const { userConfig, setPartialConfig } = useUserConfigStore();
|
const { userConfig, setUserConfig } = useUserConfigStore();
|
||||||
const { currentPage, setCurrentPage } = useOutletContext() as OutletContextType;
|
const { currentPage, setCurrentPage } = useOutletContext() as OutletContextType;
|
||||||
const isAdmin = useIsAdmin();
|
const isAdmin = useIsAdmin();
|
||||||
|
|
||||||
@@ -63,18 +64,20 @@ const Channels = () => {
|
|||||||
// const channelCount = pagination?.total_hits;
|
// const channelCount = pagination?.total_hits;
|
||||||
const hasChannels = channels?.length !== 0;
|
const hasChannels = channels?.length !== 0;
|
||||||
|
|
||||||
|
const handleUserConfigUpdate = async (config: Partial<UserConfigType>) => {
|
||||||
|
const updatedUserConfig = await updateUserConfig(config);
|
||||||
|
setUserConfig(updatedUserConfig);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const channelListResponse = await loadChannelList(
|
const channelListResponse = await loadChannelList(currentPage, userConfig.show_subed_only);
|
||||||
currentPage,
|
|
||||||
userConfig.config.show_subed_only,
|
|
||||||
);
|
|
||||||
|
|
||||||
setChannelListResponse(channelListResponse);
|
setChannelListResponse(channelListResponse);
|
||||||
setShowNotification(false);
|
setShowNotification(false);
|
||||||
setRefresh(false);
|
setRefresh(false);
|
||||||
})();
|
})();
|
||||||
}, [refresh, userConfig.config.show_subed_only, currentPage, pagination?.current_page]);
|
}, [refresh, userConfig.show_subed_only, currentPage, pagination?.current_page]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -145,18 +148,18 @@ const Channels = () => {
|
|||||||
<input
|
<input
|
||||||
id="show_subed_only"
|
id="show_subed_only"
|
||||||
onChange={async () => {
|
onChange={async () => {
|
||||||
setPartialConfig({ show_subed_only: !userConfig.config.show_subed_only });
|
handleUserConfigUpdate({ show_subed_only: !userConfig.show_subed_only });
|
||||||
setRefresh(true);
|
setRefresh(true);
|
||||||
}}
|
}}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={userConfig.config.show_subed_only}
|
checked={userConfig.show_subed_only}
|
||||||
/>
|
/>
|
||||||
{!userConfig.config.show_subed_only && (
|
{!userConfig.show_subed_only && (
|
||||||
<label htmlFor="" className="ofbtn">
|
<label htmlFor="" className="ofbtn">
|
||||||
Off
|
Off
|
||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
{userConfig.config.show_subed_only && (
|
{userConfig.show_subed_only && (
|
||||||
<label htmlFor="" className="onbtn">
|
<label htmlFor="" className="onbtn">
|
||||||
On
|
On
|
||||||
</label>
|
</label>
|
||||||
@@ -167,7 +170,7 @@ const Channels = () => {
|
|||||||
<img
|
<img
|
||||||
src={iconGridView}
|
src={iconGridView}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPartialConfig({ view_style_channel: 'grid' });
|
handleUserConfigUpdate({ view_style_channel: 'grid' });
|
||||||
}}
|
}}
|
||||||
data-origin="channel"
|
data-origin="channel"
|
||||||
data-value="grid"
|
data-value="grid"
|
||||||
@@ -176,7 +179,7 @@ const Channels = () => {
|
|||||||
<img
|
<img
|
||||||
src={iconListView}
|
src={iconListView}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPartialConfig({ view_style_channel: 'list' });
|
handleUserConfigUpdate({ view_style_channel: 'list' });
|
||||||
}}
|
}}
|
||||||
data-origin="channel"
|
data-origin="channel"
|
||||||
data-value="list"
|
data-value="list"
|
||||||
@@ -186,7 +189,7 @@ const Channels = () => {
|
|||||||
</div>
|
</div>
|
||||||
{/* {hasChannels && <h2>Total channels: {channelCount}</h2>} */}
|
{/* {hasChannels && <h2>Total channels: {channelCount}</h2>} */}
|
||||||
|
|
||||||
<div className={`channel-list ${userConfig.config.view_style_channel}`}>
|
<div className={`channel-list ${userConfig.view_style_channel}`}>
|
||||||
{!hasChannels && <h2>No channels found...</h2>}
|
{!hasChannels && <h2>No channels found...</h2>}
|
||||||
|
|
||||||
{hasChannels && <ChannelList channelList={channels} refreshChannelList={setRefresh} />}
|
{hasChannels && <ChannelList channelList={channels} refreshChannelList={setRefresh} />}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import Button from '../components/Button';
|
|||||||
import DownloadListItem from '../components/DownloadListItem';
|
import DownloadListItem from '../components/DownloadListItem';
|
||||||
import loadDownloadAggs, { DownloadAggsType } from '../api/loader/loadDownloadAggs';
|
import loadDownloadAggs, { DownloadAggsType } from '../api/loader/loadDownloadAggs';
|
||||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
import { useUserConfigStore } from '../stores/UserConfigStore';
|
||||||
|
import updateUserConfig, { UserConfigType } from '../api/actions/updateUserConfig';
|
||||||
|
|
||||||
type Download = {
|
type Download = {
|
||||||
auto_start: boolean;
|
auto_start: boolean;
|
||||||
@@ -46,7 +47,7 @@ export type DownloadResponseType = {
|
|||||||
|
|
||||||
const Download = () => {
|
const Download = () => {
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const { userConfig, setPartialConfig } = useUserConfigStore();
|
const { userConfig, setUserConfig } = useUserConfigStore();
|
||||||
const { currentPage, setCurrentPage } = useOutletContext() as OutletContextType;
|
const { currentPage, setCurrentPage } = useOutletContext() as OutletContextType;
|
||||||
|
|
||||||
const channelFilterFromUrl = searchParams.get('channel');
|
const channelFilterFromUrl = searchParams.get('channel');
|
||||||
@@ -75,13 +76,18 @@ const Download = () => {
|
|||||||
? downloadResponse?.data[0].channel_name
|
? downloadResponse?.data[0].channel_name
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
const view = userConfig.config.view_style_downloads;
|
const view = userConfig.view_style_downloads;
|
||||||
const gridItems = userConfig.config.grid_items;
|
const gridItems = userConfig.grid_items;
|
||||||
const showIgnored = userConfig.config.show_ignored_only;
|
const showIgnored = userConfig.show_ignored_only;
|
||||||
const isGridView = view === ViewStyles.grid;
|
const isGridView = view === ViewStyles.grid;
|
||||||
const gridView = isGridView ? `boxed-${gridItems}` : '';
|
const gridView = isGridView ? `boxed-${gridItems}` : '';
|
||||||
const gridViewGrid = isGridView ? `grid-${gridItems}` : '';
|
const gridViewGrid = isGridView ? `grid-${gridItems}` : '';
|
||||||
|
|
||||||
|
const handleUserConfigUpdate = async (config: Partial<UserConfigType>) => {
|
||||||
|
const updatedUserConfig = await updateUserConfig(config);
|
||||||
|
setUserConfig(updatedUserConfig);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const videos = await loadDownloadQueue(currentPage, channelFilterFromUrl, showIgnored);
|
const videos = await loadDownloadQueue(currentPage, channelFilterFromUrl, showIgnored);
|
||||||
@@ -208,7 +214,7 @@ const Download = () => {
|
|||||||
<input
|
<input
|
||||||
id="showIgnored"
|
id="showIgnored"
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
setPartialConfig({ show_ignored_only: !showIgnored });
|
handleUserConfigUpdate({ show_ignored_only: !showIgnored });
|
||||||
setRefresh(true);
|
setRefresh(true);
|
||||||
}}
|
}}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -263,7 +269,7 @@ const Download = () => {
|
|||||||
<img
|
<img
|
||||||
src={iconAdd}
|
src={iconAdd}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPartialConfig({ grid_items: gridItems + 1 });
|
handleUserConfigUpdate({ grid_items: gridItems + 1 });
|
||||||
}}
|
}}
|
||||||
alt="grid plus row"
|
alt="grid plus row"
|
||||||
/>
|
/>
|
||||||
@@ -272,7 +278,7 @@ const Download = () => {
|
|||||||
<img
|
<img
|
||||||
src={iconSubstract}
|
src={iconSubstract}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPartialConfig({ grid_items: gridItems - 1 });
|
handleUserConfigUpdate({ grid_items: gridItems - 1 });
|
||||||
}}
|
}}
|
||||||
alt="grid minus row"
|
alt="grid minus row"
|
||||||
/>
|
/>
|
||||||
@@ -283,14 +289,14 @@ const Download = () => {
|
|||||||
<img
|
<img
|
||||||
src={iconGridView}
|
src={iconGridView}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPartialConfig({ view_style_downloads: 'grid' });
|
handleUserConfigUpdate({ view_style_downloads: 'grid' });
|
||||||
}}
|
}}
|
||||||
alt="grid view"
|
alt="grid view"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
src={iconListView}
|
src={iconListView}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPartialConfig({ view_style_downloads: 'list' });
|
handleUserConfigUpdate({ view_style_downloads: 'list' });
|
||||||
}}
|
}}
|
||||||
alt="list view"
|
alt="list view"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -108,8 +108,6 @@ const Home = () => {
|
|||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const videoId = searchParams.get('videoId');
|
const videoId = searchParams.get('videoId');
|
||||||
|
|
||||||
const userMeConfig = userConfig.config;
|
|
||||||
|
|
||||||
const [refreshVideoList, setRefreshVideoList] = useState(false);
|
const [refreshVideoList, setRefreshVideoList] = useState(false);
|
||||||
|
|
||||||
const [videoResponse, setVideoReponse] = useState<VideoListByFilterResponseType>();
|
const [videoResponse, setVideoReponse] = useState<VideoListByFilterResponseType>();
|
||||||
@@ -123,17 +121,17 @@ const Home = () => {
|
|||||||
const hasVideos = videoResponse?.data?.length !== 0;
|
const hasVideos = videoResponse?.data?.length !== 0;
|
||||||
const showEmbeddedVideo = videoId !== null;
|
const showEmbeddedVideo = videoId !== null;
|
||||||
|
|
||||||
const isGridView = userMeConfig.view_style_home === ViewStyles.grid;
|
const isGridView = userConfig.view_style_home === ViewStyles.grid;
|
||||||
const gridView = isGridView ? `boxed-${userMeConfig.grid_items}` : '';
|
const gridView = isGridView ? `boxed-${userConfig.grid_items}` : '';
|
||||||
const gridViewGrid = isGridView ? `grid-${userMeConfig.grid_items}` : '';
|
const gridViewGrid = isGridView ? `grid-${userConfig.grid_items}` : '';
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const videos = await loadVideoListByFilter({
|
const videos = await loadVideoListByFilter({
|
||||||
page: currentPage,
|
page: currentPage,
|
||||||
watch: userMeConfig.hide_watched ? 'unwatched' : undefined,
|
watch: userConfig.hide_watched ? 'unwatched' : undefined,
|
||||||
sort: userMeConfig.sort_by,
|
sort: userConfig.sort_by,
|
||||||
order: userMeConfig.sort_order,
|
order: userConfig.sort_order,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -150,9 +148,9 @@ const Home = () => {
|
|||||||
})();
|
})();
|
||||||
}, [
|
}, [
|
||||||
refreshVideoList,
|
refreshVideoList,
|
||||||
userMeConfig.sort_by,
|
userConfig.sort_by,
|
||||||
userMeConfig.sort_order,
|
userConfig.sort_order,
|
||||||
userMeConfig.hide_watched,
|
userConfig.hide_watched,
|
||||||
currentPage,
|
currentPage,
|
||||||
pagination?.current_page,
|
pagination?.current_page,
|
||||||
showEmbeddedVideo,
|
showEmbeddedVideo,
|
||||||
@@ -171,10 +169,10 @@ const Home = () => {
|
|||||||
<div className="title-bar">
|
<div className="title-bar">
|
||||||
<h1>Continue Watching</h1>
|
<h1>Continue Watching</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className={`video-list ${userMeConfig.view_style_home} ${gridViewGrid}`}>
|
<div className={`video-list ${userConfig.view_style_home} ${gridViewGrid}`}>
|
||||||
<VideoList
|
<VideoList
|
||||||
videoList={continueVideos}
|
videoList={continueVideos}
|
||||||
viewLayout={userMeConfig.view_style_home}
|
viewLayout={userConfig.view_style_home}
|
||||||
refreshVideoList={setRefreshVideoList}
|
refreshVideoList={setRefreshVideoList}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -189,7 +187,7 @@ const Home = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={`boxed-content ${gridView}`}>
|
<div className={`boxed-content ${gridView}`}>
|
||||||
<div className={`video-list ${userMeConfig.view_style_home} ${gridViewGrid}`}>
|
<div className={`video-list ${userConfig.view_style_home} ${gridViewGrid}`}>
|
||||||
{!hasVideos && (
|
{!hasVideos && (
|
||||||
<>
|
<>
|
||||||
<h2>No videos found...</h2>
|
<h2>No videos found...</h2>
|
||||||
@@ -204,7 +202,7 @@ const Home = () => {
|
|||||||
{hasVideos && (
|
{hasVideos && (
|
||||||
<VideoList
|
<VideoList
|
||||||
videoList={videoList}
|
videoList={videoList}
|
||||||
viewLayout={userMeConfig.view_style_home}
|
viewLayout={userConfig.view_style_home}
|
||||||
refreshVideoList={setRefreshVideoList}
|
refreshVideoList={setRefreshVideoList}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -81,9 +81,9 @@ const Playlist = () => {
|
|||||||
const videoInPlaylistCount = pagination?.total_hits;
|
const videoInPlaylistCount = pagination?.total_hits;
|
||||||
const showEmbeddedVideo = videoId !== null;
|
const showEmbeddedVideo = videoId !== null;
|
||||||
|
|
||||||
const view = userConfig.config.view_style_home;
|
const view = userConfig.view_style_home;
|
||||||
const gridItems = userConfig.config.grid_items;
|
const gridItems = userConfig.grid_items;
|
||||||
const hideWatched = userConfig.config.hide_watched;
|
const hideWatched = userConfig.hide_watched;
|
||||||
const isGridView = view === ViewStyles.grid;
|
const isGridView = view === ViewStyles.grid;
|
||||||
const gridView = isGridView ? `boxed-${gridItems}` : '';
|
const gridView = isGridView ? `boxed-${gridItems}` : '';
|
||||||
const gridViewGrid = isGridView ? `grid-${gridItems}` : '';
|
const gridViewGrid = isGridView ? `grid-${gridItems}` : '';
|
||||||
@@ -112,7 +112,7 @@ const Playlist = () => {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [
|
}, [
|
||||||
playlistId,
|
playlistId,
|
||||||
userConfig.config.hide_watched,
|
userConfig.hide_watched,
|
||||||
refresh,
|
refresh,
|
||||||
currentPage,
|
currentPage,
|
||||||
pagination?.current_page,
|
pagination?.current_page,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import Button from '../components/Button';
|
|||||||
import useIsAdmin from '../functions/useIsAdmin';
|
import useIsAdmin from '../functions/useIsAdmin';
|
||||||
import { useUserConfigStore } from '../stores/UserConfigStore';
|
import { useUserConfigStore } from '../stores/UserConfigStore';
|
||||||
import Notifications from '../components/Notifications';
|
import Notifications from '../components/Notifications';
|
||||||
|
import updateUserConfig, { UserConfigType } from '../api/actions/updateUserConfig';
|
||||||
|
|
||||||
export type PlaylistEntryType = {
|
export type PlaylistEntryType = {
|
||||||
youtube_id: string;
|
youtube_id: string;
|
||||||
@@ -34,7 +35,7 @@ export type PlaylistsResponseType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Playlists = () => {
|
const Playlists = () => {
|
||||||
const { userConfig, setPartialConfig } = useUserConfigStore();
|
const { userConfig, setUserConfig } = useUserConfigStore();
|
||||||
const { currentPage, setCurrentPage } = useOutletContext() as OutletContextType;
|
const { currentPage, setCurrentPage } = useOutletContext() as OutletContextType;
|
||||||
const isAdmin = useIsAdmin();
|
const isAdmin = useIsAdmin();
|
||||||
|
|
||||||
@@ -51,8 +52,8 @@ const Playlists = () => {
|
|||||||
|
|
||||||
const hasPlaylists = playlistResponse?.data?.length !== 0;
|
const hasPlaylists = playlistResponse?.data?.length !== 0;
|
||||||
|
|
||||||
const view = userConfig.config.view_style_playlist;
|
const view = userConfig.view_style_playlist;
|
||||||
const showSubedOnly = userConfig.config.show_subed_only;
|
const showSubedOnly = userConfig.show_subed_only;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
@@ -66,7 +67,12 @@ const Playlists = () => {
|
|||||||
setShowNotification(false);
|
setShowNotification(false);
|
||||||
})();
|
})();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [refresh, userConfig.config.show_subed_only, currentPage, pagination?.current_page]);
|
}, [refresh, userConfig.show_subed_only, currentPage, pagination?.current_page]);
|
||||||
|
|
||||||
|
const handleUserConfigUpdate = async (config: Partial<UserConfigType>) => {
|
||||||
|
const updatedUserConfig = await updateUserConfig(config);
|
||||||
|
setUserConfig(updatedUserConfig);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -156,7 +162,7 @@ const Playlists = () => {
|
|||||||
<input
|
<input
|
||||||
checked={showSubedOnly}
|
checked={showSubedOnly}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
setPartialConfig({ show_subed_only: !showSubedOnly });
|
handleUserConfigUpdate({ show_subed_only: !showSubedOnly });
|
||||||
}}
|
}}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
/>
|
/>
|
||||||
@@ -176,14 +182,14 @@ const Playlists = () => {
|
|||||||
<img
|
<img
|
||||||
src={iconGridView}
|
src={iconGridView}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPartialConfig({ view_style_playlist: 'grid' });
|
handleUserConfigUpdate({ view_style_playlist: 'grid' });
|
||||||
}}
|
}}
|
||||||
alt="grid view"
|
alt="grid view"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
src={iconListView}
|
src={iconListView}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPartialConfig({ view_style_playlist: 'list' });
|
handleUserConfigUpdate({ view_style_playlist: 'list' });
|
||||||
}}
|
}}
|
||||||
alt="list view"
|
alt="list view"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -39,12 +39,11 @@ const Search = () => {
|
|||||||
const { userConfig } = useUserConfigStore();
|
const { userConfig } = useUserConfigStore();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const videoId = searchParams.get('videoId');
|
const videoId = searchParams.get('videoId');
|
||||||
const userMeConfig = userConfig.config;
|
|
||||||
|
|
||||||
const viewVideos = userMeConfig.view_style_home;
|
const viewVideos = userConfig.view_style_home;
|
||||||
const viewChannels = userMeConfig.view_style_channel;
|
const viewChannels = userConfig.view_style_channel;
|
||||||
const viewPlaylists = userMeConfig.view_style_playlist;
|
const viewPlaylists = userConfig.view_style_playlist;
|
||||||
const gridItems = userMeConfig.grid_items || 3;
|
const gridItems = userConfig.grid_items || 3;
|
||||||
|
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState<string>('');
|
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState<string>('');
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ const SettingsApplication = () => {
|
|||||||
<div className="info-box">
|
<div className="info-box">
|
||||||
<div className="info-box-item">
|
<div className="info-box-item">
|
||||||
<h2 id="subscriptions">Subscription Scan</h2>
|
<h2 id="subscriptions">Subscription Scan</h2>
|
||||||
{userConfig.config.show_help_text && (
|
{userConfig.show_help_text && (
|
||||||
<div className="help-text">
|
<div className="help-text">
|
||||||
<p>Configure how a subscription Scan tracks videos.</p>
|
<p>Configure how a subscription Scan tracks videos.</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -275,7 +275,7 @@ const SettingsApplication = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="info-box-item">
|
<div className="info-box-item">
|
||||||
<h2 id="downloads">Downloads</h2>
|
<h2 id="downloads">Downloads</h2>
|
||||||
{userConfig.config.show_help_text && (
|
{userConfig.show_help_text && (
|
||||||
<div className="help-text">
|
<div className="help-text">
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
@@ -362,7 +362,7 @@ const SettingsApplication = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="info-box-item">
|
<div className="info-box-item">
|
||||||
<h2 id="format">Download Format</h2>
|
<h2 id="format">Download Format</h2>
|
||||||
{userConfig.config.show_help_text && (
|
{userConfig.show_help_text && (
|
||||||
<div className="help-text">
|
<div className="help-text">
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
@@ -507,7 +507,7 @@ const SettingsApplication = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="info-box-item">
|
<div className="info-box-item">
|
||||||
<h2 id="subtitles">Subtitles</h2>
|
<h2 id="subtitles">Subtitles</h2>
|
||||||
{userConfig.config.show_help_text && (
|
{userConfig.show_help_text && (
|
||||||
<div className="help-text">
|
<div className="help-text">
|
||||||
<p>Additional subtitle options show once you choose a language.</p>
|
<p>Additional subtitle options show once you choose a language.</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -586,7 +586,7 @@ const SettingsApplication = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="info-box-item">
|
<div className="info-box-item">
|
||||||
<h2 id="comments">Comments</h2>
|
<h2 id="comments">Comments</h2>
|
||||||
{userConfig.config.show_help_text && (
|
{userConfig.show_help_text && (
|
||||||
<div className="help-text">
|
<div className="help-text">
|
||||||
<p>Additional options show once you set a comment index option.</p>
|
<p>Additional options show once you set a comment index option.</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -650,7 +650,7 @@ const SettingsApplication = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="info-box-item">
|
<div className="info-box-item">
|
||||||
<h2 id="cookie">Cookie</h2>
|
<h2 id="cookie">Cookie</h2>
|
||||||
{userConfig.config.show_help_text && (
|
{userConfig.show_help_text && (
|
||||||
<div className="help-text">
|
<div className="help-text">
|
||||||
<p>
|
<p>
|
||||||
Importing your cookie will authenticate requests to YT with your user account.
|
Importing your cookie will authenticate requests to YT with your user account.
|
||||||
@@ -777,7 +777,7 @@ const SettingsApplication = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="info-box-item">
|
<div className="info-box-item">
|
||||||
<h2 id="sntegrations">Integrations</h2>
|
<h2 id="sntegrations">Integrations</h2>
|
||||||
{userConfig.config.show_help_text && (
|
{userConfig.show_help_text && (
|
||||||
<div className="help-text">
|
<div className="help-text">
|
||||||
<ul>
|
<ul>
|
||||||
<li>The API token is used to make automater requests to TA through the API.</li>
|
<li>The API token is used to make automater requests to TA through the API.</li>
|
||||||
@@ -868,7 +868,7 @@ const SettingsApplication = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="info-box-item">
|
<div className="info-box-item">
|
||||||
<h2>Snapshots</h2>
|
<h2>Snapshots</h2>
|
||||||
{userConfig.config.show_help_text && (
|
{userConfig.show_help_text && (
|
||||||
<div className="help-text">
|
<div className="help-text">
|
||||||
<p>
|
<p>
|
||||||
Automatically create daily deduplicated snapshots of the index, stored in
|
Automatically create daily deduplicated snapshots of the index, stored in
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { ColourVariants } from '../api/actions/updateUserConfig';
|
import updateUserConfig, { ColourVariants, UserConfigType } from '../api/actions/updateUserConfig';
|
||||||
import { ColourConstant } from '../configuration/colours/useColours';
|
import { ColourConstant } from '../configuration/colours/useColours';
|
||||||
import SettingsNavigation from '../components/SettingsNavigation';
|
import SettingsNavigation from '../components/SettingsNavigation';
|
||||||
import Notifications from '../components/Notifications';
|
import Notifications from '../components/Notifications';
|
||||||
@@ -10,35 +10,40 @@ import { useEffect, useState } from 'react';
|
|||||||
import ToggleConfig from '../components/ToggleConfig';
|
import ToggleConfig from '../components/ToggleConfig';
|
||||||
|
|
||||||
const SettingsUser = () => {
|
const SettingsUser = () => {
|
||||||
const { userConfig, setPartialConfig } = useUserConfigStore();
|
const { userConfig, setUserConfig } = useUserConfigStore();
|
||||||
const isAdmin = useIsAdmin();
|
const isAdmin = useIsAdmin();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [styleSheet, setStyleSheet] = useState<ColourVariants>(userConfig.config.stylesheet);
|
const [styleSheet, setStyleSheet] = useState<ColourVariants>(userConfig.stylesheet);
|
||||||
const [styleSheetRefresh, setStyleSheetRefresh] = useState(false);
|
const [styleSheetRefresh, setStyleSheetRefresh] = useState(false);
|
||||||
const [pageSize, setPageSize] = useState<number>(userConfig.config.page_size);
|
const [pageSize, setPageSize] = useState<number>(userConfig.page_size);
|
||||||
const [showHelpText, setShowHelpText] = useState(userConfig.config.show_help_text);
|
const [showHelpText, setShowHelpText] = useState(userConfig.show_help_text);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
setStyleSheet(userConfig.config.stylesheet);
|
setStyleSheet(userConfig.stylesheet);
|
||||||
setPageSize(userConfig.config.page_size);
|
setPageSize(userConfig.page_size);
|
||||||
setShowHelpText(userConfig.config.show_help_text);
|
setShowHelpText(userConfig.show_help_text);
|
||||||
})();
|
})();
|
||||||
}, [userConfig.config.page_size, userConfig.config.stylesheet, userConfig.config.show_help_text]);
|
}, [userConfig.page_size, userConfig.stylesheet, userConfig.show_help_text]);
|
||||||
|
|
||||||
const handleStyleSheetChange = async (selectedStyleSheet: ColourVariants) => {
|
const handleStyleSheetChange = async (selectedStyleSheet: ColourVariants) => {
|
||||||
setPartialConfig({ stylesheet: selectedStyleSheet });
|
handleUserConfigUpdate({ stylesheet: selectedStyleSheet });
|
||||||
setStyleSheet(selectedStyleSheet);
|
setStyleSheet(selectedStyleSheet);
|
||||||
setStyleSheetRefresh(true);
|
setStyleSheetRefresh(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePageSizeChange = async () => {
|
const handlePageSizeChange = async () => {
|
||||||
setPartialConfig({ page_size: pageSize });
|
handleUserConfigUpdate({ page_size: pageSize });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleShowHelpTextChange = async (configKey: string, configValue: boolean) => {
|
const handleShowHelpTextChange = async (configKey: string, configValue: boolean) => {
|
||||||
setPartialConfig({ [configKey]: configValue });
|
handleUserConfigUpdate({ [configKey]: configValue });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUserConfigUpdate = async (config: Partial<UserConfigType>) => {
|
||||||
|
const updatedUserConfig = await updateUserConfig(config);
|
||||||
|
setUserConfig(updatedUserConfig);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePageRefresh = () => {
|
const handlePageRefresh = () => {
|
||||||
@@ -99,12 +104,10 @@ const SettingsUser = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="button-box">
|
<div className="button-box">
|
||||||
{userConfig.config.page_size !== pageSize && (
|
{userConfig.page_size !== pageSize && (
|
||||||
<>
|
<>
|
||||||
<button onClick={handlePageSizeChange}>Update</button>
|
<button onClick={handlePageSizeChange}>Update</button>
|
||||||
<button onClick={() => setPageSize(userConfig.config.page_size)}>
|
<button onClick={() => setPageSize(userConfig.page_size)}>Cancel</button>
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
12
frontend/src/stores/UserAccountStore.ts
Normal file
12
frontend/src/stores/UserAccountStore.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { create } from 'zustand';
|
||||||
|
import { UserAccountType } from '../pages/Base';
|
||||||
|
|
||||||
|
interface AccountState {
|
||||||
|
userAccount: UserAccountType | null;
|
||||||
|
setUserAccount: (userAccount: UserAccountType) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useUserAccountStore = create<AccountState>(set => ({
|
||||||
|
userAccount: null,
|
||||||
|
setUserAccount: userAccount => set({ userAccount }),
|
||||||
|
}));
|
||||||
@@ -1,22 +1,13 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import updateUserConfig, { UserMeType, UserConfigType } from '../api/actions/updateUserConfig';
|
import { UserConfigType } from '../api/actions/updateUserConfig';
|
||||||
|
|
||||||
interface UserConfigState {
|
interface UserConfigState {
|
||||||
userConfig: UserMeType;
|
userConfig: UserConfigType;
|
||||||
setUserConfig: (userConfig: UserMeType) => void;
|
setUserConfig: (userConfig: UserConfigType) => void;
|
||||||
setPartialConfig: (userConfig: Partial<UserConfigType>) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useUserConfigStore = create<UserConfigState>(set => ({
|
export const useUserConfigStore = create<UserConfigState>(set => ({
|
||||||
userConfig: {
|
userConfig: {
|
||||||
id: 0,
|
|
||||||
name: '',
|
|
||||||
is_superuser: false,
|
|
||||||
is_staff: false,
|
|
||||||
groups: [],
|
|
||||||
user_permissions: [],
|
|
||||||
last_login: '',
|
|
||||||
config: {
|
|
||||||
stylesheet: 'dark.css',
|
stylesheet: 'dark.css',
|
||||||
page_size: 12,
|
page_size: 12,
|
||||||
sort_by: 'published',
|
sort_by: 'published',
|
||||||
@@ -31,15 +22,5 @@ export const useUserConfigStore = create<UserConfigState>(set => ({
|
|||||||
show_subed_only: false,
|
show_subed_only: false,
|
||||||
show_help_text: true,
|
show_help_text: true,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
setUserConfig: userConfig => set({ userConfig }),
|
setUserConfig: userConfig => set({ userConfig }),
|
||||||
|
|
||||||
setPartialConfig: async (userConfig: Partial<UserConfigType>) => {
|
|
||||||
const userConfigResponse = await updateUserConfig(userConfig);
|
|
||||||
set(state => ({
|
|
||||||
userConfig: state.userConfig
|
|
||||||
? { ...state.userConfig, config: userConfigResponse }
|
|
||||||
: state.userConfig,
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import react from '@vitejs/plugin-react-swc';
|
import react from '@vitejs/plugin-react-swc';
|
||||||
|
import checker from 'vite-plugin-checker';
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react(), checker({ typescript: true })],
|
||||||
base: '/',
|
base: '/',
|
||||||
server: {
|
server: {
|
||||||
host: true,
|
host: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user