codeforlife 2.8.3 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (364) hide show
  1. package/dist/Countdown-DFI1RljW.js +28 -0
  2. package/dist/Countdown-DFI1RljW.js.map +1 -0
  3. package/dist/Countdown-zY3nMzmi.cjs +2 -0
  4. package/dist/Countdown-zY3nMzmi.cjs.map +1 -0
  5. package/dist/Image-D5jC9UoX.js +21 -0
  6. package/dist/Image-D5jC9UoX.js.map +1 -0
  7. package/dist/Image-mgT45r_B.cjs +2 -0
  8. package/dist/Image-mgT45r_B.cjs.map +1 -0
  9. package/dist/LinkButton-BYFkvL_O.cjs +2 -0
  10. package/dist/LinkButton-BYFkvL_O.cjs.map +1 -0
  11. package/dist/LinkButton-oK0RThqn.js +12 -0
  12. package/dist/LinkButton-oK0RThqn.js.map +1 -0
  13. package/dist/Navigate-Dq47aqC8.js +25 -0
  14. package/dist/Navigate-Dq47aqC8.js.map +1 -0
  15. package/dist/Navigate-dQocLhzN.cjs +2 -0
  16. package/dist/Navigate-dQocLhzN.cjs.map +1 -0
  17. package/dist/api/endpoints/index.cjs.js +2 -0
  18. package/dist/api/endpoints/index.cjs.js.map +1 -0
  19. package/dist/api/endpoints/index.d.ts +81 -0
  20. package/dist/api/endpoints/index.es.js +78 -0
  21. package/dist/api/endpoints/index.es.js.map +1 -0
  22. package/dist/api/index.cjs.js +4 -0
  23. package/dist/api/index.cjs.js.map +1 -0
  24. package/dist/api/index.d.ts +77 -0
  25. package/dist/api/index.es.js +541 -0
  26. package/dist/api/index.es.js.map +1 -0
  27. package/dist/api-Cbyt3rw0.js +92 -0
  28. package/dist/api-Cbyt3rw0.js.map +1 -0
  29. package/dist/api-CnMfjRk3.cjs +2 -0
  30. package/dist/api-CnMfjRk3.cjs.map +1 -0
  31. package/dist/api-Cs4Y-WeI.js +20 -0
  32. package/dist/api-Cs4Y-WeI.js.map +1 -0
  33. package/dist/api-DIgp_6Vr.cjs +2 -0
  34. package/dist/api-DIgp_6Vr.cjs.map +1 -0
  35. package/dist/api.d.ts +123 -0
  36. package/dist/auth-BPfUPjmM.js +237 -0
  37. package/dist/auth-BPfUPjmM.js.map +1 -0
  38. package/dist/auth-Vf1MgMci.cjs +2 -0
  39. package/dist/auth-Vf1MgMci.cjs.map +1 -0
  40. package/dist/auth.d.ts +40 -0
  41. package/dist/components/form/index.cjs.js +2 -0
  42. package/dist/components/form/index.cjs.js.map +1 -0
  43. package/dist/components/form/index.d.ts +86 -0
  44. package/dist/components/form/index.es.js +18 -0
  45. package/dist/components/form/index.es.js.map +1 -0
  46. package/dist/components/index.cjs.js +11 -0
  47. package/dist/components/index.cjs.js.map +1 -0
  48. package/dist/components/index.d.ts +90 -0
  49. package/dist/components/index.es.js +383 -0
  50. package/dist/components/index.es.js.map +1 -0
  51. package/dist/components/page/index.cjs.js +2 -0
  52. package/dist/components/page/index.cjs.js.map +1 -0
  53. package/dist/components/page/index.d.ts +77 -0
  54. package/dist/components/page/index.es.js +9 -0
  55. package/dist/components/page/index.es.js.map +1 -0
  56. package/dist/components/router/index.cjs.js +2 -0
  57. package/dist/components/router/index.cjs.js.map +1 -0
  58. package/dist/components/router/index.d.ts +78 -0
  59. package/dist/components/router/index.es.js +11 -0
  60. package/dist/components/router/index.es.js.map +1 -0
  61. package/dist/components/table/index.cjs.js +2 -0
  62. package/dist/components/table/index.cjs.js.map +1 -0
  63. package/dist/components/table/index.d.ts +75 -0
  64. package/dist/components/table/index.es.js +9 -0
  65. package/dist/components/table/index.es.js.map +1 -0
  66. package/dist/en-gb-B_rK7Jx1.js +20 -0
  67. package/dist/en-gb-B_rK7Jx1.js.map +1 -0
  68. package/dist/en-gb-CpyEkKq3.cjs +2 -0
  69. package/dist/en-gb-CpyEkKq3.cjs.map +1 -0
  70. package/dist/features/index.cjs.js +2 -0
  71. package/dist/features/index.cjs.js.map +1 -0
  72. package/dist/features/index.d.ts +76 -0
  73. package/dist/features/index.es.js +47 -0
  74. package/dist/features/index.es.js.map +1 -0
  75. package/dist/form.d.ts +40 -0
  76. package/dist/general-BPbbmkeX.cjs +2 -0
  77. package/dist/general-BPbbmkeX.cjs.map +1 -0
  78. package/dist/general-CtTJPCJn.js +58 -0
  79. package/dist/general-CtTJPCJn.js.map +1 -0
  80. package/dist/general.d.ts +53 -0
  81. package/dist/hooks/index.cjs.js +2 -0
  82. package/dist/hooks/index.cjs.js.map +1 -0
  83. package/dist/hooks/index.d.ts +77 -0
  84. package/dist/hooks/index.es.js +25 -0
  85. package/dist/hooks/index.es.js.map +1 -0
  86. package/dist/index-2W--_sNE.js +30 -0
  87. package/dist/index-2W--_sNE.js.map +1 -0
  88. package/dist/index-BUMdUVBH.cjs +5 -0
  89. package/dist/index-BUMdUVBH.cjs.map +1 -0
  90. package/dist/index-B_Zy_zwA.cjs +2 -0
  91. package/dist/index-B_Zy_zwA.cjs.map +1 -0
  92. package/dist/index-BgsynEGX.cjs +2 -0
  93. package/dist/index-BgsynEGX.cjs.map +1 -0
  94. package/dist/index-CA-ugBSa.cjs +2 -0
  95. package/dist/index-CA-ugBSa.cjs.map +1 -0
  96. package/dist/index-CNtOlJ49.js +572 -0
  97. package/dist/index-CNtOlJ49.js.map +1 -0
  98. package/dist/index-DkM_cG3a.js +237 -0
  99. package/dist/index-DkM_cG3a.js.map +1 -0
  100. package/dist/index-Dqp7dpn3.js +1111 -0
  101. package/dist/index-Dqp7dpn3.js.map +1 -0
  102. package/dist/index.cjs.js +2 -0
  103. package/dist/index.cjs.js.map +1 -0
  104. package/dist/index.d.ts +78 -0
  105. package/dist/index.es.js +7 -0
  106. package/dist/index.es.js.map +1 -0
  107. package/dist/jsx-runtime-CeSfJrVB.cjs +31 -0
  108. package/dist/jsx-runtime-CeSfJrVB.cjs.map +1 -0
  109. package/dist/jsx-runtime-Dpn_P65e.js +634 -0
  110. package/dist/jsx-runtime-Dpn_P65e.js.map +1 -0
  111. package/dist/middlewares/index.cjs.js +2 -0
  112. package/dist/middlewares/index.cjs.js.map +1 -0
  113. package/dist/middlewares/index.d.ts +73 -0
  114. package/dist/middlewares/index.es.js +10 -0
  115. package/dist/middlewares/index.es.js.map +1 -0
  116. package/dist/palette-CE2mKYse.cjs +2 -0
  117. package/dist/palette-CE2mKYse.cjs.map +1 -0
  118. package/dist/palette-C_L0akN_.js +43 -0
  119. package/dist/palette-C_L0akN_.js.map +1 -0
  120. package/dist/router.d.ts +48 -0
  121. package/dist/rtk-query.modern-CjetfYPg.js +3038 -0
  122. package/dist/rtk-query.modern-CjetfYPg.js.map +1 -0
  123. package/dist/rtk-query.modern-PBCErCqb.cjs +15 -0
  124. package/dist/rtk-query.modern-PBCErCqb.cjs.map +1 -0
  125. package/dist/schema.d.ts +89 -0
  126. package/dist/schemas-BZbJpkD5.cjs +2 -0
  127. package/dist/schemas-BZbJpkD5.cjs.map +1 -0
  128. package/dist/schemas-DlOtf2vf.js +162 -0
  129. package/dist/schemas-DlOtf2vf.js.map +1 -0
  130. package/dist/server.cjs.js +6 -0
  131. package/dist/server.cjs.js.map +1 -0
  132. package/dist/server.d.ts +129 -0
  133. package/dist/server.es.js +127 -0
  134. package/dist/server.es.js.map +1 -0
  135. package/dist/session-CE2U7oL1.cjs +2 -0
  136. package/dist/session-CE2U7oL1.cjs.map +1 -0
  137. package/dist/session-CacrgFSv.cjs +2 -0
  138. package/dist/session-CacrgFSv.cjs.map +1 -0
  139. package/dist/session-D312kYKk.js +32 -0
  140. package/dist/session-D312kYKk.js.map +1 -0
  141. package/dist/session-oI-Ht2C8.js +30 -0
  142. package/dist/session-oI-Ht2C8.js.map +1 -0
  143. package/dist/settings/index.cjs.js +2 -0
  144. package/dist/settings/index.cjs.js.map +1 -0
  145. package/dist/settings/index.d.ts +76 -0
  146. package/dist/settings/index.es.js +17 -0
  147. package/dist/settings/index.es.js.map +1 -0
  148. package/dist/slices/index.cjs.js +2 -0
  149. package/dist/slices/index.cjs.js.map +1 -0
  150. package/dist/slices/index.d.ts +74 -0
  151. package/dist/slices/index.es.js +6 -0
  152. package/dist/slices/index.es.js.map +1 -0
  153. package/dist/store.d.ts +17 -0
  154. package/dist/style.css +1 -0
  155. package/dist/test.d.ts +75 -0
  156. package/dist/theme/components/index.cjs.js +2 -0
  157. package/dist/theme/components/index.cjs.js.map +1 -0
  158. package/dist/theme/components/index.d.ts +75 -0
  159. package/dist/theme/components/index.es.js +6 -0
  160. package/dist/theme/components/index.es.js.map +1 -0
  161. package/dist/theme/index.cjs.js +2 -0
  162. package/dist/theme/index.cjs.js.map +1 -0
  163. package/dist/theme/index.d.ts +79 -0
  164. package/dist/theme/index.es.js +222 -0
  165. package/dist/theme/index.es.js.map +1 -0
  166. package/dist/theme.d.ts +19 -0
  167. package/dist/urls-DtHr1d3H.js +14 -0
  168. package/dist/urls-DtHr1d3H.js.map +1 -0
  169. package/dist/urls-MaVXL_C2.cjs +2 -0
  170. package/dist/urls-MaVXL_C2.cjs.map +1 -0
  171. package/dist/utils/api.cjs.js +2 -0
  172. package/dist/utils/api.cjs.js.map +1 -0
  173. package/dist/utils/api.d.ts +144 -0
  174. package/dist/utils/api.es.js +16 -0
  175. package/dist/utils/api.es.js.map +1 -0
  176. package/dist/utils/auth.cjs.js +2 -0
  177. package/dist/utils/auth.cjs.js.map +1 -0
  178. package/dist/utils/auth.d.ts +100 -0
  179. package/dist/utils/auth.es.js +117 -0
  180. package/dist/utils/auth.es.js.map +1 -0
  181. package/dist/utils/form.cjs.js +2 -0
  182. package/dist/utils/form.cjs.js.map +1 -0
  183. package/dist/utils/form.d.ts +97 -0
  184. package/dist/utils/form.es.js +76 -0
  185. package/dist/utils/form.es.js.map +1 -0
  186. package/dist/utils/general.cjs.js +2 -0
  187. package/dist/utils/general.cjs.js.map +1 -0
  188. package/dist/utils/general.d.ts +102 -0
  189. package/{src/utils/general.ts → dist/utils/general.es.js} +87 -165
  190. package/dist/utils/general.es.js.map +1 -0
  191. package/dist/utils/router.cjs.js +2 -0
  192. package/dist/utils/router.cjs.js.map +1 -0
  193. package/dist/utils/router.d.ts +91 -0
  194. package/dist/utils/router.es.js +33 -0
  195. package/dist/utils/router.es.js.map +1 -0
  196. package/dist/utils/schema.cjs.js +2 -0
  197. package/dist/utils/schema.cjs.js.map +1 -0
  198. package/dist/utils/schema.d.ts +115 -0
  199. package/dist/utils/schema.es.js +123 -0
  200. package/dist/utils/schema.es.js.map +1 -0
  201. package/dist/utils/store.cjs.js +2 -0
  202. package/dist/utils/store.cjs.js.map +1 -0
  203. package/dist/utils/store.d.ts +82 -0
  204. package/dist/utils/store.es.js +20 -0
  205. package/dist/utils/store.es.js.map +1 -0
  206. package/dist/utils/test.cjs.js +2 -0
  207. package/dist/utils/test.cjs.js.map +1 -0
  208. package/dist/utils/test.d.ts +125 -0
  209. package/dist/utils/test.es.js +60 -0
  210. package/dist/utils/test.es.js.map +1 -0
  211. package/dist/utils/theme.cjs.js +2 -0
  212. package/dist/utils/theme.cjs.js.map +1 -0
  213. package/dist/utils/theme.d.ts +83 -0
  214. package/dist/utils/theme.es.js +12 -0
  215. package/dist/utils/theme.es.js.map +1 -0
  216. package/dist/utils/window.cjs.js +2 -0
  217. package/dist/utils/window.cjs.js.map +1 -0
  218. package/dist/utils/window.d.ts +74 -0
  219. package/dist/utils/window.es.js +11 -0
  220. package/dist/utils/window.es.js.map +1 -0
  221. package/dist/window.d.ts +5 -0
  222. package/package.json +142 -37
  223. package/.github/workflows/main.yml +0 -55
  224. package/.prettierignore +0 -1
  225. package/CHANGELOG.md +0 -2027
  226. package/CONTRIBUTING.md +0 -3
  227. package/eslint.config.js +0 -17
  228. package/src/api/createApi.ts +0 -91
  229. package/src/api/endpoints/authFactor.ts +0 -31
  230. package/src/api/endpoints/index.ts +0 -9
  231. package/src/api/endpoints/klass.ts +0 -87
  232. package/src/api/endpoints/school.ts +0 -34
  233. package/src/api/endpoints/session.ts +0 -47
  234. package/src/api/endpoints/user.ts +0 -70
  235. package/src/api/index.ts +0 -5
  236. package/src/api/models.ts +0 -145
  237. package/src/api/schemas.ts +0 -243
  238. package/src/api/tagTypes.ts +0 -12
  239. package/src/api/urls.ts +0 -13
  240. package/src/components/App.css +0 -38
  241. package/src/components/App.tsx +0 -152
  242. package/src/components/ClickableTooltip.tsx +0 -43
  243. package/src/components/CopyIconButton.test.tsx +0 -16
  244. package/src/components/CopyIconButton.tsx +0 -27
  245. package/src/components/Countdown.tsx +0 -42
  246. package/src/components/DownloadFileButton.tsx +0 -55
  247. package/src/components/ElevatedAppBar.tsx +0 -41
  248. package/src/components/Image.tsx +0 -41
  249. package/src/components/InputFileButton.tsx +0 -27
  250. package/src/components/ItemizedList.tsx +0 -63
  251. package/src/components/OrderedGrid.tsx +0 -92
  252. package/src/components/ScrollIntoViewLink.tsx +0 -22
  253. package/src/components/SyncError.tsx +0 -14
  254. package/src/components/TablePagination.tsx +0 -142
  255. package/src/components/YouTubeVideo.tsx +0 -26
  256. package/src/components/form/ApiAutocompleteField.tsx +0 -187
  257. package/src/components/form/AutocompleteField.tsx +0 -135
  258. package/src/components/form/CheckboxField.tsx +0 -86
  259. package/src/components/form/CountryField.tsx +0 -75
  260. package/src/components/form/DatePickerField.tsx +0 -126
  261. package/src/components/form/EmailField.tsx +0 -38
  262. package/src/components/form/FirstNameField.tsx +0 -40
  263. package/src/components/form/Form.tsx +0 -165
  264. package/src/components/form/OtpField.tsx +0 -28
  265. package/src/components/form/PasswordField.tsx +0 -71
  266. package/src/components/form/RepeatField.tsx +0 -123
  267. package/src/components/form/SubmitButton.tsx +0 -57
  268. package/src/components/form/TextField.tsx +0 -150
  269. package/src/components/form/UkCountyField.tsx +0 -68
  270. package/src/components/form/index.tsx +0 -35
  271. package/src/components/index.ts +0 -28
  272. package/src/components/page/Banner.tsx +0 -95
  273. package/src/components/page/Notification.tsx +0 -71
  274. package/src/components/page/Page.tsx +0 -73
  275. package/src/components/page/Section.tsx +0 -21
  276. package/src/components/page/TabBar.tsx +0 -131
  277. package/src/components/page/index.ts +0 -10
  278. package/src/components/router/Link.tsx +0 -23
  279. package/src/components/router/LinkButton.tsx +0 -22
  280. package/src/components/router/LinkIconButton.tsx +0 -22
  281. package/src/components/router/LinkListItem.tsx +0 -22
  282. package/src/components/router/LinkTab.tsx +0 -22
  283. package/src/components/router/Navigate.tsx +0 -33
  284. package/src/components/router/index.tsx +0 -9
  285. package/src/components/table/CellStack.tsx +0 -19
  286. package/src/components/table/Table.tsx +0 -55
  287. package/src/components/table/index.tsx +0 -8
  288. package/src/features/InactiveDialog.tsx +0 -40
  289. package/src/features/ScreenTimeDialog.tsx +0 -30
  290. package/src/features/index.ts +0 -4
  291. package/src/fonts/ttf/Inter-VariableFont_slnt,wght.ttf +0 -0
  292. package/src/fonts/ttf/SpaceGrotesk-VariableFont_wght.ttf +0 -0
  293. package/src/hooks/api.tsx +0 -39
  294. package/src/hooks/auth.tsx +0 -397
  295. package/src/hooks/form.tsx +0 -11
  296. package/src/hooks/general.tsx +0 -110
  297. package/src/hooks/index.ts +0 -5
  298. package/src/hooks/router.tsx +0 -168
  299. package/src/images/svg/brain.svg +0 -1
  300. package/src/index.ts +0 -2
  301. package/src/middlewares/index.ts +0 -1
  302. package/src/middlewares/session.ts +0 -21
  303. package/src/scripts/freshDesk.js +0 -473
  304. package/src/scripts/index.ts +0 -1
  305. package/src/server.js +0 -187
  306. package/src/settings/custom.ts +0 -22
  307. package/src/settings/index.ts +0 -7
  308. package/src/settings/vite.ts +0 -26
  309. package/src/setupTests.ts +0 -2
  310. package/src/slices/createSlice.ts +0 -8
  311. package/src/slices/index.ts +0 -2
  312. package/src/slices/session.ts +0 -32
  313. package/src/theme/ThemedBox.tsx +0 -265
  314. package/src/theme/colors.ts +0 -57
  315. package/src/theme/components/MuiAccordion.tsx +0 -13
  316. package/src/theme/components/MuiAutocomplete.tsx +0 -11
  317. package/src/theme/components/MuiButton.ts +0 -70
  318. package/src/theme/components/MuiCardActions.tsx +0 -12
  319. package/src/theme/components/MuiCheckbox.ts +0 -12
  320. package/src/theme/components/MuiContainer.ts +0 -19
  321. package/src/theme/components/MuiDialog.tsx +0 -16
  322. package/src/theme/components/MuiFormControlLabel.ts +0 -18
  323. package/src/theme/components/MuiFormHelperText.ts +0 -12
  324. package/src/theme/components/MuiGrid2.ts +0 -16
  325. package/src/theme/components/MuiInputBase.ts +0 -14
  326. package/src/theme/components/MuiLink.ts +0 -41
  327. package/src/theme/components/MuiList.ts +0 -12
  328. package/src/theme/components/MuiListItemText.ts +0 -18
  329. package/src/theme/components/MuiMenu.ts +0 -14
  330. package/src/theme/components/MuiMenuItem.ts +0 -15
  331. package/src/theme/components/MuiSelect.ts +0 -16
  332. package/src/theme/components/MuiTab.ts +0 -29
  333. package/src/theme/components/MuiTable.ts +0 -29
  334. package/src/theme/components/MuiTableBody.ts +0 -15
  335. package/src/theme/components/MuiTableHead.ts +0 -26
  336. package/src/theme/components/MuiTabs.ts +0 -26
  337. package/src/theme/components/MuiTextField.ts +0 -86
  338. package/src/theme/components/MuiToolbar.ts +0 -11
  339. package/src/theme/components/MuiTypography.ts +0 -12
  340. package/src/theme/components/_components.ts +0 -95
  341. package/src/theme/components/index.ts +0 -57
  342. package/src/theme/index.ts +0 -25
  343. package/src/theme/palette.ts +0 -98
  344. package/src/theme/spacing.ts +0 -8
  345. package/src/theme/typography.ts +0 -101
  346. package/src/utils/api.test.ts +0 -19
  347. package/src/utils/api.tsx +0 -339
  348. package/src/utils/auth.ts +0 -78
  349. package/src/utils/form.test.ts +0 -50
  350. package/src/utils/form.ts +0 -193
  351. package/src/utils/general.test.ts +0 -55
  352. package/src/utils/router.test.ts +0 -156
  353. package/src/utils/router.ts +0 -67
  354. package/src/utils/schema.ts +0 -290
  355. package/src/utils/store.ts +0 -31
  356. package/src/utils/test.tsx +0 -82
  357. package/src/utils/theme.tsx +0 -83
  358. package/src/utils/window.ts +0 -11
  359. package/src/vite-env.d.ts +0 -1
  360. package/tsconfig.app.json +0 -4
  361. package/tsconfig.json +0 -7
  362. package/tsconfig.node.json +0 -4
  363. package/types/fixes.d.ts +0 -18
  364. package/vite.config.ts +0 -23
@@ -1,397 +0,0 @@
1
- import * as yup from "yup"
2
- import { type ReactNode, useCallback, useEffect, useState } from "react"
3
- import Cookies from "js-cookie"
4
- import type { TypedUseMutation } from "@reduxjs/toolkit/query/react"
5
- import { createSearchParams } from "react-router-dom"
6
- // eslint-disable-next-line @typescript-eslint/no-restricted-imports
7
- import { useSelector } from "react-redux"
8
-
9
- import { type AuthFactor, type User } from "../api"
10
- import {
11
- type OAuth2CodeChallenge,
12
- type OAuth2CodeChallengeLengths,
13
- type OAuth2ReceiveCodeUrlSearchParams,
14
- type OAuth2RequestCodeUrlSearchParams,
15
- generateOAuth2CodeChallenge,
16
- makeOAuth2StorageKey,
17
- } from "../utils/auth"
18
- import { useLocation, useNavigate, useSearchParams } from "./router"
19
- import { type ExchangeOAuth2CodeArg } from "../api/endpoints/session"
20
- import { SESSION_METADATA_COOKIE_NAME } from "../settings"
21
- import { generateSecureRandomString } from "../utils/general"
22
- import { selectIsLoggedIn } from "../slices/session"
23
-
24
- // -----------------------------------------------------------------------------
25
- // Session
26
- // -----------------------------------------------------------------------------
27
-
28
- export interface SessionMetadata {
29
- user_id: User["id"]
30
- user_type: "teacher" | "student" | "indy"
31
- auth_factors: Array<AuthFactor["type"]>
32
- otp_bypass_token_exists: boolean
33
- }
34
-
35
- export function useSessionMetadata<T = SessionMetadata>(
36
- cookieName = SESSION_METADATA_COOKIE_NAME,
37
- ): T | undefined {
38
- return useSelector(selectIsLoggedIn)
39
- ? (JSON.parse(Cookies.get(cookieName)!) as T)
40
- : undefined
41
- }
42
-
43
- /**
44
- * A utility function to predefine a useSessionMetadata hook.
45
- * @param cookieName The name of the session metadata cookie.
46
- * @returns An object containing the session metadata.
47
- */
48
- useSessionMetadata.predefine = <SessionMetadata,>(
49
- cookieName = SESSION_METADATA_COOKIE_NAME,
50
- ) => {
51
- return () => useSessionMetadata<SessionMetadata>(cookieName)
52
- }
53
-
54
- export type UseSessionChildrenFunction<Required extends boolean> = (
55
- metadata: Required extends true
56
- ? SessionMetadata
57
- : SessionMetadata | undefined,
58
- ) => ReactNode
59
-
60
- export type UseSessionChildren<
61
- UserType extends SessionMetadata["user_type"] | undefined,
62
- > =
63
- | ReactNode
64
- | (UserType extends undefined
65
- ? UseSessionChildrenFunction<false>
66
- : UseSessionChildrenFunction<true>)
67
-
68
- export type UseSessionOptions<
69
- UserType extends SessionMetadata["user_type"] | undefined,
70
- > = Partial<{
71
- userType: UserType
72
- next: boolean
73
- }>
74
-
75
- export function useSession<
76
- UserType extends SessionMetadata["user_type"] | undefined = undefined,
77
- >(
78
- children: UseSessionChildren<UserType>,
79
- options: UseSessionOptions<UserType> = {},
80
- ) {
81
- const { userType, next = true } = options
82
-
83
- const { pathname } = useLocation()
84
- const navigate = useNavigate()
85
- const sessionMetadata = useSessionMetadata()
86
-
87
- const loginRequired =
88
- userType && (!sessionMetadata || sessionMetadata.user_type !== userType)
89
-
90
- useEffect(() => {
91
- if (loginRequired) {
92
- navigate({
93
- pathname:
94
- "/login" +
95
- {
96
- teacher: "/teacher",
97
- student: "/student",
98
- indy: "/independent",
99
- }[userType],
100
- search: next
101
- ? createSearchParams({ next: pathname }).toString()
102
- : undefined,
103
- })
104
- }
105
- }, [navigate, loginRequired, userType, next, pathname])
106
-
107
- if (loginRequired) return <></>
108
-
109
- if (typeof children === "function") {
110
- return sessionMetadata
111
- ? (children as UseSessionChildrenFunction<true>)(sessionMetadata)
112
- : (children as UseSessionChildrenFunction<false>)(sessionMetadata)
113
- }
114
-
115
- return children
116
- }
117
-
118
- // -----------------------------------------------------------------------------
119
- // OAuth2
120
- // -----------------------------------------------------------------------------
121
-
122
- export function useOAuth2State(
123
- provider: string,
124
- length: number = 32,
125
- storageKey: string = "state",
126
- ): [string | undefined, () => void] {
127
- const oAuth2StorageKey = makeOAuth2StorageKey(provider, storageKey)
128
- const storageValue = sessionStorage.getItem(oAuth2StorageKey)
129
-
130
- const [_state, _setState] = useState<string>()
131
-
132
- useEffect(() => {
133
- let state: string
134
- if (storageValue && storageValue.length === length) {
135
- state = storageValue
136
- } else {
137
- state = generateSecureRandomString(length)
138
- sessionStorage.setItem(oAuth2StorageKey, state)
139
- }
140
-
141
- _setState(state)
142
- }, [oAuth2StorageKey, storageValue, length])
143
-
144
- const resetState = useCallback(() => {
145
- sessionStorage.removeItem(oAuth2StorageKey)
146
- _setState(undefined)
147
- }, [oAuth2StorageKey])
148
-
149
- return [_state, resetState]
150
- }
151
-
152
- export function useOAuth2CodeChallenge(
153
- provider: string,
154
- length: OAuth2CodeChallengeLengths = 128,
155
- storageKey: string = "codeChallenge",
156
- ): [OAuth2CodeChallenge | undefined, () => void] {
157
- const oAuth2StorageKey = makeOAuth2StorageKey(provider, storageKey)
158
- const storageValue = sessionStorage.getItem(oAuth2StorageKey)
159
-
160
- const [_codeChallenge, _setCodeChallenge] = useState<OAuth2CodeChallenge>()
161
-
162
- useEffect(() => {
163
- let codeChallenge: OAuth2CodeChallenge | undefined
164
- if (storageValue) {
165
- const storageJsonValue: unknown = JSON.parse(storageValue)
166
- if (
167
- typeof storageJsonValue === "object" &&
168
- storageJsonValue &&
169
- "verifier" in storageJsonValue &&
170
- typeof storageJsonValue.verifier == "string" &&
171
- storageJsonValue.verifier.length === length &&
172
- "challenge" in storageJsonValue &&
173
- typeof storageJsonValue.challenge === "string" &&
174
- "method" in storageJsonValue &&
175
- storageJsonValue.method === "S256"
176
- ) {
177
- codeChallenge = {
178
- verifier: storageJsonValue.verifier,
179
- challenge: storageJsonValue.challenge,
180
- method: storageJsonValue.method,
181
- }
182
- }
183
- }
184
-
185
- if (codeChallenge) _setCodeChallenge(codeChallenge)
186
- else {
187
- generateOAuth2CodeChallenge(length)
188
- .then(codeChallenge => {
189
- sessionStorage.setItem(
190
- oAuth2StorageKey,
191
- JSON.stringify(codeChallenge),
192
- )
193
-
194
- _setCodeChallenge(codeChallenge)
195
- })
196
- .catch(error => {
197
- if (error) console.error(error)
198
- })
199
- }
200
- }, [oAuth2StorageKey, storageValue, length])
201
-
202
- const resetCodeChallenge = useCallback(() => {
203
- sessionStorage.removeItem(oAuth2StorageKey)
204
- _setCodeChallenge(undefined)
205
- }, [oAuth2StorageKey])
206
-
207
- return [_codeChallenge, resetCodeChallenge]
208
- }
209
-
210
- interface BaseUseOAuth2KwArgs<SessionMetadata> {
211
- provider: string
212
- authUri: string
213
- clientId: string
214
- redirectUri: string
215
- scope: string
216
- responseType?: "code"
217
- accessType?: "offline"
218
- prompt?: string
219
- useLoginMutation: TypedUseMutation<
220
- SessionMetadata,
221
- ExchangeOAuth2CodeArg,
222
- any
223
- >
224
- onCreateSession: (result: SessionMetadata) => void
225
- onRetrieveSession: (metadata: SessionMetadata) => void
226
- }
227
-
228
- interface UseOAuth2KwArgs<SessionMetadata>
229
- extends BaseUseOAuth2KwArgs<SessionMetadata> {
230
- useSessionMetadata: () => SessionMetadata | undefined
231
- }
232
-
233
- export type OAuth2 = [string, OAuth2RequestCodeUrlSearchParams] | []
234
-
235
- // https://datatracker.ietf.org/doc/html/rfc7636
236
- function useOAuth2Internal<SessionMetadata>({
237
- provider,
238
- authUri,
239
- clientId,
240
- redirectUri,
241
- scope,
242
- responseType = "code",
243
- accessType = "offline",
244
- prompt,
245
- useSessionMetadata,
246
- useLoginMutation,
247
- onCreateSession,
248
- onRetrieveSession,
249
- }: UseOAuth2KwArgs<SessionMetadata>): OAuth2 {
250
- const [state, resetState] = useOAuth2State(provider)
251
- const [
252
- {
253
- verifier: codeVerifier,
254
- challenge: codeChallenge,
255
- method: codeChallengeMethod,
256
- } = {},
257
- resetCodeChallenge,
258
- ] = useOAuth2CodeChallenge(provider)
259
- const [
260
- login,
261
- {
262
- originalArgs: loginArgs = {} as ExchangeOAuth2CodeArg,
263
- isLoading: loginIsLoading,
264
- isError: loginIsError,
265
- },
266
- ] = useLoginMutation()
267
- const sessionMetadata = useSessionMetadata()
268
- const navigate = useNavigate()
269
- const searchParams =
270
- useSearchParams({ code: yup.string(), state: yup.string() }) || {}
271
- const location = useLocation<OAuth2ReceiveCodeUrlSearchParams>()
272
-
273
- const locationState = location.state || {}
274
-
275
- useEffect(() => {
276
- // If the the auth provider has redirected back to our site with the
277
- // expected search params, we redirect to the current page to remove them.
278
- if (searchParams.code && searchParams.state) {
279
- navigate<OAuth2ReceiveCodeUrlSearchParams>(".", {
280
- // Removes the URL containing the search params from the history stack.
281
- replace: true,
282
- // Ensure we don't break the auth flow by navigating to another page.
283
- next: false,
284
- // Store the search params in the page's state instead.
285
- state: { code: searchParams.code, state: searchParams.state },
286
- })
287
- }
288
- }, [searchParams.code, searchParams.state, navigate])
289
-
290
- useEffect(() => {
291
- // If we're already logged in, no need to log in again.
292
- if (sessionMetadata) onRetrieveSession(sessionMetadata)
293
- else if (
294
- // If the state and code verifier have been generated...
295
- state &&
296
- codeVerifier &&
297
- // ...and the page's state contains a code...
298
- locationState.code &&
299
- // ...and the page's state contains the stored state...
300
- locationState.state === state &&
301
- // ...and the login endpoint was not called with the current values or has
302
- // not returned an error...
303
- (loginArgs.code !== locationState.code ||
304
- loginArgs.code_verifier !== codeVerifier ||
305
- loginArgs.redirect_uri !== redirectUri ||
306
- !loginIsError) &&
307
- // ...and the login endpoint is not currently being called...
308
- !loginIsLoading
309
- ) {
310
- // ...call the login endpoint.
311
- login({
312
- code: locationState.code,
313
- code_verifier: codeVerifier,
314
- redirect_uri: redirectUri,
315
- })
316
- .unwrap()
317
- .then(onCreateSession)
318
- .catch(() => {
319
- navigate(".", {
320
- replace: true,
321
- state: {
322
- notifications: [
323
- {
324
- props: {
325
- error: true,
326
- children: "Failed to login. Please try again.",
327
- },
328
- },
329
- ],
330
- },
331
- })
332
- })
333
- .finally(() => {
334
- resetState()
335
- resetCodeChallenge()
336
- })
337
- }
338
- }, [
339
- navigate,
340
- redirectUri,
341
- // State
342
- state,
343
- locationState.state,
344
- resetState,
345
- // Code
346
- codeVerifier,
347
- locationState.code,
348
- resetCodeChallenge,
349
- // Login
350
- login,
351
- loginIsLoading,
352
- loginIsError,
353
- loginArgs.code,
354
- loginArgs.code_verifier,
355
- loginArgs.redirect_uri,
356
- // Session
357
- sessionMetadata,
358
- onCreateSession,
359
- onRetrieveSession,
360
- ])
361
-
362
- if (state && codeChallenge && codeChallengeMethod) {
363
- const urlSearchParams: OAuth2RequestCodeUrlSearchParams = {
364
- client_id: clientId,
365
- redirect_uri: redirectUri,
366
- scope,
367
- response_type: responseType,
368
- access_type: accessType,
369
- state,
370
- code_challenge: codeChallenge,
371
- code_challenge_method: codeChallengeMethod,
372
- }
373
-
374
- if (prompt) urlSearchParams["prompt"] = prompt
375
-
376
- return [
377
- authUri + "?" + new URLSearchParams(urlSearchParams).toString(),
378
- urlSearchParams,
379
- ]
380
- }
381
-
382
- return []
383
- }
384
-
385
- export const useOAuth2: {
386
- <SessionMetadata>(kwargs: UseOAuth2KwArgs<SessionMetadata>): OAuth2
387
- (kwargs: BaseUseOAuth2KwArgs<SessionMetadata>): OAuth2
388
- } = <_SessionMetadata,>(
389
- kwargs:
390
- | UseOAuth2KwArgs<_SessionMetadata>
391
- | BaseUseOAuth2KwArgs<SessionMetadata>,
392
- ): OAuth2 => {
393
- return useOAuth2Internal(
394
- // @ts-expect-error value is assignable
395
- "useSessionMetadata" in kwargs ? kwargs : { ...kwargs, useSessionMetadata },
396
- )
397
- }
@@ -1,11 +0,0 @@
1
- import { useRef } from "react"
2
-
3
- /**
4
- * Shorthand for a reference to a HTML input element since this is so common for
5
- * forms.
6
- *
7
- * @returns Ref object to a HTML input element.
8
- */
9
- export function useInputRef() {
10
- return useRef<HTMLInputElement>(null)
11
- }
@@ -1,110 +0,0 @@
1
- import {
2
- type DependencyList,
3
- type Dispatch,
4
- type SetStateAction,
5
- useEffect,
6
- useState,
7
- } from "react"
8
-
9
- export function useExternalScript<EventType extends keyof HTMLElementEventMap>({
10
- props,
11
- attrs,
12
- eventTypes,
13
- }: {
14
- props: Partial<HTMLScriptElement> & { src: string }
15
- attrs?: Record<string, string>
16
- eventTypes?: EventType[]
17
- }): EventType | undefined {
18
- const [eventType, setEventType] = useState<EventType>()
19
-
20
- useEffect(() => {
21
- if (
22
- document.querySelector<HTMLScriptElement>(`script[src="${props.src}"]`)
23
- ) {
24
- throw Error("already exists")
25
- }
26
-
27
- const script = document.createElement("script")
28
-
29
- Object.entries(props).forEach(([key, value]) => {
30
- // @ts-expect-error value is assignable
31
- script[key] = value
32
- })
33
-
34
- if (attrs !== undefined) {
35
- Object.entries(attrs).forEach(([key, value]) => {
36
- script.setAttribute(key, value)
37
- })
38
- }
39
-
40
- function eventListener(event: Event): void {
41
- setEventType(event.type as EventType)
42
- }
43
-
44
- eventTypes?.forEach(eventType => {
45
- script.addEventListener(eventType, eventListener)
46
- })
47
-
48
- document.head.appendChild(script)
49
-
50
- return () => {
51
- eventTypes?.forEach(eventType => {
52
- script.removeEventListener(eventType, eventListener)
53
- })
54
-
55
- document.head.removeChild(script)
56
- }
57
- }, [eventTypes, attrs, props])
58
-
59
- return eventType
60
- }
61
-
62
- export function useCountdown(
63
- seconds: number,
64
- interval: number = 1,
65
- ): [number, Dispatch<SetStateAction<number>>] {
66
- if (seconds <= 0) throw Error("seconds must be > 0")
67
- if (interval <= 0) throw Error("interval must be > 0")
68
-
69
- const [_seconds, _setSeconds] = useState(seconds)
70
-
71
- useEffect(() => {
72
- const countdown = setInterval(() => {
73
- _setSeconds(seconds => {
74
- seconds = seconds - interval
75
- return seconds < 0 ? 0 : seconds
76
- })
77
- }, interval * 1000)
78
-
79
- return () => {
80
- clearInterval(countdown)
81
- }
82
- }, [interval])
83
-
84
- return [_seconds, _setSeconds]
85
- }
86
-
87
- export function useEventListener<EventType extends keyof HTMLElementEventMap>(
88
- element: HTMLElement,
89
- type: EventType,
90
- listener: (this: HTMLElement, ev: HTMLElementEventMap[EventType]) => any,
91
- kwArgs: {
92
- options?: boolean | AddEventListenerOptions
93
- deps?: DependencyList
94
- } = {},
95
- ): void {
96
- const { options, deps = [] } = kwArgs
97
-
98
- useEffect(
99
- () => {
100
- element.addEventListener(type, listener, options)
101
-
102
- return () => {
103
- element.removeEventListener(type, listener, options)
104
- }
105
- },
106
- // TODO: simplify this hook.
107
- // eslint-disable-next-line react-hooks/exhaustive-deps
108
- deps,
109
- )
110
- }
@@ -1,5 +0,0 @@
1
- export * from "./api"
2
- export * from "./auth"
3
- export * from "./form"
4
- export * from "./general"
5
- export * from "./router"
@@ -1,168 +0,0 @@
1
- import {
2
- type Location,
3
- type Params,
4
- type To,
5
- type NavigateOptions as _NavigateOptions,
6
- useLocation as _useLocation,
7
- useNavigate as _useNavigate,
8
- useParams as _useParams,
9
- useSearchParams as _useSearchParams,
10
- } from "react-router-dom"
11
- import { type ObjectShape, object as objectSchema } from "yup"
12
- import { type ReactNode, useEffect } from "react"
13
-
14
- import {
15
- type ObjectSchemaFromShape,
16
- type TryValidateSyncOnErrorRT,
17
- type TryValidateSyncOptions,
18
- type TryValidateSyncRT,
19
- tryValidateSync,
20
- } from "../utils/schema"
21
- import { type PageState } from "../components/page"
22
- import { type ReadOnly } from "../utils/router"
23
-
24
- export type NavigateOptions<
25
- State extends Record<string, any> = Record<string, any>,
26
- > = Omit<_NavigateOptions, "state"> & {
27
- state?: State & Partial<PageState>
28
- next?: boolean
29
- }
30
-
31
- export type Navigate = {
32
- <State extends Record<string, any> = Record<string, any>>(
33
- to: To,
34
- options?: NavigateOptions<State>,
35
- ): void
36
- (delta: number): void
37
- }
38
-
39
- export function useNavigate(): Navigate {
40
- const navigate = _useNavigate()
41
- const searchParams = useSearchParams()
42
-
43
- return (
44
- toOrDelta: To | number,
45
- options: (NavigateOptions & { next?: boolean }) | undefined = undefined,
46
- ) => {
47
- if (typeof toOrDelta === "number") navigate(toOrDelta)
48
- else {
49
- const { next = true, ..._options } = options || {}
50
-
51
- navigate(
52
- next && "next" in searchParams ? searchParams.next : toOrDelta,
53
- _options,
54
- )
55
- }
56
- }
57
- }
58
-
59
- export function useLocation<State = {}>() {
60
- return _useLocation() as Location<null | Partial<PageState & State>>
61
- }
62
-
63
- // -----------------------------------------------------------------------------
64
- // Use Search Params
65
- // -----------------------------------------------------------------------------
66
-
67
- export function useSearchParams(): { [k: string]: string }
68
-
69
- export function useSearchParams<
70
- OnErrorRT extends TryValidateSyncOnErrorRT<ObjectSchemaFromShape<Shape>>,
71
- Shape extends ObjectShape = {},
72
- >(
73
- shape: Shape,
74
- validateOptions?: TryValidateSyncOptions<
75
- ObjectSchemaFromShape<Shape>,
76
- OnErrorRT
77
- >,
78
- ): TryValidateSyncRT<ObjectSchemaFromShape<Shape>, OnErrorRT>
79
-
80
- export function useSearchParams<
81
- OnErrorRT extends TryValidateSyncOnErrorRT<ObjectSchemaFromShape<Shape>>,
82
- Shape extends ObjectShape = {},
83
- >(
84
- shape?: Shape,
85
- validateOptions?: TryValidateSyncOptions<
86
- ObjectSchemaFromShape<Shape>,
87
- OnErrorRT
88
- >,
89
- ) {
90
- const searchParams = Object.fromEntries(_useSearchParams()[0].entries())
91
- if (!shape) return searchParams
92
-
93
- return tryValidateSync(searchParams, objectSchema(shape), validateOptions)
94
- }
95
-
96
- // -----------------------------------------------------------------------------
97
- // Use Params
98
- // -----------------------------------------------------------------------------
99
-
100
- export function useParams(): ReadOnly<Params<string>>
101
-
102
- export function useParams<
103
- OnErrorRT extends TryValidateSyncOnErrorRT<ObjectSchemaFromShape<Shape>>,
104
- Shape extends ObjectShape = {},
105
- >(
106
- shape: Shape,
107
- validateOptions?: TryValidateSyncOptions<
108
- ObjectSchemaFromShape<Shape>,
109
- OnErrorRT
110
- >,
111
- ): TryValidateSyncRT<ObjectSchemaFromShape<Shape>, OnErrorRT>
112
-
113
- export function useParams<
114
- OnErrorRT extends TryValidateSyncOnErrorRT<ObjectSchemaFromShape<Shape>>,
115
- Shape extends ObjectShape = {},
116
- >(
117
- shape?: Shape,
118
- validateOptions?: TryValidateSyncOptions<
119
- ObjectSchemaFromShape<Shape>,
120
- OnErrorRT
121
- >,
122
- ) {
123
- const params = _useParams()
124
- if (!shape) return params
125
-
126
- return tryValidateSync(params, objectSchema(shape), validateOptions)
127
- }
128
-
129
- export function useParamsRequired<
130
- OnErrorRT extends TryValidateSyncOnErrorRT<ObjectSchemaFromShape<Shape>>,
131
- Shape extends ObjectShape = {},
132
- >({
133
- shape,
134
- children,
135
- onValidationError,
136
- onValidationSuccess = () => {},
137
- validateOptions,
138
- }: {
139
- shape: Shape
140
- children: (
141
- data: NonNullable<
142
- TryValidateSyncRT<ObjectSchemaFromShape<Shape>, OnErrorRT>
143
- >,
144
- ) => ReactNode
145
- onValidationError: (navigate: Navigate) => void
146
- onValidationSuccess?: (
147
- params: NonNullable<
148
- TryValidateSyncRT<ObjectSchemaFromShape<Shape>, OnErrorRT>
149
- >,
150
- ) => void
151
- validateOptions?: TryValidateSyncOptions<
152
- ObjectSchemaFromShape<Shape>,
153
- OnErrorRT
154
- >
155
- }) {
156
- const params = useParams(shape, validateOptions)
157
- const navigate = useNavigate()
158
-
159
- useEffect(
160
- () => {
161
- if (params) onValidationSuccess(params)
162
- else onValidationError(navigate)
163
- },
164
- [], // eslint-disable-line react-hooks/exhaustive-deps
165
- )
166
-
167
- return params ? children(params) : <></>
168
- }