feat: add redux store
This commit is contained in:
parent
a43bbe719a
commit
13a378b45c
245
package-lock.json
generated
245
package-lock.json
generated
@ -8,11 +8,13 @@
|
||||
"name": "aurawebapp",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "^1.9.5",
|
||||
"i18next": "^22.4.15",
|
||||
"react": "^18.2.0",
|
||||
"react-circular-progressbar": "^2.1.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-i18next": "^12.2.2",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-router-dom": "^6.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -946,6 +948,29 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"dependencies": {
|
||||
"immer": "^9.0.21",
|
||||
"redux": "^4.2.1",
|
||||
"redux-thunk": "^2.4.2",
|
||||
"reselect": "^4.1.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.9.0 || ^17.0.0 || ^18",
|
||||
"react-redux": "^7.2.1 || ^8.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-redux": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.0.tgz",
|
||||
@ -960,6 +985,15 @@
|
||||
"integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/hoist-non-react-statics": {
|
||||
"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==",
|
||||
"dependencies": {
|
||||
"@types/react": "*",
|
||||
"hoist-non-react-statics": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
|
||||
@ -969,14 +1003,12 @@
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
|
||||
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
|
||||
"dev": true
|
||||
"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==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "*",
|
||||
@ -987,7 +1019,7 @@
|
||||
"version": "18.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.1.tgz",
|
||||
"integrity": "sha512-8QZEV9+Kwy7tXFmjJrp3XUKQSs9LTnE0KnoUb0YCguWBiNW0Yfb2iBMYZ08WPg35IR6P3Z0s00B15SwZnO26+w==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
@ -1016,8 +1048,7 @@
|
||||
"node_modules/@types/scheduler": {
|
||||
"version": "0.16.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
|
||||
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.3.13",
|
||||
@ -1025,6 +1056,11 @@
|
||||
"integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/use-sync-external-store": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
|
||||
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz",
|
||||
@ -1447,8 +1483,7 @@
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
|
||||
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
@ -2079,6 +2114,19 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/hoist-non-react-statics": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||
"dependencies": {
|
||||
"react-is": "^16.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hoist-non-react-statics/node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"node_modules/html-parse-stringify": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
||||
@ -2118,6 +2166,15 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/immer": {
|
||||
"version": "9.0.21",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
|
||||
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/immer"
|
||||
}
|
||||
},
|
||||
"node_modules/import-fresh": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
@ -2637,6 +2694,49 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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-redux": {
|
||||
"version": "8.0.5",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz",
|
||||
"integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==",
|
||||
"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"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"react-native": {
|
||||
"optional": true
|
||||
},
|
||||
"redux": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-refresh": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
|
||||
@ -2676,11 +2776,32 @@
|
||||
"react-dom": ">=16.8"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"node_modules/reselect": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz",
|
||||
"integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="
|
||||
},
|
||||
"node_modules/resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
@ -2994,6 +3115,14 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.3.tgz",
|
||||
@ -3686,6 +3815,17 @@
|
||||
"fastq": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"@reduxjs/toolkit": {
|
||||
"version": "1.9.5",
|
||||
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz",
|
||||
"integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==",
|
||||
"requires": {
|
||||
"immer": "^9.0.21",
|
||||
"redux": "^4.2.1",
|
||||
"redux-thunk": "^2.4.2",
|
||||
"reselect": "^4.1.8"
|
||||
}
|
||||
},
|
||||
"@remix-run/router": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.0.tgz",
|
||||
@ -3697,6 +3837,15 @@
|
||||
"integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/hoist-non-react-statics": {
|
||||
"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==",
|
||||
"requires": {
|
||||
"@types/react": "*",
|
||||
"hoist-non-react-statics": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"@types/json-schema": {
|
||||
"version": "7.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
|
||||
@ -3706,14 +3855,12 @@
|
||||
"@types/prop-types": {
|
||||
"version": "15.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
|
||||
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
|
||||
"dev": true
|
||||
"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==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "*",
|
||||
@ -3724,7 +3871,7 @@
|
||||
"version": "18.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.1.tgz",
|
||||
"integrity": "sha512-8QZEV9+Kwy7tXFmjJrp3XUKQSs9LTnE0KnoUb0YCguWBiNW0Yfb2iBMYZ08WPg35IR6P3Z0s00B15SwZnO26+w==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
@ -3753,8 +3900,7 @@
|
||||
"@types/scheduler": {
|
||||
"version": "0.16.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
|
||||
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
|
||||
},
|
||||
"@types/semver": {
|
||||
"version": "7.3.13",
|
||||
@ -3762,6 +3908,11 @@
|
||||
"integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/use-sync-external-store": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
|
||||
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz",
|
||||
@ -4026,8 +4177,7 @@
|
||||
"csstype": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
|
||||
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
@ -4496,6 +4646,21 @@
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true
|
||||
},
|
||||
"hoist-non-react-statics": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||
"requires": {
|
||||
"react-is": "^16.7.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"html-parse-stringify": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
||||
@ -4518,6 +4683,11 @@
|
||||
"integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"immer": {
|
||||
"version": "9.0.21",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
|
||||
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA=="
|
||||
},
|
||||
"import-fresh": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
@ -4871,6 +5041,24 @@
|
||||
"html-parse-stringify": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"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-redux": {
|
||||
"version": "8.0.5",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz",
|
||||
"integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"react-refresh": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
|
||||
@ -4894,11 +5082,30 @@
|
||||
"react-router": "6.11.0"
|
||||
}
|
||||
},
|
||||
"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": {}
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"reselect": {
|
||||
"version": "4.1.8",
|
||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz",
|
||||
"integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="
|
||||
},
|
||||
"resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
@ -5099,6 +5306,12 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||
"requires": {}
|
||||
},
|
||||
"vite": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.3.tgz",
|
||||
|
||||
@ -10,11 +10,13 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "^1.9.5",
|
||||
"i18next": "^22.4.15",
|
||||
"react": "^18.2.0",
|
||||
"react-circular-progressbar": "^2.1.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-i18next": "^12.2.2",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-router-dom": "^6.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
3
src/auth/AuthContext.ts
Normal file
3
src/auth/AuthContext.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { createContext } from 'react'
|
||||
|
||||
export const AuthContext = createContext<unknown>({})
|
||||
10
src/auth/AuthProvider.tsx
Normal file
10
src/auth/AuthProvider.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { AuthContext } from './AuthContext'
|
||||
|
||||
export function AuthProvider({ children }: React.PropsWithChildren<unknown>): JSX.Element {
|
||||
const auth = {}
|
||||
return (
|
||||
<AuthContext.Provider value={auth}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
)
|
||||
}
|
||||
3
src/auth/index.ts
Normal file
3
src/auth/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './useAuth'
|
||||
export * from './AuthContext'
|
||||
export * from './AuthProvider'
|
||||
4
src/auth/useAuth.ts
Normal file
4
src/auth/useAuth.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { useContext } from 'react'
|
||||
import { AuthContext } from './AuthContext'
|
||||
|
||||
export const useAuth = () => useContext(AuthContext)
|
||||
@ -1,4 +1,4 @@
|
||||
import { Routes, Route, Navigate } from 'react-router-dom';
|
||||
import { Routes, Route, Navigate } from 'react-router-dom'
|
||||
import BirthdayPage from '../BirthdayPage'
|
||||
import BirthtimePage from '../BirthtimePage'
|
||||
import CreateProfilePage from '../CreateProfilePage'
|
||||
@ -9,7 +9,7 @@ import Header from '../Header'
|
||||
import routes from '../../routes'
|
||||
import './styles.css'
|
||||
|
||||
function App() {
|
||||
function App(): JSX.Element {
|
||||
return (
|
||||
<div className='container'>
|
||||
<Header />
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { actions, RootState } from '../../store'
|
||||
import routes from '../../routes'
|
||||
import Policy from '../Policy'
|
||||
import Purposes from '../Purposes'
|
||||
@ -11,8 +13,9 @@ import './styles.css'
|
||||
|
||||
function BirthdayPage(): JSX.Element {
|
||||
const { t } = useTranslation()
|
||||
const dispatch = useDispatch()
|
||||
const navigate = useNavigate()
|
||||
const [birthdate, setBirthdate] = useState('')
|
||||
const birthdate = useSelector((state: RootState) => state.birthdate)
|
||||
const [isDisabled, setIsDisabled] = useState(true)
|
||||
const links = [
|
||||
{ text: 'EULA', href: 'https://aura.wit.life/terms' },
|
||||
@ -21,14 +24,10 @@ function BirthdayPage(): JSX.Element {
|
||||
]
|
||||
const handleNext = () => navigate(routes.client.birthtime())
|
||||
const handleValid = (birthdate: string) => {
|
||||
setBirthdate(birthdate)
|
||||
dispatch(actions.birthdate.update(birthdate))
|
||||
setIsDisabled(false)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log('birthdate', birthdate)
|
||||
}, [birthdate])
|
||||
|
||||
return (
|
||||
<section className='page'>
|
||||
<Title variant='h3' className='mt-24'>{t('letsStart')}</Title>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from "react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { actions, RootState } from '../../store'
|
||||
import Title from "../Title"
|
||||
import MainButton from "../MainButton"
|
||||
import { TimePicker } from "../DateTimePicker"
|
||||
@ -9,26 +10,17 @@ import './styles.css'
|
||||
|
||||
function BirthtimePage(): JSX.Element {
|
||||
const { t } = useTranslation()
|
||||
const dispatch = useDispatch()
|
||||
const navigate = useNavigate();
|
||||
const [birthtime, setBirhtime] = useState('')
|
||||
const [isDisabled, setIsDisabled] = useState(true)
|
||||
const birthtime = useSelector((state: RootState) => state.birthtime)
|
||||
const handleNext = () => navigate(routes.client.createProfile())
|
||||
|
||||
useEffect(() => {
|
||||
if (!birthtime) {
|
||||
setIsDisabled(true)
|
||||
return
|
||||
}
|
||||
setIsDisabled(false)
|
||||
console.log('birthtime', birthtime)
|
||||
}, [birthtime])
|
||||
|
||||
const handleChange = (value: string) => dispatch(actions.birthtime.update(value))
|
||||
return (
|
||||
<section className='page'>
|
||||
<Title variant="h2" className="mt-24">{t('bornTimeQuestion')}</Title>
|
||||
<p className="description">{t('nasaDataUsing')}</p>
|
||||
<TimePicker onChange={(value: string) => setBirhtime(value)}/>
|
||||
<MainButton label={t('next')} onClick={handleNext} disabled={isDisabled}/>
|
||||
<TimePicker value={birthtime} onChange={handleChange}/>
|
||||
<MainButton label={t('next')} onClick={handleNext} disabled={!birthtime}/>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import { normalize } from "./utils"
|
||||
import { normalize, parse, format } from "./utils"
|
||||
|
||||
type TimePickerProps = {
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
}
|
||||
|
||||
export function TimePicker({ onChange }: TimePickerProps): JSX.Element {
|
||||
const [hour, setHour] = useState('1')
|
||||
const [minute, setMinute] = useState('0')
|
||||
const [period, setPeriod] = useState('AM')
|
||||
export function TimePicker({ value, onChange }: TimePickerProps): JSX.Element {
|
||||
const parsedValue = parse(value)
|
||||
const [hour, setHour] = useState(parsedValue.hour)
|
||||
const [minute, setMinute] = useState(parsedValue.minute)
|
||||
const [period, setPeriod] = useState(parsedValue.period)
|
||||
|
||||
useEffect(() => {
|
||||
const formattedHour = period === 'AM' ? normalize(hour, 2) : String(Number(hour) + 12)
|
||||
const formattedMinute = normalize(minute, 2)
|
||||
onChange(`${formattedHour}:${formattedMinute}`)
|
||||
onChange(format(hour, minute, period))
|
||||
}, [hour, minute, period, onChange])
|
||||
|
||||
return (
|
||||
@ -22,6 +22,7 @@ export function TimePicker({ onChange }: TimePickerProps): JSX.Element {
|
||||
<div className="date-picker__field">
|
||||
<select
|
||||
className="date-picker__field-select"
|
||||
value={hour}
|
||||
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => setHour(e.target.value)}>
|
||||
{Array.from(Array(12).keys()).map((hour) => (
|
||||
<option key={hour} value={hour + 1}>{hour + 1}</option>
|
||||
@ -31,10 +32,11 @@ export function TimePicker({ onChange }: TimePickerProps): JSX.Element {
|
||||
<div className="date-picker__field">
|
||||
<select
|
||||
className="date-picker__field-select"
|
||||
value={minute}
|
||||
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => setMinute(e.target.value)}>
|
||||
{Array.from(Array(60).keys()).map((minute) => {
|
||||
return (
|
||||
<option key={minute} value={minute}>{normalize(minute, 2)}</option>
|
||||
<option key={minute} value={normalize(minute, 2)}>{normalize(minute, 2)}</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
@ -42,6 +44,7 @@ export function TimePicker({ onChange }: TimePickerProps): JSX.Element {
|
||||
<div className="date-picker__field">
|
||||
<select
|
||||
className="date-picker__field-select"
|
||||
value={period}
|
||||
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => setPeriod(e.target.value)}>
|
||||
<option value="AM">AM</option>
|
||||
<option value="PM">PM</option>
|
||||
|
||||
@ -21,3 +21,21 @@ export const isNotTheDate = (date: Date) => {
|
||||
export const getDaysInMonth = (year: number, month: number): number => {
|
||||
return new Date(year, month, 0).getDate();
|
||||
}
|
||||
|
||||
export const format = (hour: string, minute: string, period: string) => {
|
||||
const formattedHour = period === 'AM' ? normalize(hour, 2) : String(Number(hour) + 12)
|
||||
const formattedMinute = normalize(minute, 2)
|
||||
return `${formattedHour}:${formattedMinute}`
|
||||
}
|
||||
|
||||
export const parse = (value: string) => {
|
||||
const [hour, minute] = value.split(':')
|
||||
const numHour = Number(hour)
|
||||
const period = numHour < 12 ? 'AM' : 'PM'
|
||||
const formattedHour = numHour > 12 ? String(numHour - 12) : String(numHour)
|
||||
return {
|
||||
hour: formattedHour,
|
||||
minute,
|
||||
period
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { actions, RootState } from '../../store'
|
||||
import Title from '../Title'
|
||||
import Policy from '../Policy'
|
||||
import EmailInput from '../EmailInput'
|
||||
@ -9,23 +11,20 @@ import routes from '../../routes'
|
||||
|
||||
function EmailEnterPage(): JSX.Element {
|
||||
const { t } = useTranslation()
|
||||
const dispatch = useDispatch()
|
||||
const navigate = useNavigate()
|
||||
const [email, setEmail] = useState('')
|
||||
const email = useSelector((state: RootState) => state.email)
|
||||
const [isDisabled, setIsDisabled] = useState(true)
|
||||
const links = [
|
||||
{ text: 'EULA', href: 'https://aura.wit.life/terms' },
|
||||
{ text: 'Privacy Policy', href: 'https://aura.wit.life/privacy' },
|
||||
]
|
||||
const handleValidEmail = (email: string) => {
|
||||
setEmail(email)
|
||||
dispatch(actions.email.update(email))
|
||||
setIsDisabled(false)
|
||||
}
|
||||
const handleClick = () => navigate(routes.client.subscription())
|
||||
|
||||
useEffect(() => {
|
||||
console.log('email', email)
|
||||
}, [email])
|
||||
|
||||
return (
|
||||
<section className='page'>
|
||||
<Title variant='h2' className='mt-24'>{t('weWillEmailYou')}</Title>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { FormField } from '../../types'
|
||||
import './styles.css'
|
||||
|
||||
@ -11,14 +11,16 @@ function EmailInput(props: FormField<string>): JSX.Element {
|
||||
const { name, value, placeholder, onValid, onInvalid } = props
|
||||
const [email, setEmail] = useState(value)
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const inputEmail = event.target.value
|
||||
setEmail(inputEmail)
|
||||
if (isValidEmail(inputEmail)) {
|
||||
onValid(inputEmail)
|
||||
setEmail(event.target.value)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isValidEmail(email)) {
|
||||
onValid(email)
|
||||
} else {
|
||||
onInvalid()
|
||||
}
|
||||
}
|
||||
}, [email, onInvalid, onValid])
|
||||
|
||||
return (
|
||||
<div className="email-input">
|
||||
|
||||
13
src/init.tsx
13
src/init.tsx
@ -2,8 +2,11 @@ import React from 'react'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import i18next from 'i18next'
|
||||
import { I18nextProvider, initReactI18next } from 'react-i18next'
|
||||
import { Provider } from 'react-redux'
|
||||
import { store } from './store'
|
||||
import resources from './locales'
|
||||
import routes from './routes'
|
||||
import { AuthProvider } from './auth'
|
||||
import App from './components/App'
|
||||
|
||||
const init = async () => {
|
||||
@ -17,9 +20,13 @@ const init = async () => {
|
||||
return (
|
||||
<React.StrictMode>
|
||||
<I18nextProvider i18n={i18nextInstance}>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
<Provider store={store}>
|
||||
<BrowserRouter>
|
||||
<AuthProvider>
|
||||
<App />
|
||||
</AuthProvider>
|
||||
</BrowserRouter>
|
||||
</Provider>
|
||||
</I18nextProvider>
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
||||
20
src/store/birthdate.ts
Normal file
20
src/store/birthdate.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { createSlice } from '@reduxjs/toolkit'
|
||||
import type { PayloadAction } from '@reduxjs/toolkit'
|
||||
|
||||
const birthdateSlice = createSlice({
|
||||
name: 'birthdate',
|
||||
initialState: '',
|
||||
reducers: {
|
||||
update(state, action: PayloadAction<string>) {
|
||||
state = action.payload
|
||||
return state
|
||||
},
|
||||
clear(state) {
|
||||
state = ''
|
||||
return state
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export const { actions } = birthdateSlice
|
||||
export default birthdateSlice.reducer
|
||||
20
src/store/birthtime.ts
Normal file
20
src/store/birthtime.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { createSlice } from '@reduxjs/toolkit'
|
||||
import type { PayloadAction } from '@reduxjs/toolkit'
|
||||
|
||||
const birthtimeSlice = createSlice({
|
||||
name: 'birthtime',
|
||||
initialState: '',
|
||||
reducers: {
|
||||
update(state, action: PayloadAction<string>) {
|
||||
state = action.payload
|
||||
return state
|
||||
},
|
||||
clear(state) {
|
||||
state = ''
|
||||
return state
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export const { actions } = birthtimeSlice
|
||||
export default birthtimeSlice.reducer
|
||||
20
src/store/email.ts
Normal file
20
src/store/email.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { createSlice } from '@reduxjs/toolkit'
|
||||
import type { PayloadAction } from '@reduxjs/toolkit'
|
||||
|
||||
const emailSlice = createSlice({
|
||||
name: 'email',
|
||||
initialState: '',
|
||||
reducers: {
|
||||
update(state, action: PayloadAction<string>) {
|
||||
state = action.payload
|
||||
return state
|
||||
},
|
||||
clear(state) {
|
||||
state = ''
|
||||
return state
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export const { actions } = emailSlice
|
||||
export default emailSlice.reducer
|
||||
22
src/store/index.ts
Normal file
22
src/store/index.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { combineReducers, configureStore } from '@reduxjs/toolkit'
|
||||
import birthdate, { actions as birthdateActions } from './birthdate'
|
||||
import birthtime, { actions as birthtimeActions } from './birthtime'
|
||||
import email, { actions as emailActions } from './email'
|
||||
import { loadStore, backupStore } from './storageHelper'
|
||||
|
||||
const preloadedState = loadStore()
|
||||
export const reducer = combineReducers({
|
||||
email,
|
||||
birthdate,
|
||||
birthtime,
|
||||
})
|
||||
export const actions = {
|
||||
email: emailActions,
|
||||
birthdate: birthdateActions,
|
||||
birthtime: birthtimeActions,
|
||||
}
|
||||
export type RootState = ReturnType<typeof reducer>
|
||||
export const store = configureStore({ reducer, preloadedState })
|
||||
export type AppDispatch = typeof store.dispatch
|
||||
export type StoreType = typeof store
|
||||
export const unsubscribe = backupStore(store)
|
||||
38
src/store/storageHelper.ts
Normal file
38
src/store/storageHelper.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { ToolkitStore } from "@reduxjs/toolkit/dist/configureStore"
|
||||
|
||||
const storageKey = '_aura'
|
||||
|
||||
export const backupStore = (store: ToolkitStore) => {
|
||||
const saveState = () => {
|
||||
const state = store.getState()
|
||||
try {
|
||||
const serializedState = JSON.stringify(state)
|
||||
localStorage.setItem(storageKey, serializedState)
|
||||
} catch (err) {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('beforeunload', saveState)
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
saveState()
|
||||
}
|
||||
})
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', saveState)
|
||||
document.removeEventListener('visibilitychange', saveState)
|
||||
}
|
||||
}
|
||||
export const loadStore = () => {
|
||||
try {
|
||||
const serializedState = localStorage.getItem(storageKey)
|
||||
if (serializedState === null) {
|
||||
return undefined
|
||||
}
|
||||
return JSON.parse(serializedState)
|
||||
} catch (err) {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user