From d1922363631d9f3f5dafad10dcdb02cab78e60c0 Mon Sep 17 00:00:00 2001 From: Nicholas de Paola Date: Sun, 20 Oct 2024 19:12:47 +1000 Subject: [PATCH] upgrade react and redux dependencies --- frontend/package-lock.json | 306 ++++++++---------- frontend/package.json | 14 +- frontend/src/app/listenerMiddleware.ts | 42 +-- frontend/src/app/store.ts | 39 ++- frontend/src/common/test-utils.tsx | 3 +- frontend/src/common/types.ts | 22 +- frontend/src/features/backend/backendSlice.ts | 6 +- frontend/src/features/card/cardbackSlice.ts | 12 +- .../src/features/export/exportDecklist.tsx | 8 +- frontend/src/features/export/exportXML.tsx | 6 +- .../finishSettings/finishSettingsSlice.ts | 6 +- .../invalidIdentifiersSlice.ts | 11 +- frontend/src/features/modals/modalsSlice.ts | 12 +- frontend/src/features/project/projectSlice.ts | 5 +- .../src/features/search/cardDocumentsSlice.ts | 9 +- .../src/features/search/searchResultsSlice.ts | 9 +- .../features/search/sourceDocumentsSlice.ts | 12 +- .../searchSettings/searchSettingsSlice.ts | 5 +- frontend/src/features/toasts/toastsSlice.ts | 6 +- .../viewSettings/viewSettingsSlice.ts | 6 +- 20 files changed, 264 insertions(+), 275 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 80e4f57c3..9aa479816 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,9 +9,9 @@ "version": "0.1.0", "license": "GPL-3.0-only", "dependencies": { - "@hello-pangea/dnd": "^16.2.0", + "@hello-pangea/dnd": "^17.0.0", "@popperjs/core": "^2.11.6", - "@reduxjs/toolkit": "^1.9.5", + "@reduxjs/toolkit": "^2.3.0", "@types/ua-parser-js": "^0.7.36", "ajv": "^8.12.0", "async-await-queue": "^2.1.4", @@ -26,13 +26,13 @@ "next": "^13.5.4", "nextjs-google-analytics": "^2.3.3", "ping.js": "^0.3.0", - "react": "18.2.0", + "react": "^18.3.1", "react-bootstrap": "^2.7.2", "react-bootstrap-toggle": "^2.3.2", - "react-dom": "18.2.0", + "react-dom": "^18.3.1", "react-dropdown-tree-select": "^2.8.0", "react-dropzone": "^14.2.3", - "react-redux": "^8.1.1", + "react-redux": "^9.1.2", "react-render-if-visible": "^2.1.1", "react-use": "^17.5.0", "styled-components": "^5.3.9", @@ -50,9 +50,9 @@ "@types/jest": "^29.5.0", "@types/js-cookie": "^3.0.3", "@types/node": "^18.16.0", - "@types/react": "^18.2.0", + "@types/react": "^18.3.1", "@types/react-bootstrap": "^0.32.32", - "@types/react-dom": "^18.2.0", + "@types/react-dom": "^18.3.1", "@types/styled-components": "^5.1.26", "@typescript-eslint/eslint-plugin": "^5.57.0", "@typescript-eslint/parser": "^5.57.0", @@ -731,11 +731,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", - "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", + "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" @@ -920,21 +920,21 @@ } }, "node_modules/@hello-pangea/dnd": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-16.2.0.tgz", - "integrity": "sha512-inACvMcvvLr34CG0P6+G/3bprVKhwswxjcsFUSJ+fpOGjhvDj9caiA9X3clby0lgJ6/ILIJjyedHZYECB7GAgA==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-17.0.0.tgz", + "integrity": "sha512-LDDPOix/5N0j5QZxubiW9T0M0+1PR0rTDWeZF5pu1Tz91UQnuVK4qQ/EjY83Qm2QeX0eM8qDXANfDh3VVqtR4Q==", "dependencies": { - "@babel/runtime": "^7.19.4", + "@babel/runtime": "^7.25.6", "css-box-model": "^1.2.1", "memoize-one": "^6.0.0", "raf-schd": "^4.0.3", - "react-redux": "^8.0.4", - "redux": "^4.2.0", + "react-redux": "^9.1.2", + "redux": "^5.0.1", "use-memo-one": "^1.1.3" }, "peerDependencies": { - "react": "^16.8.5 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@humanwhocodes/config-array": { @@ -1732,18 +1732,18 @@ } }, "node_modules/@reduxjs/toolkit": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", - "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.3.0.tgz", + "integrity": "sha512-WC7Yd6cNGfHx8zf+iu+Q1UPTfEcXhQ+ATi7CV1hlrSAaQBdlPzg7Ww/wJHNQem7qG9rxmWoFCDCPubSvFObGzA==", "dependencies": { - "immer": "^9.0.21", - "redux": "^4.2.1", - "redux-thunk": "^2.4.2", - "reselect": "^4.1.8" + "immer": "^10.0.3", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18", - "react-redux": "^7.2.1 || ^8.0.2" + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "peerDependenciesMeta": { "react": { @@ -1754,6 +1754,14 @@ } } }, + "node_modules/@reduxjs/toolkit/node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "peerDependencies": { + "redux": "^5.0.0" + } + }, "node_modules/@restart/hooks": { "version": "0.4.9", "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.9.tgz", @@ -2290,6 +2298,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dev": true, "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -2395,12 +2404,11 @@ "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "node_modules/@types/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz", - "integrity": "sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz", + "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==", "dependencies": { "@types/prop-types": "*", - "@types/scheduler": "*", "csstype": "^3.0.2" } }, @@ -2414,10 +2422,10 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-8yQrvS6sMpSwIovhPOwfyNf2Wz6v/B62LFSVYQ85+Rq3tLsBIG7rP5geMxaijTUxSkrO6RzN/IRuIAADYQsleA==", - "devOptional": true, + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", + "dev": true, "dependencies": { "@types/react": "*" } @@ -2430,11 +2438,6 @@ "@types/react": "*" } }, - "node_modules/@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" - }, "node_modules/@types/semver": { "version": "7.3.13", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", @@ -6114,9 +6117,9 @@ } }, "node_modules/immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -9147,9 +9150,9 @@ } }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dependencies": { "loose-envify": "^1.1.0" }, @@ -9201,15 +9204,15 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.3.1" } }, "node_modules/react-dropdown-tree-select": { @@ -9260,48 +9263,27 @@ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, "node_modules/react-redux": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.1.tgz", - "integrity": "sha512-5W0QaKtEhj+3bC0Nj0NkqkhIv8gLADH/2kYFMTHxCVqQILiWzLv6MaLuV5wJU3BQEdHKzTfcvPN0WMS6SC1oyA==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz", + "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==", "dependencies": { - "@babel/runtime": "^7.12.1", - "@types/hoist-non-react-statics": "^3.3.1", "@types/use-sync-external-store": "^0.0.3", - "hoist-non-react-statics": "^3.3.2", - "react-is": "^18.0.0", "use-sync-external-store": "^1.0.0" }, "peerDependencies": { - "@types/react": "^16.8 || ^17.0 || ^18.0", - "@types/react-dom": "^16.8 || ^17.0 || ^18.0", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0", - "react-native": ">=0.59", - "redux": "^4 || ^5.0.0-beta.0" + "@types/react": "^18.2.25", + "react": "^18.0", + "redux": "^5.0.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true }, - "@types/react-dom": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - }, "redux": { "optional": true } } }, - "node_modules/react-redux/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, "node_modules/react-render-if-visible": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/react-render-if-visible/-/react-render-if-visible-2.1.1.tgz", @@ -9414,25 +9396,14 @@ } }, "node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "dependencies": { - "@babel/runtime": "^7.9.2" - } - }, - "node_modules/redux-thunk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", - "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", - "peerDependencies": { - "redux": "^4" - } + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" }, "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "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.4.3", @@ -9487,9 +9458,9 @@ "dev": true }, "node_modules/reselect": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", - "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" }, "node_modules/resize-observer-polyfill": { "version": "1.5.1", @@ -9753,9 +9724,9 @@ } }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "dependencies": { "loose-envify": "^1.1.0" } @@ -11865,11 +11836,11 @@ } }, "@babel/runtime": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", - "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", + "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", "requires": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" } }, "@babel/template": { @@ -12015,16 +11986,16 @@ "dev": true }, "@hello-pangea/dnd": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-16.2.0.tgz", - "integrity": "sha512-inACvMcvvLr34CG0P6+G/3bprVKhwswxjcsFUSJ+fpOGjhvDj9caiA9X3clby0lgJ6/ILIJjyedHZYECB7GAgA==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-17.0.0.tgz", + "integrity": "sha512-LDDPOix/5N0j5QZxubiW9T0M0+1PR0rTDWeZF5pu1Tz91UQnuVK4qQ/EjY83Qm2QeX0eM8qDXANfDh3VVqtR4Q==", "requires": { - "@babel/runtime": "^7.19.4", + "@babel/runtime": "^7.25.6", "css-box-model": "^1.2.1", "memoize-one": "^6.0.0", "raf-schd": "^4.0.3", - "react-redux": "^8.0.4", - "redux": "^4.2.0", + "react-redux": "^9.1.2", + "redux": "^5.0.1", "use-memo-one": "^1.1.3" } }, @@ -12612,14 +12583,22 @@ } }, "@reduxjs/toolkit": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", - "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.3.0.tgz", + "integrity": "sha512-WC7Yd6cNGfHx8zf+iu+Q1UPTfEcXhQ+ATi7CV1hlrSAaQBdlPzg7Ww/wJHNQem7qG9rxmWoFCDCPubSvFObGzA==", "requires": { - "immer": "^9.0.21", - "redux": "^4.2.1", - "redux-thunk": "^2.4.2", - "reselect": "^4.1.8" + "immer": "^10.0.3", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "dependencies": { + "redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "requires": {} + } } }, "@restart/hooks": { @@ -13005,6 +12984,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dev": true, "requires": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -13110,12 +13090,11 @@ "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "@types/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz", - "integrity": "sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz", + "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==", "requires": { "@types/prop-types": "*", - "@types/scheduler": "*", "csstype": "^3.0.2" } }, @@ -13129,10 +13108,10 @@ } }, "@types/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-8yQrvS6sMpSwIovhPOwfyNf2Wz6v/B62LFSVYQ85+Rq3tLsBIG7rP5geMxaijTUxSkrO6RzN/IRuIAADYQsleA==", - "devOptional": true, + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", + "dev": true, "requires": { "@types/react": "*" } @@ -13145,11 +13124,6 @@ "@types/react": "*" } }, - "@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" - }, "@types/semver": { "version": "7.3.13", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", @@ -15864,9 +15838,9 @@ "dev": true }, "immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==" + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==" }, "immutable": { "version": "4.3.0", @@ -18074,9 +18048,9 @@ } }, "react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "requires": { "loose-envify": "^1.1.0" } @@ -18111,12 +18085,12 @@ } }, "react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "requires": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.2" } }, "react-dropdown-tree-select": { @@ -18155,23 +18129,12 @@ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, "react-redux": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.1.tgz", - "integrity": "sha512-5W0QaKtEhj+3bC0Nj0NkqkhIv8gLADH/2kYFMTHxCVqQILiWzLv6MaLuV5wJU3BQEdHKzTfcvPN0WMS6SC1oyA==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz", + "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==", "requires": { - "@babel/runtime": "^7.12.1", - "@types/hoist-non-react-statics": "^3.3.1", "@types/use-sync-external-store": "^0.0.3", - "hoist-non-react-statics": "^3.3.2", - "react-is": "^18.0.0", "use-sync-external-store": "^1.0.0" - }, - "dependencies": { - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - } } }, "react-render-if-visible": { @@ -18261,23 +18224,14 @@ } }, "redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "requires": { - "@babel/runtime": "^7.9.2" - } - }, - "redux-thunk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", - "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", - "requires": {} + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" }, "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "regexp.prototype.flags": { "version": "1.4.3", @@ -18314,9 +18268,9 @@ "dev": true }, "reselect": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", - "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" }, "resize-observer-polyfill": { "version": "1.5.1", @@ -18480,9 +18434,9 @@ } }, "scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "requires": { "loose-envify": "^1.1.0" } diff --git a/frontend/package.json b/frontend/package.json index c2a9be50b..0d1b00109 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,9 +13,9 @@ "generate-schema-types": "npx json2ts -i \"../common/schemas/*.json\" --cwd ../common/schemas > src/common/schema_types.ts" }, "dependencies": { - "@hello-pangea/dnd": "^16.2.0", + "@hello-pangea/dnd": "^17.0.0", "@popperjs/core": "^2.11.6", - "@reduxjs/toolkit": "^1.9.5", + "@reduxjs/toolkit": "^2.3.0", "@types/ua-parser-js": "^0.7.36", "ajv": "^8.12.0", "async-await-queue": "^2.1.4", @@ -30,13 +30,13 @@ "next": "^13.5.4", "nextjs-google-analytics": "^2.3.3", "ping.js": "^0.3.0", - "react": "18.2.0", + "react": "^18.3.1", "react-bootstrap": "^2.7.2", "react-bootstrap-toggle": "^2.3.2", - "react-dom": "18.2.0", + "react-dom": "^18.3.1", "react-dropdown-tree-select": "^2.8.0", "react-dropzone": "^14.2.3", - "react-redux": "^8.1.1", + "react-redux": "^9.1.2", "react-render-if-visible": "^2.1.1", "react-use": "^17.5.0", "styled-components": "^5.3.9", @@ -54,9 +54,9 @@ "@types/jest": "^29.5.0", "@types/js-cookie": "^3.0.3", "@types/node": "^18.16.0", - "@types/react": "^18.2.0", + "@types/react": "^18.3.1", "@types/react-bootstrap": "^0.32.32", - "@types/react-dom": "^18.2.0", + "@types/react-dom": "^18.3.1", "@types/styled-components": "^5.1.26", "@typescript-eslint/eslint-plugin": "^5.57.0", "@typescript-eslint/parser": "^5.57.0", diff --git a/frontend/src/app/listenerMiddleware.ts b/frontend/src/app/listenerMiddleware.ts index 4704149d9..cbf4e9094 100644 --- a/frontend/src/app/listenerMiddleware.ts +++ b/frontend/src/app/listenerMiddleware.ts @@ -2,7 +2,6 @@ * Retrieved from https://redux-toolkit.js.org/api/createListenerMiddleware */ -import type { TypedAddListener, TypedStartListening } from "@reduxjs/toolkit"; import { addListener, createListenerMiddleware, @@ -25,7 +24,6 @@ import { } from "@/features/invalidIdentifiers/invalidIdentifiersSlice"; import { addMembers, - clearQueries, selectProjectCardback, setQueries, setSelectedCardback, @@ -55,12 +53,12 @@ import type { AppDispatch, RootState } from "./store"; export const listenerMiddleware = createListenerMiddleware(); -export type AppStartListening = TypedStartListening; +const startAppListening = listenerMiddleware.startListening.withTypes< + RootState, + AppDispatch +>(); -const startAppListening = - listenerMiddleware.startListening as AppStartListening; - -const addAppListener = addListener as TypedAddListener; +const addAppListener = addListener.withTypes(); //# endregion @@ -128,7 +126,7 @@ startAppListening({ const isBackendConfigured = selectBackendConfigured(state); const searchSettingsSourcesValid = selectSearchSettingsSourcesValid(state); if (isBackendConfigured && searchSettingsSourcesValid) { - await dispatch(clearSearchResults()); + dispatch(clearSearchResults()); await fetchCardDocumentsAndReportError(dispatch); } }, @@ -182,18 +180,22 @@ startAppListening({ */ // wait for all search results to load (removing this will cause a race condition) - await condition((action, currentState) => { - const { slots }: { slots: Array<[Faces, number]> } = action.payload; - return slots - .map(([face, slot]) => { - const searchQuery = currentState.project.members[slot][face]?.query; - return searchQuery?.query != null - ? currentState.searchResults.searchResults[searchQuery.query][ - searchQuery.card_type - ] != null - : true; - }) - .every((value) => value); + await condition((action, currentState): boolean => { + if (setQueries.match(action)) { + const { slots }: { slots: Array<[Faces, number]> } = action.payload; + return slots + .map(([face, slot]) => { + const searchQuery = currentState.project.members[slot][face]?.query; + return searchQuery?.query != null + ? currentState.searchResults.searchResults[searchQuery.query][ + searchQuery.card_type + ] != null + : true; + }) + .every((value) => value); + } else { + return true; + } }); const state = getState(); diff --git a/frontend/src/app/store.ts b/frontend/src/app/store.ts index 86c8bcbc3..4544b0b6d 100644 --- a/frontend/src/app/store.ts +++ b/frontend/src/app/store.ts @@ -1,7 +1,10 @@ -import type { PreloadedState } from "@reduxjs/toolkit"; -import type { Middleware, MiddlewareAPI } from "@reduxjs/toolkit"; -import { combineReducers, configureStore } from "@reduxjs/toolkit"; -import { isRejectedWithValue } from "@reduxjs/toolkit"; +import { isAction, MiddlewareAPI } from "@reduxjs/toolkit"; +import { + combineReducers, + configureStore, + isRejectedWithValue, + Tuple, +} from "@reduxjs/toolkit"; import { api } from "@/app/api"; import { listenerMiddleware } from "@/app/listenerMiddleware"; @@ -38,25 +41,36 @@ const rootReducer = combineReducers({ //# region middleware -const rtkQueryErrorLogger: Middleware = - (api: MiddlewareAPI) => (next) => (action) => { +const rtkQueryErrorLogger = + (api: MiddlewareAPI) => + (next: (action: unknown) => unknown) => + (action: unknown) => { /** * Whenever a RTK Query API request fails, display the response's error message to the user as a toast. */ + if (!isAction(action)) { + return; + } + const backendConfigured = selectBackendConfigured(api.getState()); if ( backendConfigured && isRejectedWithValue(action) && - action.payload?.data != null + action.payload != null ) { // dispatch the error to the store for displaying in a toast to the user + const data = ( + action.payload as { + data?: { name?: string | null; message?: string | null }; + } + )?.data; api.dispatch( setNotification([ action.type, { - name: action.payload.data.name, - message: action.payload.data.message, + name: data?.name ?? null, + message: data?.message ?? null, level: "error", }, ]) @@ -68,20 +82,21 @@ const rtkQueryErrorLogger: Middleware = //# endregion -export const setupStore = (preloadedState?: PreloadedState) => { +export const setupStore = (preloadedState?: Partial) => { return configureStore({ reducer: rootReducer, preloadedState, middleware: (getDefaultMiddleware) => getDefaultMiddleware() .prepend(listenerMiddleware.middleware) - .concat([api.middleware, rtkQueryErrorLogger]), + .concat(new Tuple(api.middleware, rtkQueryErrorLogger)), }); }; const store = setupStore(); +export type AppStore = ReturnType; export type RootState = ReturnType; -export type AppDispatch = typeof store.dispatch; +export type AppDispatch = AppStore["dispatch"]; export default store; diff --git a/frontend/src/common/test-utils.tsx b/frontend/src/common/test-utils.tsx index 50dc8ef6a..fefadbc62 100644 --- a/frontend/src/common/test-utils.tsx +++ b/frontend/src/common/test-utils.tsx @@ -3,7 +3,6 @@ * Retrieved from https://redux.js.org/usage/writing-tests */ -import type { PreloadedState } from "@reduxjs/toolkit"; import { Store } from "@reduxjs/toolkit"; import { within } from "@testing-library/dom"; import type { RenderOptions } from "@testing-library/react"; @@ -24,7 +23,7 @@ import { LayoutWithoutReduxProvider } from "@/features/ui/layout"; // This type interface extends the default options for render from RTL, as well // as allows the user to specify other things such as initialState, store. interface ExtendedRenderOptions extends Omit { - preloadedState?: PreloadedState; + preloadedState?: Partial; store?: Store; } diff --git a/frontend/src/common/types.ts b/frontend/src/common/types.ts index f98397a21..795e879f8 100644 --- a/frontend/src/common/types.ts +++ b/frontend/src/common/types.ts @@ -1,7 +1,11 @@ -import { createAsyncThunk } from "@reduxjs/toolkit"; -import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; - -import type { AppDispatch, RootState } from "@/app/store"; +import { + asyncThunkCreator, + buildCreateSlice, + createAsyncThunk, +} from "@reduxjs/toolkit"; +import { useDispatch, useSelector, useStore } from "react-redux"; + +import type { AppDispatch, AppStore, RootState } from "@/app/store"; import { CSVHeaders } from "@/common/constants"; import { SearchQuery } from "@/common/schema_types"; export type { @@ -13,15 +17,17 @@ export type { SourceSettings, } from "@/common/schema_types"; -type DispatchFunc = () => AppDispatch; -export const useAppDispatch: DispatchFunc = useDispatch; -export const useAppSelector: TypedUseSelectorHook = useSelector; +export const useAppDispatch = useDispatch.withTypes(); +export const useAppSelector = useSelector.withTypes(); +export const useAppStore = useStore.withTypes(); +export const createAppSlice = buildCreateSlice({ + creators: { asyncThunk: asyncThunkCreator }, +}); export const createAppAsyncThunk = createAsyncThunk.withTypes<{ state: RootState; dispatch: AppDispatch; rejectValue: string; - extra: { s: string; n: number }; }>(); export type ThunkStatus = "idle" | "loading" | "succeeded" | "failed"; diff --git a/frontend/src/features/backend/backendSlice.ts b/frontend/src/features/backend/backendSlice.ts index 345d39d98..149582307 100644 --- a/frontend/src/features/backend/backendSlice.ts +++ b/frontend/src/features/backend/backendSlice.ts @@ -2,12 +2,10 @@ * State management for the backend that the app should communicate with as configured by the user. */ -import { createSlice } from "@reduxjs/toolkit"; - import { useGetBackendInfoQuery } from "@/app/api"; import { RootState } from "@/app/store"; import { ProjectName } from "@/common/constants"; -import { BackendState, useAppSelector } from "@/common/types"; +import { BackendState, createAppSlice, useAppSelector } from "@/common/types"; //# region slice configuration @@ -15,7 +13,7 @@ const initialState: BackendState = { url: null, }; -export const backendSlice = createSlice({ +export const backendSlice = createAppSlice({ name: "backend", initialState, reducers: { diff --git a/frontend/src/features/card/cardbackSlice.ts b/frontend/src/features/card/cardbackSlice.ts index 7e1b52b82..88dd7fa47 100644 --- a/frontend/src/features/card/cardbackSlice.ts +++ b/frontend/src/features/card/cardbackSlice.ts @@ -2,11 +2,13 @@ * State management for cardbacks retrieved from the backend. */ -import { createSlice } from "@reduxjs/toolkit"; - import { APIGetCardbacks } from "@/app/api"; import { AppDispatch, RootState } from "@/app/store"; -import { CardbacksState, createAppAsyncThunk } from "@/common/types"; +import { + CardbacksState, + createAppAsyncThunk, + createAppSlice, +} from "@/common/types"; import { selectBackendURL } from "@/features/backend/backendSlice"; import { selectSearchSettings } from "@/features/searchSettings/searchSettingsSlice"; import { setNotification } from "@/features/toasts/toastsSlice"; @@ -51,7 +53,7 @@ const initialState: CardbacksState = { error: null, }; -export const cardbackSlice = createSlice({ +export const cardbackSlice = createAppSlice({ name: "cardbacks", initialState, reducers: { @@ -59,7 +61,7 @@ export const cardbackSlice = createSlice({ state.cardbacks = [...action.payload]; }, }, - extraReducers(builder) { + extraReducers: (builder) => { builder .addCase(fetchCardbacks.pending, (state, action) => { state.status = "loading"; diff --git a/frontend/src/features/export/exportDecklist.tsx b/frontend/src/features/export/exportDecklist.tsx index 333da62bc..666b20a53 100644 --- a/frontend/src/features/export/exportDecklist.tsx +++ b/frontend/src/features/export/exportDecklist.tsx @@ -6,7 +6,6 @@ import { saveAs } from "file-saver"; import React from "react"; import Dropdown from "react-bootstrap/Dropdown"; -import { useStore } from "react-redux"; import { RootState } from "@/app/store"; import { Back, Card, FaceSeparator, Front } from "@/common/constants"; @@ -16,6 +15,7 @@ import { ProjectMember, SlotProjectMembers, useAppSelector, + useAppStore, } from "@/common/types"; import { RightPaddedIcon } from "@/components/icon"; import { @@ -120,7 +120,7 @@ const selectGeneratedDecklist = (state: RootState): string => { export function ExportDecklist() { //# region queries and hooks - const store = useStore(); + const store = useAppStore(); const isProjectEmpty = useAppSelector(selectIsProjectEmpty); //# endregion @@ -128,9 +128,7 @@ export function ExportDecklist() { //# region callbacks const downloadFile = () => { - const generatedDecklist = selectGeneratedDecklist( - store.getState() as RootState - ); + const generatedDecklist = selectGeneratedDecklist(store.getState()); saveAs( new Blob([generatedDecklist], { type: "text/plain;charset=utf-8" }), "decklist.txt" // TODO: use project name here when we eventually track that diff --git a/frontend/src/features/export/exportXML.tsx b/frontend/src/features/export/exportXML.tsx index 77b22d404..ec2bfd13c 100644 --- a/frontend/src/features/export/exportXML.tsx +++ b/frontend/src/features/export/exportXML.tsx @@ -7,11 +7,11 @@ import { saveAs } from "file-saver"; import React from "react"; import Dropdown from "react-bootstrap/Dropdown"; -import { useStore } from "react-redux"; import formatXML from "xml-formatter"; import { RootState } from "@/app/store"; import { Back, Front, ReversedCardTypePrefixes } from "@/common/constants"; +import { useAppStore } from "@/common/types"; import { CardDocuments, FinishSettingsState, @@ -208,10 +208,10 @@ export function generateXML( } export function useExportXML() { - const store = useStore(); + const store = useAppStore(); return () => { - const generatedXML = selectGeneratedXML(store.getState() as RootState); + const generatedXML = selectGeneratedXML(store.getState()); saveAs( new Blob([generatedXML], { type: "text/xml;charset=utf-8" }), "cards.xml" diff --git a/frontend/src/features/finishSettings/finishSettingsSlice.ts b/frontend/src/features/finishSettings/finishSettingsSlice.ts index 105c2c985..ce5938841 100644 --- a/frontend/src/features/finishSettings/finishSettingsSlice.ts +++ b/frontend/src/features/finishSettings/finishSettingsSlice.ts @@ -1,8 +1,8 @@ -import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { PayloadAction } from "@reduxjs/toolkit"; import { RootState } from "@/app/store"; import { CardstockFoilCompatibility } from "@/common/constants"; -import { Cardstock, FinishSettingsState } from "@/common/types"; +import { Cardstock, createAppSlice, FinishSettingsState } from "@/common/types"; //# region slice configuration @@ -11,7 +11,7 @@ const initialState: FinishSettingsState = { foil: false, }; -export const finishSettingsSlice = createSlice({ +export const finishSettingsSlice = createAppSlice({ name: "finishSettings", initialState, reducers: { diff --git a/frontend/src/features/invalidIdentifiers/invalidIdentifiersSlice.ts b/frontend/src/features/invalidIdentifiers/invalidIdentifiersSlice.ts index a9ef758f9..39a3b5683 100644 --- a/frontend/src/features/invalidIdentifiers/invalidIdentifiersSlice.ts +++ b/frontend/src/features/invalidIdentifiers/invalidIdentifiersSlice.ts @@ -1,13 +1,18 @@ -import { createSelector, createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { createSelector, PayloadAction } from "@reduxjs/toolkit"; import { RootState } from "@/app/store"; -import { Faces, InvalidIdentifiersState, SearchQuery } from "@/common/types"; +import { + createAppSlice, + Faces, + InvalidIdentifiersState, + SearchQuery, +} from "@/common/types"; //# region slice configuration const initialState: InvalidIdentifiersState = { invalidIdentifiers: [] }; -export const invalidIdentifiersSlice = createSlice({ +export const invalidIdentifiersSlice = createAppSlice({ name: "invalidIdentifiers", initialState, reducers: { diff --git a/frontend/src/features/modals/modalsSlice.ts b/frontend/src/features/modals/modalsSlice.ts index 475b2d701..7d2cfea26 100644 --- a/frontend/src/features/modals/modalsSlice.ts +++ b/frontend/src/features/modals/modalsSlice.ts @@ -1,13 +1,19 @@ -import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { PayloadAction } from "@reduxjs/toolkit"; import { RootState } from "@/app/store"; -import { CardDocument, Modals, ModalsState, Slots } from "@/common/types"; +import { + CardDocument, + createAppSlice, + Modals, + ModalsState, + Slots, +} from "@/common/types"; //# region slice configuration const initialState: ModalsState = { card: null, slots: null, shownModal: null }; -export const modalsSlice = createSlice({ +export const modalsSlice = createAppSlice({ name: "modals", initialState, reducers: { diff --git a/frontend/src/features/project/projectSlice.ts b/frontend/src/features/project/projectSlice.ts index 095378108..9725909d6 100644 --- a/frontend/src/features/project/projectSlice.ts +++ b/frontend/src/features/project/projectSlice.ts @@ -2,13 +2,14 @@ * State management for the user's configuration of the project - selected cards and cardbacks. */ -import { createSelector, createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { createSelector, PayloadAction } from "@reduxjs/toolkit"; import { RootState } from "@/app/store"; import { Card, Cardback } from "@/common/constants"; import { Back, Front, ProjectMaxSize } from "@/common/constants"; import { processPrefix } from "@/common/processing"; import { + createAppSlice, Faces, Project, ProjectMember, @@ -25,7 +26,7 @@ const initialState: Project = { mostRecentlySelectedSlot: null, }; -export const projectSlice = createSlice({ +export const projectSlice = createAppSlice({ name: "project", initialState, reducers: { diff --git a/frontend/src/features/search/cardDocumentsSlice.ts b/frontend/src/features/search/cardDocumentsSlice.ts index d6945a11c..d6546805f 100644 --- a/frontend/src/features/search/cardDocumentsSlice.ts +++ b/frontend/src/features/search/cardDocumentsSlice.ts @@ -2,7 +2,7 @@ * State management for cards retrieved from the backend. */ -import { createSelector, createSlice } from "@reduxjs/toolkit"; +import { createSelector } from "@reduxjs/toolkit"; import { APIGetCards } from "@/app/api"; import { AppDispatch, RootState } from "@/app/store"; @@ -11,6 +11,7 @@ import { CardDocument, CardDocumentsState, createAppAsyncThunk, + createAppSlice, useAppSelector, } from "@/common/types"; import { CardDocuments } from "@/common/types"; @@ -37,7 +38,7 @@ const fetchCardDocuments = createAppAsyncThunk( await fetchSearchResultsAndReportError(dispatch); await fetchCardbacksAndReportError(dispatch); - const state: RootState = getState(); + const state = getState(); const allIdentifiers = selectUniqueCardIdentifiers(state); const identifiersWithKnownData = new Set( @@ -103,7 +104,7 @@ const initialState: CardDocumentsState = { error: null, }; -export const cardDocumentsSlice = createSlice({ +export const cardDocumentsSlice = createAppSlice({ name: "cardDocuments", initialState, reducers: { @@ -111,7 +112,7 @@ export const cardDocumentsSlice = createSlice({ state.cardDocuments = { ...state.cardDocuments, ...action.payload }; }, }, - extraReducers(builder) { + extraReducers: (builder) => { builder .addCase(fetchCardDocuments.pending, (state, action) => { state.status = "loading"; diff --git a/frontend/src/features/search/searchResultsSlice.ts b/frontend/src/features/search/searchResultsSlice.ts index 82ea5e2c9..e6df335ab 100644 --- a/frontend/src/features/search/searchResultsSlice.ts +++ b/frontend/src/features/search/searchResultsSlice.ts @@ -2,13 +2,12 @@ * State management for search results - what images are returned for what search queries. */ -import { createSlice } from "@reduxjs/toolkit"; - import { APISearch } from "@/app/api"; import { AppDispatch, RootState } from "@/app/store"; import { Back, SearchResultsEndpointPageSize } from "@/common/constants"; import { createAppAsyncThunk, + createAppSlice, Faces, SearchQuery, SearchResults, @@ -25,7 +24,7 @@ const typePrefix = "searchResults/fetchCards"; export const fetchSearchResults = createAppAsyncThunk( typePrefix, - async (arg, { getState, rejectWithValue }) => { + async (arg, { getState }) => { const state = getState(); const queriesToSearch = selectQueriesWithoutSearchResults(state); @@ -80,7 +79,7 @@ const initialState: SearchResultsState = { error: null, }; -export const searchResultsSlice = createSlice({ +export const searchResultsSlice = createAppSlice({ name: "searchResults", initialState, reducers: { @@ -91,7 +90,7 @@ export const searchResultsSlice = createSlice({ state.searchResults = {}; }, }, - extraReducers(builder) { + extraReducers: (builder) => { builder .addCase(fetchSearchResults.pending, (state, action) => { state.status = "loading"; diff --git a/frontend/src/features/search/sourceDocumentsSlice.ts b/frontend/src/features/search/sourceDocumentsSlice.ts index 1925825bd..0d81dce44 100644 --- a/frontend/src/features/search/sourceDocumentsSlice.ts +++ b/frontend/src/features/search/sourceDocumentsSlice.ts @@ -2,11 +2,15 @@ * State management for sources retrieved from the backend. */ -import { createSelector, createSlice } from "@reduxjs/toolkit"; +import { createSelector } from "@reduxjs/toolkit"; import { APIGetSources } from "@/app/api"; import { AppDispatch, RootState } from "@/app/store"; -import { createAppAsyncThunk, SourceDocumentsState } from "@/common/types"; +import { + createAppAsyncThunk, + createAppSlice, + SourceDocumentsState, +} from "@/common/types"; import { setNotification } from "@/features/toasts/toastsSlice"; //# region async thunk @@ -45,7 +49,7 @@ const initialState = { sourceDocuments: undefined, } as SourceDocumentsState; -export const sourceDocumentsSlice = createSlice({ +export const sourceDocumentsSlice = createAppSlice({ name: "sourceDocuments", initialState, reducers: { @@ -53,7 +57,7 @@ export const sourceDocumentsSlice = createSlice({ state.sourceDocuments = { ...state.sourceDocuments, ...action.payload }; }, }, - extraReducers(builder) { + extraReducers: (builder) => { builder .addCase(fetchSourceDocuments.pending, (state, action) => { state.status = "loading"; diff --git a/frontend/src/features/searchSettings/searchSettingsSlice.ts b/frontend/src/features/searchSettings/searchSettingsSlice.ts index 12419c863..e9886cb40 100644 --- a/frontend/src/features/searchSettings/searchSettingsSlice.ts +++ b/frontend/src/features/searchSettings/searchSettingsSlice.ts @@ -1,8 +1,7 @@ -import { createSlice } from "@reduxjs/toolkit"; - import { RootState } from "@/app/store"; import { MaximumDPI, MaximumSize, MinimumDPI } from "@/common/constants"; import { + createAppSlice, SearchSettings, SourceDocument, SourceDocuments, @@ -33,7 +32,7 @@ export function getDefaultSearchSettings( const initialState = getDefaultSearchSettings({}); -export const searchSettingsSlice = createSlice({ +export const searchSettingsSlice = createAppSlice({ name: "searchSettings", initialState, reducers: { diff --git a/frontend/src/features/toasts/toastsSlice.ts b/frontend/src/features/toasts/toastsSlice.ts index 4c0824ffc..33708b414 100644 --- a/frontend/src/features/toasts/toastsSlice.ts +++ b/frontend/src/features/toasts/toastsSlice.ts @@ -1,14 +1,14 @@ -import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { PayloadAction } from "@reduxjs/toolkit"; import { RootState } from "@/app/store"; -import { Notification } from "@/common/types"; +import { createAppSlice, Notification } from "@/common/types"; import { ToastsState } from "@/common/types"; //# region slice configuration const initialState: ToastsState = { notifications: {} }; -export const toastsSlice = createSlice({ +export const toastsSlice = createAppSlice({ name: "toasts", initialState, reducers: { diff --git a/frontend/src/features/viewSettings/viewSettingsSlice.ts b/frontend/src/features/viewSettings/viewSettingsSlice.ts index 60c5e8b98..63d744b2c 100644 --- a/frontend/src/features/viewSettings/viewSettingsSlice.ts +++ b/frontend/src/features/viewSettings/viewSettingsSlice.ts @@ -1,8 +1,8 @@ -import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { PayloadAction } from "@reduxjs/toolkit"; import { RootState } from "@/app/store"; import { Back, Front } from "@/common/constants"; -import { Faces, ViewSettingsState } from "@/common/types"; +import { createAppSlice, Faces, ViewSettingsState } from "@/common/types"; //# region slice configuration @@ -13,7 +13,7 @@ const initialState: ViewSettingsState = { jumpToVersionVisible: false, }; -export const viewSettingsSlice = createSlice({ +export const viewSettingsSlice = createAppSlice({ name: "viewSettings", initialState, reducers: {