@magmamath/students-features 1.8.4 → 1.8.6-rc.1

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 (260) hide show
  1. package/dist/commonjs/features/chatbot/components/ChatInput/FreeText/ChatTextInput.js.map +1 -1
  2. package/dist/commonjs/features/profile/Profile.js +90 -0
  3. package/dist/commonjs/features/profile/Profile.js.map +1 -0
  4. package/dist/commonjs/features/profile/components/actions/ProfileActions.js +70 -0
  5. package/dist/commonjs/features/profile/components/actions/ProfileActions.js.map +1 -0
  6. package/dist/commonjs/features/profile/components/cards/ProfileCard.js +57 -0
  7. package/dist/commonjs/features/profile/components/cards/ProfileCard.js.map +1 -0
  8. package/dist/commonjs/features/profile/components/info/AccountInfoCard.js +91 -0
  9. package/dist/commonjs/features/profile/components/info/AccountInfoCard.js.map +1 -0
  10. package/dist/commonjs/features/profile/components/modals/JoinClassModal.js +190 -0
  11. package/dist/commonjs/features/profile/components/modals/JoinClassModal.js.map +1 -0
  12. package/dist/commonjs/features/profile/components/modals/LogoutConfirmModal.js +61 -0
  13. package/dist/commonjs/features/profile/components/modals/LogoutConfirmModal.js.map +1 -0
  14. package/dist/commonjs/features/profile/components/modals/TranslationLanguageModal.js +128 -0
  15. package/dist/commonjs/features/profile/components/modals/TranslationLanguageModal.js.map +1 -0
  16. package/dist/commonjs/features/profile/components/settings/LanguageSettingsCard.js +22 -0
  17. package/dist/commonjs/features/profile/components/settings/LanguageSettingsCard.js.map +1 -0
  18. package/dist/commonjs/features/profile/components/settings/PreferencesCard.js +69 -0
  19. package/dist/commonjs/features/profile/components/settings/PreferencesCard.js.map +1 -0
  20. package/dist/commonjs/features/profile/components/settings/SettingRow.js +48 -0
  21. package/dist/commonjs/features/profile/components/settings/SettingRow.js.map +1 -0
  22. package/dist/commonjs/features/profile/components/settings/TextSettingsCard.js +77 -0
  23. package/dist/commonjs/features/profile/components/settings/TextSettingsCard.js.map +1 -0
  24. package/dist/commonjs/features/profile/components/settings/TextSizeSlider.js +119 -0
  25. package/dist/commonjs/features/profile/components/settings/TextSizeSlider.js.map +1 -0
  26. package/dist/commonjs/features/profile/components/settings/TextToSpeechRow.js +37 -0
  27. package/dist/commonjs/features/profile/components/settings/TextToSpeechRow.js.map +1 -0
  28. package/dist/commonjs/features/profile/components/settings/TranslationLanguageRow.js +62 -0
  29. package/dist/commonjs/features/profile/components/settings/TranslationLanguageRow.js.map +1 -0
  30. package/dist/commonjs/features/profile/context/ProfileContext.js +18 -0
  31. package/dist/commonjs/features/profile/context/ProfileContext.js.map +1 -0
  32. package/dist/commonjs/features/profile/index.js +80 -0
  33. package/dist/commonjs/features/profile/index.js.map +1 -0
  34. package/dist/commonjs/features/profile/model/ProfileApi.js +15 -0
  35. package/dist/commonjs/features/profile/model/ProfileApi.js.map +1 -0
  36. package/dist/commonjs/features/profile/model/ProfileJoinClassModel.js +60 -0
  37. package/dist/commonjs/features/profile/model/ProfileJoinClassModel.js.map +1 -0
  38. package/dist/commonjs/features/profile/model/ProfileLanguageModel.js +30 -0
  39. package/dist/commonjs/features/profile/model/ProfileLanguageModel.js.map +1 -0
  40. package/dist/commonjs/features/profile/model/ProfileModel.js +53 -0
  41. package/dist/commonjs/features/profile/model/ProfileModel.js.map +1 -0
  42. package/dist/commonjs/features/profile/model/profileModal.types.js +6 -0
  43. package/dist/commonjs/features/profile/model/profileModal.types.js.map +1 -0
  44. package/dist/commonjs/features/profile/shared/JoinClassError.js +16 -0
  45. package/dist/commonjs/features/profile/shared/JoinClassError.js.map +1 -0
  46. package/dist/commonjs/features/profile/shared/profile.constants.js +29 -0
  47. package/dist/commonjs/features/profile/shared/profile.constants.js.map +1 -0
  48. package/dist/commonjs/features/profile/shared/profile.helpers.js +40 -0
  49. package/dist/commonjs/features/profile/shared/profile.helpers.js.map +1 -0
  50. package/dist/commonjs/features/profile/shared/profile.types.js +6 -0
  51. package/dist/commonjs/features/profile/shared/profile.types.js.map +1 -0
  52. package/dist/commonjs/index.js +12 -0
  53. package/dist/commonjs/index.js.map +1 -1
  54. package/dist/commonjs/shared/hooks/useKeyboardOffset.js +54 -0
  55. package/dist/commonjs/shared/hooks/useKeyboardOffset.js.map +1 -0
  56. package/dist/module/features/chatbot/components/ChatInput/FreeText/ChatTextInput.js.map +1 -1
  57. package/dist/module/features/profile/Profile.js +84 -0
  58. package/dist/module/features/profile/Profile.js.map +1 -0
  59. package/dist/module/features/profile/components/actions/ProfileActions.js +64 -0
  60. package/dist/module/features/profile/components/actions/ProfileActions.js.map +1 -0
  61. package/dist/module/features/profile/components/cards/ProfileCard.js +51 -0
  62. package/dist/module/features/profile/components/cards/ProfileCard.js.map +1 -0
  63. package/dist/module/features/profile/components/info/AccountInfoCard.js +85 -0
  64. package/dist/module/features/profile/components/info/AccountInfoCard.js.map +1 -0
  65. package/dist/module/features/profile/components/modals/JoinClassModal.js +183 -0
  66. package/dist/module/features/profile/components/modals/JoinClassModal.js.map +1 -0
  67. package/dist/module/features/profile/components/modals/LogoutConfirmModal.js +55 -0
  68. package/dist/module/features/profile/components/modals/LogoutConfirmModal.js.map +1 -0
  69. package/dist/module/features/profile/components/modals/TranslationLanguageModal.js +122 -0
  70. package/dist/module/features/profile/components/modals/TranslationLanguageModal.js.map +1 -0
  71. package/dist/module/features/profile/components/settings/LanguageSettingsCard.js +16 -0
  72. package/dist/module/features/profile/components/settings/LanguageSettingsCard.js.map +1 -0
  73. package/dist/module/features/profile/components/settings/PreferencesCard.js +63 -0
  74. package/dist/module/features/profile/components/settings/PreferencesCard.js.map +1 -0
  75. package/dist/module/features/profile/components/settings/SettingRow.js +42 -0
  76. package/dist/module/features/profile/components/settings/SettingRow.js.map +1 -0
  77. package/dist/module/features/profile/components/settings/TextSettingsCard.js +71 -0
  78. package/dist/module/features/profile/components/settings/TextSettingsCard.js.map +1 -0
  79. package/dist/module/features/profile/components/settings/TextSizeSlider.js +112 -0
  80. package/dist/module/features/profile/components/settings/TextSizeSlider.js.map +1 -0
  81. package/dist/module/features/profile/components/settings/TextToSpeechRow.js +31 -0
  82. package/dist/module/features/profile/components/settings/TextToSpeechRow.js.map +1 -0
  83. package/dist/module/features/profile/components/settings/TranslationLanguageRow.js +56 -0
  84. package/dist/module/features/profile/components/settings/TranslationLanguageRow.js.map +1 -0
  85. package/dist/module/features/profile/context/ProfileContext.js +13 -0
  86. package/dist/module/features/profile/context/ProfileContext.js.map +1 -0
  87. package/dist/module/features/profile/index.js +11 -0
  88. package/dist/module/features/profile/index.js.map +1 -0
  89. package/dist/module/features/profile/model/ProfileApi.js +10 -0
  90. package/dist/module/features/profile/model/ProfileApi.js.map +1 -0
  91. package/dist/module/features/profile/model/ProfileJoinClassModel.js +55 -0
  92. package/dist/module/features/profile/model/ProfileJoinClassModel.js.map +1 -0
  93. package/dist/module/features/profile/model/ProfileLanguageModel.js +25 -0
  94. package/dist/module/features/profile/model/ProfileLanguageModel.js.map +1 -0
  95. package/dist/module/features/profile/model/ProfileModel.js +48 -0
  96. package/dist/module/features/profile/model/ProfileModel.js.map +1 -0
  97. package/dist/module/features/profile/model/profileModal.types.js +4 -0
  98. package/dist/module/features/profile/model/profileModal.types.js.map +1 -0
  99. package/dist/module/features/profile/shared/JoinClassError.js +11 -0
  100. package/dist/module/features/profile/shared/JoinClassError.js.map +1 -0
  101. package/dist/module/features/profile/shared/profile.constants.js +25 -0
  102. package/dist/module/features/profile/shared/profile.constants.js.map +1 -0
  103. package/dist/module/features/profile/shared/profile.helpers.js +32 -0
  104. package/dist/module/features/profile/shared/profile.helpers.js.map +1 -0
  105. package/dist/module/features/profile/shared/profile.types.js +4 -0
  106. package/dist/module/features/profile/shared/profile.types.js.map +1 -0
  107. package/dist/module/index.js +1 -0
  108. package/dist/module/index.js.map +1 -1
  109. package/dist/module/shared/hooks/useKeyboardOffset.js +49 -0
  110. package/dist/module/shared/hooks/useKeyboardOffset.js.map +1 -0
  111. package/dist/typescript/commonjs/features/chatbot/components/ChatInput/FreeText/ChatTextInput.d.ts.map +1 -1
  112. package/dist/typescript/commonjs/features/profile/Profile.d.ts +6 -0
  113. package/dist/typescript/commonjs/features/profile/Profile.d.ts.map +1 -0
  114. package/dist/typescript/commonjs/features/profile/__tests__/ProfileModel.test.d.ts +2 -0
  115. package/dist/typescript/commonjs/features/profile/__tests__/ProfileModel.test.d.ts.map +1 -0
  116. package/dist/typescript/commonjs/features/profile/__tests__/profile.helpers.test.d.ts +2 -0
  117. package/dist/typescript/commonjs/features/profile/__tests__/profile.helpers.test.d.ts.map +1 -0
  118. package/dist/typescript/commonjs/features/profile/components/actions/ProfileActions.d.ts +3 -0
  119. package/dist/typescript/commonjs/features/profile/components/actions/ProfileActions.d.ts.map +1 -0
  120. package/dist/typescript/commonjs/features/profile/components/cards/ProfileCard.d.ts +10 -0
  121. package/dist/typescript/commonjs/features/profile/components/cards/ProfileCard.d.ts.map +1 -0
  122. package/dist/typescript/commonjs/features/profile/components/info/AccountInfoCard.d.ts +3 -0
  123. package/dist/typescript/commonjs/features/profile/components/info/AccountInfoCard.d.ts.map +1 -0
  124. package/dist/typescript/commonjs/features/profile/components/modals/JoinClassModal.d.ts +4 -0
  125. package/dist/typescript/commonjs/features/profile/components/modals/JoinClassModal.d.ts.map +1 -0
  126. package/dist/typescript/commonjs/features/profile/components/modals/LogoutConfirmModal.d.ts +4 -0
  127. package/dist/typescript/commonjs/features/profile/components/modals/LogoutConfirmModal.d.ts.map +1 -0
  128. package/dist/typescript/commonjs/features/profile/components/modals/TranslationLanguageModal.d.ts +4 -0
  129. package/dist/typescript/commonjs/features/profile/components/modals/TranslationLanguageModal.d.ts.map +1 -0
  130. package/dist/typescript/commonjs/features/profile/components/settings/LanguageSettingsCard.d.ts +3 -0
  131. package/dist/typescript/commonjs/features/profile/components/settings/LanguageSettingsCard.d.ts.map +1 -0
  132. package/dist/typescript/commonjs/features/profile/components/settings/PreferencesCard.d.ts +3 -0
  133. package/dist/typescript/commonjs/features/profile/components/settings/PreferencesCard.d.ts.map +1 -0
  134. package/dist/typescript/commonjs/features/profile/components/settings/SettingRow.d.ts +11 -0
  135. package/dist/typescript/commonjs/features/profile/components/settings/SettingRow.d.ts.map +1 -0
  136. package/dist/typescript/commonjs/features/profile/components/settings/TextSettingsCard.d.ts +3 -0
  137. package/dist/typescript/commonjs/features/profile/components/settings/TextSettingsCard.d.ts.map +1 -0
  138. package/dist/typescript/commonjs/features/profile/components/settings/TextSizeSlider.d.ts +8 -0
  139. package/dist/typescript/commonjs/features/profile/components/settings/TextSizeSlider.d.ts.map +1 -0
  140. package/dist/typescript/commonjs/features/profile/components/settings/TextToSpeechRow.d.ts +3 -0
  141. package/dist/typescript/commonjs/features/profile/components/settings/TextToSpeechRow.d.ts.map +1 -0
  142. package/dist/typescript/commonjs/features/profile/components/settings/TranslationLanguageRow.d.ts +3 -0
  143. package/dist/typescript/commonjs/features/profile/components/settings/TranslationLanguageRow.d.ts.map +1 -0
  144. package/dist/typescript/commonjs/features/profile/context/ProfileContext.d.ts +13 -0
  145. package/dist/typescript/commonjs/features/profile/context/ProfileContext.d.ts.map +1 -0
  146. package/dist/typescript/commonjs/features/profile/index.d.ts +12 -0
  147. package/dist/typescript/commonjs/features/profile/index.d.ts.map +1 -0
  148. package/dist/typescript/commonjs/features/profile/model/ProfileApi.d.ts +11 -0
  149. package/dist/typescript/commonjs/features/profile/model/ProfileApi.d.ts.map +1 -0
  150. package/dist/typescript/commonjs/features/profile/model/ProfileJoinClassModel.d.ts +14 -0
  151. package/dist/typescript/commonjs/features/profile/model/ProfileJoinClassModel.d.ts.map +1 -0
  152. package/dist/typescript/commonjs/features/profile/model/ProfileLanguageModel.d.ts +13 -0
  153. package/dist/typescript/commonjs/features/profile/model/ProfileLanguageModel.d.ts.map +1 -0
  154. package/dist/typescript/commonjs/features/profile/model/ProfileModel.d.ts +26 -0
  155. package/dist/typescript/commonjs/features/profile/model/ProfileModel.d.ts.map +1 -0
  156. package/dist/typescript/commonjs/features/profile/model/profileModal.types.d.ts +14 -0
  157. package/dist/typescript/commonjs/features/profile/model/profileModal.types.d.ts.map +1 -0
  158. package/dist/typescript/commonjs/features/profile/shared/JoinClassError.d.ts +5 -0
  159. package/dist/typescript/commonjs/features/profile/shared/JoinClassError.d.ts.map +1 -0
  160. package/dist/typescript/commonjs/features/profile/shared/profile.constants.d.ts +20 -0
  161. package/dist/typescript/commonjs/features/profile/shared/profile.constants.d.ts.map +1 -0
  162. package/dist/typescript/commonjs/features/profile/shared/profile.helpers.d.ts +6 -0
  163. package/dist/typescript/commonjs/features/profile/shared/profile.helpers.d.ts.map +1 -0
  164. package/dist/typescript/commonjs/features/profile/shared/profile.types.d.ts +27 -0
  165. package/dist/typescript/commonjs/features/profile/shared/profile.types.d.ts.map +1 -0
  166. package/dist/typescript/commonjs/index.d.ts +1 -0
  167. package/dist/typescript/commonjs/index.d.ts.map +1 -1
  168. package/dist/typescript/commonjs/shared/hooks/useKeyboardOffset.d.ts +11 -0
  169. package/dist/typescript/commonjs/shared/hooks/useKeyboardOffset.d.ts.map +1 -0
  170. package/dist/typescript/module/features/chatbot/components/ChatInput/FreeText/ChatTextInput.d.ts.map +1 -1
  171. package/dist/typescript/module/features/profile/Profile.d.ts +6 -0
  172. package/dist/typescript/module/features/profile/Profile.d.ts.map +1 -0
  173. package/dist/typescript/module/features/profile/__tests__/ProfileModel.test.d.ts +2 -0
  174. package/dist/typescript/module/features/profile/__tests__/ProfileModel.test.d.ts.map +1 -0
  175. package/dist/typescript/module/features/profile/__tests__/profile.helpers.test.d.ts +2 -0
  176. package/dist/typescript/module/features/profile/__tests__/profile.helpers.test.d.ts.map +1 -0
  177. package/dist/typescript/module/features/profile/components/actions/ProfileActions.d.ts +3 -0
  178. package/dist/typescript/module/features/profile/components/actions/ProfileActions.d.ts.map +1 -0
  179. package/dist/typescript/module/features/profile/components/cards/ProfileCard.d.ts +10 -0
  180. package/dist/typescript/module/features/profile/components/cards/ProfileCard.d.ts.map +1 -0
  181. package/dist/typescript/module/features/profile/components/info/AccountInfoCard.d.ts +3 -0
  182. package/dist/typescript/module/features/profile/components/info/AccountInfoCard.d.ts.map +1 -0
  183. package/dist/typescript/module/features/profile/components/modals/JoinClassModal.d.ts +4 -0
  184. package/dist/typescript/module/features/profile/components/modals/JoinClassModal.d.ts.map +1 -0
  185. package/dist/typescript/module/features/profile/components/modals/LogoutConfirmModal.d.ts +4 -0
  186. package/dist/typescript/module/features/profile/components/modals/LogoutConfirmModal.d.ts.map +1 -0
  187. package/dist/typescript/module/features/profile/components/modals/TranslationLanguageModal.d.ts +4 -0
  188. package/dist/typescript/module/features/profile/components/modals/TranslationLanguageModal.d.ts.map +1 -0
  189. package/dist/typescript/module/features/profile/components/settings/LanguageSettingsCard.d.ts +3 -0
  190. package/dist/typescript/module/features/profile/components/settings/LanguageSettingsCard.d.ts.map +1 -0
  191. package/dist/typescript/module/features/profile/components/settings/PreferencesCard.d.ts +3 -0
  192. package/dist/typescript/module/features/profile/components/settings/PreferencesCard.d.ts.map +1 -0
  193. package/dist/typescript/module/features/profile/components/settings/SettingRow.d.ts +11 -0
  194. package/dist/typescript/module/features/profile/components/settings/SettingRow.d.ts.map +1 -0
  195. package/dist/typescript/module/features/profile/components/settings/TextSettingsCard.d.ts +3 -0
  196. package/dist/typescript/module/features/profile/components/settings/TextSettingsCard.d.ts.map +1 -0
  197. package/dist/typescript/module/features/profile/components/settings/TextSizeSlider.d.ts +8 -0
  198. package/dist/typescript/module/features/profile/components/settings/TextSizeSlider.d.ts.map +1 -0
  199. package/dist/typescript/module/features/profile/components/settings/TextToSpeechRow.d.ts +3 -0
  200. package/dist/typescript/module/features/profile/components/settings/TextToSpeechRow.d.ts.map +1 -0
  201. package/dist/typescript/module/features/profile/components/settings/TranslationLanguageRow.d.ts +3 -0
  202. package/dist/typescript/module/features/profile/components/settings/TranslationLanguageRow.d.ts.map +1 -0
  203. package/dist/typescript/module/features/profile/context/ProfileContext.d.ts +13 -0
  204. package/dist/typescript/module/features/profile/context/ProfileContext.d.ts.map +1 -0
  205. package/dist/typescript/module/features/profile/index.d.ts +12 -0
  206. package/dist/typescript/module/features/profile/index.d.ts.map +1 -0
  207. package/dist/typescript/module/features/profile/model/ProfileApi.d.ts +11 -0
  208. package/dist/typescript/module/features/profile/model/ProfileApi.d.ts.map +1 -0
  209. package/dist/typescript/module/features/profile/model/ProfileJoinClassModel.d.ts +14 -0
  210. package/dist/typescript/module/features/profile/model/ProfileJoinClassModel.d.ts.map +1 -0
  211. package/dist/typescript/module/features/profile/model/ProfileLanguageModel.d.ts +13 -0
  212. package/dist/typescript/module/features/profile/model/ProfileLanguageModel.d.ts.map +1 -0
  213. package/dist/typescript/module/features/profile/model/ProfileModel.d.ts +26 -0
  214. package/dist/typescript/module/features/profile/model/ProfileModel.d.ts.map +1 -0
  215. package/dist/typescript/module/features/profile/model/profileModal.types.d.ts +14 -0
  216. package/dist/typescript/module/features/profile/model/profileModal.types.d.ts.map +1 -0
  217. package/dist/typescript/module/features/profile/shared/JoinClassError.d.ts +5 -0
  218. package/dist/typescript/module/features/profile/shared/JoinClassError.d.ts.map +1 -0
  219. package/dist/typescript/module/features/profile/shared/profile.constants.d.ts +20 -0
  220. package/dist/typescript/module/features/profile/shared/profile.constants.d.ts.map +1 -0
  221. package/dist/typescript/module/features/profile/shared/profile.helpers.d.ts +6 -0
  222. package/dist/typescript/module/features/profile/shared/profile.helpers.d.ts.map +1 -0
  223. package/dist/typescript/module/features/profile/shared/profile.types.d.ts +27 -0
  224. package/dist/typescript/module/features/profile/shared/profile.types.d.ts.map +1 -0
  225. package/dist/typescript/module/index.d.ts +1 -0
  226. package/dist/typescript/module/index.d.ts.map +1 -1
  227. package/dist/typescript/module/shared/hooks/useKeyboardOffset.d.ts +11 -0
  228. package/dist/typescript/module/shared/hooks/useKeyboardOffset.d.ts.map +1 -0
  229. package/package.json +4 -2
  230. package/src/features/chatbot/components/ChatInput/FreeText/ChatTextInput.tsx +0 -1
  231. package/src/features/profile/Profile.tsx +83 -0
  232. package/src/features/profile/__tests__/ProfileModel.test.ts +99 -0
  233. package/src/features/profile/__tests__/profile.helpers.test.ts +57 -0
  234. package/src/features/profile/components/actions/ProfileActions.tsx +68 -0
  235. package/src/features/profile/components/cards/ProfileCard.tsx +53 -0
  236. package/src/features/profile/components/info/AccountInfoCard.tsx +74 -0
  237. package/src/features/profile/components/modals/JoinClassModal.tsx +180 -0
  238. package/src/features/profile/components/modals/LogoutConfirmModal.tsx +64 -0
  239. package/src/features/profile/components/modals/TranslationLanguageModal.tsx +126 -0
  240. package/src/features/profile/components/settings/LanguageSettingsCard.tsx +16 -0
  241. package/src/features/profile/components/settings/PreferencesCard.tsx +67 -0
  242. package/src/features/profile/components/settings/SettingRow.tsx +42 -0
  243. package/src/features/profile/components/settings/TextSettingsCard.tsx +64 -0
  244. package/src/features/profile/components/settings/TextSizeSlider.tsx +117 -0
  245. package/src/features/profile/components/settings/TextToSpeechRow.tsx +29 -0
  246. package/src/features/profile/components/settings/TranslationLanguageRow.tsx +60 -0
  247. package/src/features/profile/context/ProfileContext.tsx +24 -0
  248. package/src/features/profile/index.ts +11 -0
  249. package/src/features/profile/model/ProfileApi.ts +23 -0
  250. package/src/features/profile/model/ProfileJoinClassModel.ts +58 -0
  251. package/src/features/profile/model/ProfileLanguageModel.ts +26 -0
  252. package/src/features/profile/model/ProfileModel.ts +57 -0
  253. package/src/features/profile/model/profileModal.types.ts +16 -0
  254. package/src/features/profile/shared/JoinClassError.ts +7 -0
  255. package/src/features/profile/shared/profile.constants.ts +23 -0
  256. package/src/features/profile/shared/profile.helpers.ts +36 -0
  257. package/src/features/profile/shared/profile.types.ts +34 -0
  258. package/src/i18n/.generated/schema.json +9 -1
  259. package/src/index.ts +1 -0
  260. package/src/shared/hooks/useKeyboardOffset.ts +57 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useKeyboardOffset.d.ts","sourceRoot":"","sources":["../../../../../src/shared/hooks/useKeyboardOffset.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAA0C,MAAM,cAAc,CAAA;AAQ/E;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,sBAuC7B,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magmamath/students-features",
3
- "version": "1.8.4",
3
+ "version": "1.8.6-rc.1",
4
4
  "description": "Magmamath features library",
5
5
  "source": "src/index.ts",
6
6
  "main": "src/index.ts",
@@ -52,6 +52,7 @@
52
52
  "@magmamath/students-qa": "*",
53
53
  "@matteappen/skills-utils": "^3.7.1",
54
54
  "@react-native-masked-view/masked-view": "*",
55
+ "react-native-confirmation-code-field": ">=8.0.0",
55
56
  "d3-shape": "3.2.0",
56
57
  "effector": "^23.2.3",
57
58
  "effector-action": "^1.2.2",
@@ -74,7 +75,7 @@
74
75
  "devDependencies": {
75
76
  "@magmamath/frontend-config": "1.7.2",
76
77
  "@magmamath/react-native-draw-board": "1.8.3",
77
- "@magmamath/react-native-ui": "0.5.9",
78
+ "@magmamath/react-native-ui": "0.5.13",
78
79
  "@magmamath/students-qa": "0.2.3",
79
80
  "@matteappen/skills-utils": "^3.7.1",
80
81
  "@react-native-masked-view/masked-view": "^0.3.2",
@@ -102,6 +103,7 @@
102
103
  "react": "18.3.1",
103
104
  "react-i18next": "^15.5.2",
104
105
  "react-native": "0.76.3",
106
+ "react-native-confirmation-code-field": "^8.0.0",
105
107
  "react-native-builder-bob": "^0.33.3",
106
108
  "react-native-gesture-handler": "^2.28.0",
107
109
  "react-native-linear-gradient": "^2.8.3",
@@ -8,7 +8,6 @@ import {
8
8
  TextInputFocusEventData,
9
9
  } from 'react-native'
10
10
  import { COLORS, FONT_FAMILY, IS_WEB, SPACING } from '@magmamath/react-native-ui'
11
- import { INPUT_WIDTH } from '../../../constants'
12
11
  import { ColorScheme } from '@magmamath/students-features'
13
12
  import { useKeyBoard } from '../../../../../shared/hooks/useKeyboard'
14
13
  import { KeyboardKeys } from '../../../../../lib/constants'
@@ -0,0 +1,83 @@
1
+ import React from 'react'
2
+ import { ScrollView, StyleSheet, useWindowDimensions, View } from 'react-native'
3
+ import { COLORS, SPACING } from '@magmamath/react-native-ui'
4
+
5
+ import { isWeb } from '../../shared/common.constants'
6
+ import { ProfileActions } from './components/actions/ProfileActions'
7
+ import { AccountInfoCard } from './components/info/AccountInfoCard'
8
+ import { LanguageSettingsCard } from './components/settings/LanguageSettingsCard'
9
+ import { PreferencesCard } from './components/settings/PreferencesCard'
10
+ import { TextSettingsCard } from './components/settings/TextSettingsCard'
11
+ import { ProfileProvider, type ProfileContextValue } from './context/ProfileContext'
12
+
13
+ type ProfileProps = ProfileContextValue
14
+
15
+ const TWO_COLUMN_BREAKPOINT = 768
16
+ const COLUMN_MAX_WIDTH = 560
17
+
18
+ export const Profile = (props: ProfileProps) => {
19
+ const { showSolveMode, versionInfo } = props
20
+ const { width } = useWindowDimensions()
21
+ const isTwoColumn = width >= TWO_COLUMN_BREAKPOINT
22
+
23
+ return (
24
+ <ProfileProvider value={props}>
25
+ <ScrollView
26
+ style={styles.container}
27
+ contentContainerStyle={styles.scroll}
28
+ scrollEnabled={isWeb}
29
+ showsVerticalScrollIndicator={isWeb}
30
+ >
31
+ <View style={[styles.columns, !isTwoColumn && styles.columnsStacked]}>
32
+ <View style={[styles.column, isTwoColumn ? styles.columnWide : styles.columnStacked]}>
33
+ <LanguageSettingsCard />
34
+ <TextSettingsCard />
35
+ </View>
36
+ <View style={[styles.column, isTwoColumn ? styles.columnWide : styles.columnStacked]}>
37
+ {showSolveMode && <PreferencesCard />}
38
+ <AccountInfoCard />
39
+ </View>
40
+ </View>
41
+ <ProfileActions />
42
+ {versionInfo ? <View style={styles.version}>{versionInfo}</View> : null}
43
+ </ScrollView>
44
+ </ProfileProvider>
45
+ )
46
+ }
47
+
48
+ const styles = StyleSheet.create({
49
+ container: {
50
+ backgroundColor: COLORS.NEUTRAL_3,
51
+ flex: 1,
52
+ },
53
+ scroll: {
54
+ flexGrow: 1,
55
+ gap: SPACING['800'],
56
+ paddingHorizontal: SPACING['800'],
57
+ paddingVertical: SPACING['600'],
58
+ },
59
+ columns: {
60
+ flexDirection: 'row',
61
+ gap: SPACING['600'],
62
+ justifyContent: 'center',
63
+ width: '100%',
64
+ },
65
+ columnsStacked: {
66
+ alignItems: 'center',
67
+ flexDirection: 'column',
68
+ },
69
+ column: {
70
+ gap: SPACING['600'],
71
+ maxWidth: COLUMN_MAX_WIDTH,
72
+ },
73
+ columnWide: {
74
+ flex: 1,
75
+ },
76
+ columnStacked: {
77
+ width: '100%',
78
+ },
79
+ version: {
80
+ alignSelf: 'flex-end',
81
+ marginTop: 'auto',
82
+ },
83
+ })
@@ -0,0 +1,99 @@
1
+ import { createStore } from 'effector'
2
+ import { Language } from '@magmamath/frontend-config'
3
+
4
+ import { JoinClassError } from '../shared/JoinClassError'
5
+ import { ProfileModel } from '../model/ProfileModel'
6
+ import { JoinClassStatus, ProfileCanvasType } from '../shared/profile.constants'
7
+ import type { JoinClassResult, ProfileSettings, ProfileUser } from '../shared/profile.types'
8
+
9
+ const baseSettings: ProfileSettings = {
10
+ textToSpeech: false,
11
+ textSize: 16,
12
+ translationLanguage: null,
13
+ canvasType: ProfileCanvasType.DRAWING,
14
+ isTranslationLocked: false,
15
+ }
16
+
17
+ const createModel = (joinClass = jest.fn<Promise<JoinClassResult>, [unknown]>().mockResolvedValue({ name: 'Math' })) => {
18
+ const updateSettings = jest.fn().mockResolvedValue(undefined)
19
+ const notify = jest.fn()
20
+ const model = new ProfileModel({
21
+ api: { updateSettings, joinClass },
22
+ notify,
23
+ $user: createStore<ProfileUser | null>(null),
24
+ $settings: createStore<ProfileSettings>(baseSettings),
25
+ })
26
+ return { model, updateSettings, joinClass, notify }
27
+ }
28
+
29
+ describe('ProfileModel settings', () => {
30
+ it('persists text-to-speech changes', () => {
31
+ const { model, updateSettings } = createModel()
32
+ model.setTextToSpeech(true)
33
+ expect(updateSettings).toHaveBeenCalledWith({ textToSpeech: true })
34
+ })
35
+
36
+ it('persists text size changes', () => {
37
+ const { model, updateSettings } = createModel()
38
+ model.setTextSize(24)
39
+ expect(updateSettings).toHaveBeenCalledWith({ textSize: 24 })
40
+ })
41
+
42
+ it('persists canvas type changes', () => {
43
+ const { model, updateSettings } = createModel()
44
+ model.setCanvasType(ProfileCanvasType.SPREADSHEET)
45
+ expect(updateSettings).toHaveBeenCalledWith({ canvasType: ProfileCanvasType.SPREADSHEET })
46
+ })
47
+
48
+ it('persists translation language selection', () => {
49
+ const { model, updateSettings } = createModel()
50
+ model.language.selectLanguage(Language.FRENCH)
51
+ expect(updateSettings).toHaveBeenCalledWith({ translationLanguage: Language.FRENCH })
52
+ })
53
+
54
+ it('filters the language list by search', () => {
55
+ const { model } = createModel()
56
+ model.language.setSearch('french')
57
+ expect(model.language.$languages.getState()).toEqual([Language.FRENCH])
58
+ })
59
+ })
60
+
61
+ describe('ProfileModel join class', () => {
62
+ it('ignores incomplete codes', () => {
63
+ const { model, joinClass } = createModel()
64
+ model.joinClass.setCode('ABC')
65
+ expect(model.joinClass.$isComplete.getState()).toBe(false)
66
+ model.joinClass.submit()
67
+ expect(joinClass).not.toHaveBeenCalled()
68
+ })
69
+
70
+ it('submits a complete code, notifies success and resets', async () => {
71
+ const { model, joinClass, notify } = createModel()
72
+ model.joinClass.setCode('ABCDEF')
73
+ expect(model.joinClass.$isComplete.getState()).toBe(true)
74
+
75
+ model.joinClass.submit()
76
+ expect(joinClass).toHaveBeenCalledWith({ classCode: 'ABCDEF' })
77
+
78
+ await new Promise((resolve) => setTimeout(resolve, 0))
79
+ expect(notify).toHaveBeenCalledWith(
80
+ expect.objectContaining({ status: JoinClassStatus.SUCCESS })
81
+ )
82
+ expect(model.joinClass.$code.getState()).toBe('')
83
+ })
84
+
85
+ it('notifies an error when the request fails', async () => {
86
+ const joinClass = jest
87
+ .fn<Promise<JoinClassResult>, [unknown]>()
88
+ .mockRejectedValue(new JoinClassError(1021))
89
+ const { model, notify } = createModel(joinClass)
90
+
91
+ model.joinClass.setCode('ABCDEF')
92
+ model.joinClass.submit()
93
+
94
+ await new Promise((resolve) => setTimeout(resolve, 0))
95
+ expect(notify).toHaveBeenCalledWith(
96
+ expect.objectContaining({ status: JoinClassStatus.ERROR })
97
+ )
98
+ })
99
+ })
@@ -0,0 +1,57 @@
1
+ import { Language } from '@magmamath/frontend-config'
2
+
3
+ import { JoinClassErrorCode } from '../shared/profile.constants'
4
+ import {
5
+ getAvailableLanguages,
6
+ getJoinClassErrorMessage,
7
+ snapTextSize,
8
+ } from '../shared/profile.helpers'
9
+
10
+ // Echo the key so the error-code → key mapping is observable (i18next is not
11
+ // initialised under jest, so the real getText returns undefined).
12
+ jest.mock('../../../i18n/i18n', () => ({
13
+ getText: (key: string) => key,
14
+ }))
15
+
16
+ describe('profile.helpers', () => {
17
+ describe('snapTextSize', () => {
18
+ it('snaps to the nearest step and clamps to range', () => {
19
+ expect(snapTextSize(16)).toBe(16)
20
+ expect(snapTextSize(17)).toBe(16)
21
+ expect(snapTextSize(18)).toBe(20)
22
+ expect(snapTextSize(33)).toBe(32)
23
+ expect(snapTextSize(10)).toBe(16)
24
+ })
25
+ })
26
+
27
+ describe('getJoinClassErrorMessage', () => {
28
+ it('maps each known backend code to a distinct message', () => {
29
+ const messages = [
30
+ getJoinClassErrorMessage(JoinClassErrorCode.CLASS_NOT_FOUND),
31
+ getJoinClassErrorMessage(JoinClassErrorCode.ALREADY_JOINED),
32
+ getJoinClassErrorMessage(JoinClassErrorCode.OTHER_DISTRICT),
33
+ ]
34
+ expect(new Set(messages).size).toBe(messages.length)
35
+ })
36
+
37
+ it('falls back to the default message for unknown codes and null', () => {
38
+ const fallback = getJoinClassErrorMessage(null)
39
+ expect(getJoinClassErrorMessage(9999)).toBe(fallback)
40
+ expect(getJoinClassErrorMessage(JoinClassErrorCode.CLASS_NOT_FOUND)).not.toBe(fallback)
41
+ })
42
+ })
43
+
44
+ describe('getAvailableLanguages', () => {
45
+ it('lists priority languages first', () => {
46
+ const languages = getAvailableLanguages()
47
+ expect(languages[0]).toBe(Language.SPANISH)
48
+ expect(languages.length).toBeGreaterThan(5)
49
+ })
50
+
51
+ it('filters by search query', () => {
52
+ const languages = getAvailableLanguages('span')
53
+ expect(languages).toContain(Language.SPANISH)
54
+ expect(languages.every((language) => language.toLowerCase().includes('span'))).toBe(true)
55
+ })
56
+ })
57
+ })
@@ -0,0 +1,68 @@
1
+ import React from 'react'
2
+ import { StyleSheet, View } from 'react-native'
3
+ import {
4
+ ButtonColor,
5
+ ButtonSize,
6
+ ButtonVariant,
7
+ ReanimatedButton,
8
+ SPACING,
9
+ } from '@magmamath/react-native-ui'
10
+ import { TEST_IDS } from '@magmamath/students-qa'
11
+
12
+ import { useText } from '../../../../i18n/i18n'
13
+ import { useProfile } from '../../context/ProfileContext'
14
+
15
+ export const ProfileActions = () => {
16
+ const t = useText()
17
+ const { onLogout, onOpenJoinClass } = useProfile()
18
+
19
+ return (
20
+ <View style={styles.container}>
21
+ <View style={styles.leftHalf}>
22
+ <ReanimatedButton
23
+ variant={ButtonVariant.SECONDARY}
24
+ colorScheme={ButtonColor.WHITE}
25
+ size={ButtonSize.LARGE}
26
+ onPress={onOpenJoinClass}
27
+ testID={TEST_IDS.JOIN_NEW_CLASS_BUTTON}
28
+ style={buttonStyle}
29
+ >
30
+ {t('profile.joinNewClass')}
31
+ </ReanimatedButton>
32
+ </View>
33
+ <View style={styles.rightHalf}>
34
+ <ReanimatedButton
35
+ variant={ButtonVariant.PRIMARY}
36
+ colorScheme={ButtonColor.ORANGE}
37
+ size={ButtonSize.LARGE}
38
+ onPress={onLogout}
39
+ testID={TEST_IDS.LOGOUT_BUTTON}
40
+ style={buttonStyle}
41
+ >
42
+ {t('profile.logOut')}
43
+ </ReanimatedButton>
44
+ </View>
45
+ </View>
46
+ )
47
+ }
48
+
49
+ const styles = StyleSheet.create({
50
+ container: {
51
+ flexDirection: 'row',
52
+ gap: SPACING['600'],
53
+ width: '100%',
54
+ },
55
+ leftHalf: {
56
+ alignItems: 'flex-end',
57
+ flex: 1,
58
+ },
59
+ rightHalf: {
60
+ alignItems: 'flex-start',
61
+ flex: 1,
62
+ },
63
+ text: {
64
+ textTransform: 'uppercase',
65
+ },
66
+ })
67
+
68
+ const buttonStyle = { text: styles.text }
@@ -0,0 +1,53 @@
1
+ import React, { ReactNode } from 'react'
2
+ import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'
3
+ import {
4
+ BORDER_RADIUS,
5
+ COLORS,
6
+ HeadingVariants,
7
+ SHADOWS,
8
+ SPACING,
9
+ Typography,
10
+ } from '@magmamath/react-native-ui'
11
+
12
+ type ProfileCardProps = {
13
+ title: string
14
+ children: ReactNode
15
+ style?: StyleProp<ViewStyle>
16
+ }
17
+
18
+ export const ProfileCard = ({ title, children, style }: ProfileCardProps) => (
19
+ <View style={[styles.card, style]}>
20
+ <View style={styles.header}>
21
+ <Typography variant={HeadingVariants.H9} style={styles.title}>
22
+ {title}
23
+ </Typography>
24
+ </View>
25
+ <View style={styles.divider} />
26
+ <View style={styles.body}>{children}</View>
27
+ </View>
28
+ )
29
+
30
+ const styles = StyleSheet.create({
31
+ card: {
32
+ backgroundColor: COLORS.NEUTRAL_1,
33
+ borderRadius: BORDER_RADIUS['400'],
34
+ padding: SPACING['400'],
35
+ ...SHADOWS['1'],
36
+ },
37
+ header: {
38
+ paddingLeft: SPACING['200'],
39
+ },
40
+ title: {
41
+ color: COLORS.NEUTRAL_7,
42
+ },
43
+ divider: {
44
+ backgroundColor: COLORS.NEUTRAL_3,
45
+ height: 1,
46
+ marginHorizontal: SPACING['100'],
47
+ marginVertical: SPACING['200'],
48
+ },
49
+ body: {
50
+ gap: SPACING['400'],
51
+ justifyContent: 'center',
52
+ },
53
+ })
@@ -0,0 +1,74 @@
1
+ import React from 'react'
2
+ import { useUnit } from 'effector-react'
3
+ import { StyleSheet, View } from 'react-native'
4
+ import { COLORS, HeadingVariants, InfoIcon, SPACING, Typography } from '@magmamath/react-native-ui'
5
+
6
+ import { useText } from '../../../../i18n/i18n'
7
+ import { useProfile } from '../../context/ProfileContext'
8
+ import { ProfileCard } from '../cards/ProfileCard'
9
+
10
+ const NOTE_ICON_SIZE = 20
11
+
12
+ type InfoFieldProps = {
13
+ label: string
14
+ value: string
15
+ }
16
+
17
+ const InfoField = ({ label, value }: InfoFieldProps) => (
18
+ <View style={styles.field}>
19
+ <Typography variant={HeadingVariants.H8} style={styles.label}>
20
+ {label}
21
+ </Typography>
22
+ <Typography variant={HeadingVariants.H6} style={styles.value}>
23
+ {value}
24
+ </Typography>
25
+ </View>
26
+ )
27
+
28
+ export const AccountInfoCard = () => {
29
+ const t = useText()
30
+ const { model } = useProfile()
31
+ const user = useUnit(model.$user)
32
+
33
+ return (
34
+ <ProfileCard title={t('profile.accountInfo')}>
35
+ <InfoField label={t('profile.fullName')} value={user?.fullName ?? ''} />
36
+ <InfoField label={t('profile.username')} value={user?.username ?? ''} />
37
+ <InfoField label={t('profile.school')} value={user?.schoolName ?? ''} />
38
+ <View style={styles.noteDivider} />
39
+ <View style={styles.note}>
40
+ <InfoIcon size={NOTE_ICON_SIZE} color={COLORS.NEUTRAL_7} />
41
+ <Typography variant={HeadingVariants.H8} style={styles.noteText}>
42
+ {t('profile.changePasswordNote')}
43
+ </Typography>
44
+ </View>
45
+ </ProfileCard>
46
+ )
47
+ }
48
+
49
+ const styles = StyleSheet.create({
50
+ field: {
51
+ gap: SPACING['100'],
52
+ paddingHorizontal: SPACING['100'],
53
+ },
54
+ label: {
55
+ color: COLORS.NEUTRAL_7,
56
+ },
57
+ value: {
58
+ color: COLORS.NEUTRAL_9,
59
+ },
60
+ noteDivider: {
61
+ backgroundColor: COLORS.NEUTRAL_3,
62
+ height: 1,
63
+ marginHorizontal: SPACING['100'],
64
+ },
65
+ note: {
66
+ alignItems: 'center',
67
+ flexDirection: 'row',
68
+ gap: SPACING['200'],
69
+ },
70
+ noteText: {
71
+ color: COLORS.NEUTRAL_7,
72
+ flexShrink: 1,
73
+ },
74
+ })
@@ -0,0 +1,180 @@
1
+ import React, { useEffect, useMemo } from 'react'
2
+ import { Animated, StyleSheet, Text, useWindowDimensions, View } from 'react-native'
3
+ import { useUnit } from 'effector-react'
4
+ import { CodeField, Cursor, useClearByFocusCell } from 'react-native-confirmation-code-field'
5
+ import {
6
+ BaseModal,
7
+ Button,
8
+ ButtonColor,
9
+ ButtonSize,
10
+ ButtonVariant,
11
+ COLORS,
12
+ CrossIcon,
13
+ FONT_FAMILY,
14
+ ReanimatedButton,
15
+ SPACING,
16
+ } from '@magmamath/react-native-ui'
17
+ import { TEST_IDS } from '@magmamath/students-qa'
18
+
19
+ import { useText } from '../../../../i18n/i18n'
20
+ import { useKeyboardOffset } from '../../../../shared/hooks/useKeyboardOffset'
21
+ import { isWeb } from '../../../../shared/common.constants'
22
+ import { CLASS_CODE_LENGTH } from '../../shared/profile.constants'
23
+ import type { ProfileModalProps } from '../../model/profileModal.types'
24
+
25
+ const CODE_PLACEHOLDER = '0'
26
+ const MAX_MODAL_WIDTH = 1044
27
+ const SCREEN_MARGIN = 48
28
+ const CELL_GAP = SPACING['400']
29
+ const MODAL_H_PADDING = SPACING['800']
30
+ const CELL_ASPECT = 0.95
31
+ const CELL_RADIUS_RATIO = 0.24 // 36 / 150 in the design
32
+ const CELL_FONT_RATIO = 0.42 // 64 / 150 in the design
33
+ const MAX_CELL_RADIUS = 36
34
+ const CLOSE_ICON_SIZE = 18
35
+ const KEYBOARD_LIFT_RATIO = 2
36
+
37
+ export const JoinClassModal = ({ modal: { closeModal, params } }: ProfileModalProps) => {
38
+ const t = useText()
39
+ const { model } = params
40
+ const { width } = useWindowDimensions()
41
+ const { code, isComplete, isPending } = useUnit({
42
+ code: model.joinClass.$code,
43
+ isComplete: model.joinClass.$isComplete,
44
+ isPending: model.api.joinClassFx.pending,
45
+ })
46
+
47
+ const modalWidth = Math.min(MAX_MODAL_WIDTH, width - SCREEN_MARGIN)
48
+ const cellSize =
49
+ (modalWidth - MODAL_H_PADDING * 2 - CELL_GAP * (CLASS_CODE_LENGTH - 1)) / CLASS_CODE_LENGTH
50
+ const cellRadius = Math.min(MAX_CELL_RADIUS, cellSize * CELL_RADIUS_RATIO)
51
+ const cellFontSize = cellSize * CELL_FONT_RATIO
52
+
53
+ const setCode = (value: string) => model.joinClass.setCode(value.toUpperCase())
54
+ const [cellProps, getCellOnLayoutHandler] = useClearByFocusCell({ value: code, setValue: setCode })
55
+
56
+ useEffect(() => {
57
+ const subscription = model.api.joinClassFx.finally.watch(() => {
58
+ model.joinClass.reset()
59
+ closeModal()
60
+ })
61
+ return () => subscription.unsubscribe()
62
+ }, [model, closeModal])
63
+
64
+ const closeAndReset = () => {
65
+ model.joinClass.reset()
66
+ closeModal()
67
+ }
68
+
69
+ const submit = () => model.joinClass.submit()
70
+
71
+ const keyboardHeight = useKeyboardOffset()
72
+ const liftStyle = useMemo(
73
+ () => ({ transform: [{ translateY: Animated.multiply(keyboardHeight, -1 / KEYBOARD_LIFT_RATIO) }] }),
74
+ [keyboardHeight],
75
+ )
76
+
77
+ return (
78
+ <Animated.View style={liftStyle}>
79
+ <BaseModal
80
+ onClose={closeAndReset}
81
+ closeButton={
82
+ <Button
83
+ variant={ButtonVariant.TERTIARY}
84
+ icon={<CrossIcon size={CLOSE_ICON_SIZE} color={COLORS.NEUTRAL_7} />}
85
+ onPress={closeAndReset}
86
+ testID={TEST_IDS.JOIN_CLASS_CLOSE_BUTTON}
87
+ />
88
+ }
89
+ style={{ container: [styles.modal, { width: modalWidth }] }}
90
+ header={t('profile.joinClassTitle')}
91
+ content={
92
+ <CodeField
93
+ {...cellProps}
94
+ nativeID="join-class-code"
95
+ value={code}
96
+ onChangeText={setCode}
97
+ cellCount={CLASS_CODE_LENGTH}
98
+ rootStyle={styles.codeRoot}
99
+ textInputStyle={styles.codeInput}
100
+ autoFocus
101
+ autoComplete="off"
102
+ textContentType="oneTimeCode"
103
+ keyboardType="default"
104
+ returnKeyType="go"
105
+ onSubmitEditing={submit}
106
+ testID={TEST_IDS.JOIN_CLASS_INPUT(0)}
107
+ renderCell={({ index, symbol, isFocused }) => {
108
+ const isPlaceholder = !symbol && !isFocused
109
+ return (
110
+ <View
111
+ key={index}
112
+ style={[styles.cell, { borderRadius: cellRadius, width: cellSize }]}
113
+ onLayout={getCellOnLayoutHandler(index)}
114
+ >
115
+ <Text
116
+ style={[styles.cellText, { fontSize: cellFontSize }, isPlaceholder && styles.cellPlaceholder]}
117
+ >
118
+ {symbol || (isFocused ? <Cursor /> : CODE_PLACEHOLDER)}
119
+ </Text>
120
+ </View>
121
+ )
122
+ }}
123
+ />
124
+ }
125
+ footer={
126
+ <ReanimatedButton
127
+ variant={ButtonVariant.PRIMARY}
128
+ colorScheme={ButtonColor.ORANGE}
129
+ size={ButtonSize.LARGE}
130
+ onPress={submit}
131
+ disabled={!isComplete || isPending}
132
+ isLoading={isPending}
133
+ testID={TEST_IDS.JOIN_CLASS_SUBMIT_BUTTON}
134
+ style={buttonStyle}
135
+ >
136
+ {t('profile.joinClassButton')}
137
+ </ReanimatedButton>
138
+ }
139
+ />
140
+ </Animated.View>
141
+ )
142
+ }
143
+
144
+ const styles = StyleSheet.create({
145
+ modal: {
146
+ gap: SPACING['800'],
147
+ paddingHorizontal: MODAL_H_PADDING,
148
+ },
149
+ codeRoot: {
150
+ flexDirection: 'row',
151
+ gap: CELL_GAP,
152
+ justifyContent: 'center',
153
+ },
154
+ codeInput: {
155
+ borderWidth: 0,
156
+ // kill the WebKit/iPad focus ring on the hidden input — focus is shown by the cells
157
+ ...(isWeb && { outlineStyle: 'none' }),
158
+ },
159
+ cell: {
160
+ alignItems: 'center',
161
+ aspectRatio: CELL_ASPECT,
162
+ backgroundColor: COLORS.NEUTRAL_1,
163
+ borderColor: COLORS.NEUTRAL_5,
164
+ borderWidth: 3,
165
+ justifyContent: 'center',
166
+ },
167
+ cellText: {
168
+ color: COLORS.NEUTRAL_9,
169
+ fontFamily: FONT_FAMILY.buenosAiresSemiBold,
170
+ textAlign: 'center',
171
+ },
172
+ cellPlaceholder: {
173
+ color: COLORS.NEUTRAL_5,
174
+ },
175
+ text: {
176
+ textTransform: 'uppercase',
177
+ },
178
+ })
179
+
180
+ const buttonStyle = { text: styles.text }