@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.
- package/dist/commonjs/features/chatbot/components/ChatInput/FreeText/ChatTextInput.js.map +1 -1
- package/dist/commonjs/features/profile/Profile.js +90 -0
- package/dist/commonjs/features/profile/Profile.js.map +1 -0
- package/dist/commonjs/features/profile/components/actions/ProfileActions.js +70 -0
- package/dist/commonjs/features/profile/components/actions/ProfileActions.js.map +1 -0
- package/dist/commonjs/features/profile/components/cards/ProfileCard.js +57 -0
- package/dist/commonjs/features/profile/components/cards/ProfileCard.js.map +1 -0
- package/dist/commonjs/features/profile/components/info/AccountInfoCard.js +91 -0
- package/dist/commonjs/features/profile/components/info/AccountInfoCard.js.map +1 -0
- package/dist/commonjs/features/profile/components/modals/JoinClassModal.js +190 -0
- package/dist/commonjs/features/profile/components/modals/JoinClassModal.js.map +1 -0
- package/dist/commonjs/features/profile/components/modals/LogoutConfirmModal.js +61 -0
- package/dist/commonjs/features/profile/components/modals/LogoutConfirmModal.js.map +1 -0
- package/dist/commonjs/features/profile/components/modals/TranslationLanguageModal.js +128 -0
- package/dist/commonjs/features/profile/components/modals/TranslationLanguageModal.js.map +1 -0
- package/dist/commonjs/features/profile/components/settings/LanguageSettingsCard.js +22 -0
- package/dist/commonjs/features/profile/components/settings/LanguageSettingsCard.js.map +1 -0
- package/dist/commonjs/features/profile/components/settings/PreferencesCard.js +69 -0
- package/dist/commonjs/features/profile/components/settings/PreferencesCard.js.map +1 -0
- package/dist/commonjs/features/profile/components/settings/SettingRow.js +48 -0
- package/dist/commonjs/features/profile/components/settings/SettingRow.js.map +1 -0
- package/dist/commonjs/features/profile/components/settings/TextSettingsCard.js +77 -0
- package/dist/commonjs/features/profile/components/settings/TextSettingsCard.js.map +1 -0
- package/dist/commonjs/features/profile/components/settings/TextSizeSlider.js +119 -0
- package/dist/commonjs/features/profile/components/settings/TextSizeSlider.js.map +1 -0
- package/dist/commonjs/features/profile/components/settings/TextToSpeechRow.js +37 -0
- package/dist/commonjs/features/profile/components/settings/TextToSpeechRow.js.map +1 -0
- package/dist/commonjs/features/profile/components/settings/TranslationLanguageRow.js +62 -0
- package/dist/commonjs/features/profile/components/settings/TranslationLanguageRow.js.map +1 -0
- package/dist/commonjs/features/profile/context/ProfileContext.js +18 -0
- package/dist/commonjs/features/profile/context/ProfileContext.js.map +1 -0
- package/dist/commonjs/features/profile/index.js +80 -0
- package/dist/commonjs/features/profile/index.js.map +1 -0
- package/dist/commonjs/features/profile/model/ProfileApi.js +15 -0
- package/dist/commonjs/features/profile/model/ProfileApi.js.map +1 -0
- package/dist/commonjs/features/profile/model/ProfileJoinClassModel.js +60 -0
- package/dist/commonjs/features/profile/model/ProfileJoinClassModel.js.map +1 -0
- package/dist/commonjs/features/profile/model/ProfileLanguageModel.js +30 -0
- package/dist/commonjs/features/profile/model/ProfileLanguageModel.js.map +1 -0
- package/dist/commonjs/features/profile/model/ProfileModel.js +53 -0
- package/dist/commonjs/features/profile/model/ProfileModel.js.map +1 -0
- package/dist/commonjs/features/profile/model/profileModal.types.js +6 -0
- package/dist/commonjs/features/profile/model/profileModal.types.js.map +1 -0
- package/dist/commonjs/features/profile/shared/JoinClassError.js +16 -0
- package/dist/commonjs/features/profile/shared/JoinClassError.js.map +1 -0
- package/dist/commonjs/features/profile/shared/profile.constants.js +29 -0
- package/dist/commonjs/features/profile/shared/profile.constants.js.map +1 -0
- package/dist/commonjs/features/profile/shared/profile.helpers.js +40 -0
- package/dist/commonjs/features/profile/shared/profile.helpers.js.map +1 -0
- package/dist/commonjs/features/profile/shared/profile.types.js +6 -0
- package/dist/commonjs/features/profile/shared/profile.types.js.map +1 -0
- package/dist/commonjs/index.js +12 -0
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/shared/hooks/useKeyboardOffset.js +54 -0
- package/dist/commonjs/shared/hooks/useKeyboardOffset.js.map +1 -0
- package/dist/module/features/chatbot/components/ChatInput/FreeText/ChatTextInput.js.map +1 -1
- package/dist/module/features/profile/Profile.js +84 -0
- package/dist/module/features/profile/Profile.js.map +1 -0
- package/dist/module/features/profile/components/actions/ProfileActions.js +64 -0
- package/dist/module/features/profile/components/actions/ProfileActions.js.map +1 -0
- package/dist/module/features/profile/components/cards/ProfileCard.js +51 -0
- package/dist/module/features/profile/components/cards/ProfileCard.js.map +1 -0
- package/dist/module/features/profile/components/info/AccountInfoCard.js +85 -0
- package/dist/module/features/profile/components/info/AccountInfoCard.js.map +1 -0
- package/dist/module/features/profile/components/modals/JoinClassModal.js +183 -0
- package/dist/module/features/profile/components/modals/JoinClassModal.js.map +1 -0
- package/dist/module/features/profile/components/modals/LogoutConfirmModal.js +55 -0
- package/dist/module/features/profile/components/modals/LogoutConfirmModal.js.map +1 -0
- package/dist/module/features/profile/components/modals/TranslationLanguageModal.js +122 -0
- package/dist/module/features/profile/components/modals/TranslationLanguageModal.js.map +1 -0
- package/dist/module/features/profile/components/settings/LanguageSettingsCard.js +16 -0
- package/dist/module/features/profile/components/settings/LanguageSettingsCard.js.map +1 -0
- package/dist/module/features/profile/components/settings/PreferencesCard.js +63 -0
- package/dist/module/features/profile/components/settings/PreferencesCard.js.map +1 -0
- package/dist/module/features/profile/components/settings/SettingRow.js +42 -0
- package/dist/module/features/profile/components/settings/SettingRow.js.map +1 -0
- package/dist/module/features/profile/components/settings/TextSettingsCard.js +71 -0
- package/dist/module/features/profile/components/settings/TextSettingsCard.js.map +1 -0
- package/dist/module/features/profile/components/settings/TextSizeSlider.js +112 -0
- package/dist/module/features/profile/components/settings/TextSizeSlider.js.map +1 -0
- package/dist/module/features/profile/components/settings/TextToSpeechRow.js +31 -0
- package/dist/module/features/profile/components/settings/TextToSpeechRow.js.map +1 -0
- package/dist/module/features/profile/components/settings/TranslationLanguageRow.js +56 -0
- package/dist/module/features/profile/components/settings/TranslationLanguageRow.js.map +1 -0
- package/dist/module/features/profile/context/ProfileContext.js +13 -0
- package/dist/module/features/profile/context/ProfileContext.js.map +1 -0
- package/dist/module/features/profile/index.js +11 -0
- package/dist/module/features/profile/index.js.map +1 -0
- package/dist/module/features/profile/model/ProfileApi.js +10 -0
- package/dist/module/features/profile/model/ProfileApi.js.map +1 -0
- package/dist/module/features/profile/model/ProfileJoinClassModel.js +55 -0
- package/dist/module/features/profile/model/ProfileJoinClassModel.js.map +1 -0
- package/dist/module/features/profile/model/ProfileLanguageModel.js +25 -0
- package/dist/module/features/profile/model/ProfileLanguageModel.js.map +1 -0
- package/dist/module/features/profile/model/ProfileModel.js +48 -0
- package/dist/module/features/profile/model/ProfileModel.js.map +1 -0
- package/dist/module/features/profile/model/profileModal.types.js +4 -0
- package/dist/module/features/profile/model/profileModal.types.js.map +1 -0
- package/dist/module/features/profile/shared/JoinClassError.js +11 -0
- package/dist/module/features/profile/shared/JoinClassError.js.map +1 -0
- package/dist/module/features/profile/shared/profile.constants.js +25 -0
- package/dist/module/features/profile/shared/profile.constants.js.map +1 -0
- package/dist/module/features/profile/shared/profile.helpers.js +32 -0
- package/dist/module/features/profile/shared/profile.helpers.js.map +1 -0
- package/dist/module/features/profile/shared/profile.types.js +4 -0
- package/dist/module/features/profile/shared/profile.types.js.map +1 -0
- package/dist/module/index.js +1 -0
- package/dist/module/index.js.map +1 -1
- package/dist/module/shared/hooks/useKeyboardOffset.js +49 -0
- package/dist/module/shared/hooks/useKeyboardOffset.js.map +1 -0
- package/dist/typescript/commonjs/features/chatbot/components/ChatInput/FreeText/ChatTextInput.d.ts.map +1 -1
- package/dist/typescript/commonjs/features/profile/Profile.d.ts +6 -0
- package/dist/typescript/commonjs/features/profile/Profile.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/__tests__/ProfileModel.test.d.ts +2 -0
- package/dist/typescript/commonjs/features/profile/__tests__/ProfileModel.test.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/__tests__/profile.helpers.test.d.ts +2 -0
- package/dist/typescript/commonjs/features/profile/__tests__/profile.helpers.test.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/components/actions/ProfileActions.d.ts +3 -0
- package/dist/typescript/commonjs/features/profile/components/actions/ProfileActions.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/components/cards/ProfileCard.d.ts +10 -0
- package/dist/typescript/commonjs/features/profile/components/cards/ProfileCard.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/components/info/AccountInfoCard.d.ts +3 -0
- package/dist/typescript/commonjs/features/profile/components/info/AccountInfoCard.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/components/modals/JoinClassModal.d.ts +4 -0
- package/dist/typescript/commonjs/features/profile/components/modals/JoinClassModal.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/components/modals/LogoutConfirmModal.d.ts +4 -0
- package/dist/typescript/commonjs/features/profile/components/modals/LogoutConfirmModal.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/components/modals/TranslationLanguageModal.d.ts +4 -0
- package/dist/typescript/commonjs/features/profile/components/modals/TranslationLanguageModal.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/components/settings/LanguageSettingsCard.d.ts +3 -0
- package/dist/typescript/commonjs/features/profile/components/settings/LanguageSettingsCard.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/components/settings/PreferencesCard.d.ts +3 -0
- package/dist/typescript/commonjs/features/profile/components/settings/PreferencesCard.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/components/settings/SettingRow.d.ts +11 -0
- package/dist/typescript/commonjs/features/profile/components/settings/SettingRow.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/components/settings/TextSettingsCard.d.ts +3 -0
- package/dist/typescript/commonjs/features/profile/components/settings/TextSettingsCard.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/components/settings/TextSizeSlider.d.ts +8 -0
- package/dist/typescript/commonjs/features/profile/components/settings/TextSizeSlider.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/components/settings/TextToSpeechRow.d.ts +3 -0
- package/dist/typescript/commonjs/features/profile/components/settings/TextToSpeechRow.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/components/settings/TranslationLanguageRow.d.ts +3 -0
- package/dist/typescript/commonjs/features/profile/components/settings/TranslationLanguageRow.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/context/ProfileContext.d.ts +13 -0
- package/dist/typescript/commonjs/features/profile/context/ProfileContext.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/index.d.ts +12 -0
- package/dist/typescript/commonjs/features/profile/index.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/model/ProfileApi.d.ts +11 -0
- package/dist/typescript/commonjs/features/profile/model/ProfileApi.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/model/ProfileJoinClassModel.d.ts +14 -0
- package/dist/typescript/commonjs/features/profile/model/ProfileJoinClassModel.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/model/ProfileLanguageModel.d.ts +13 -0
- package/dist/typescript/commonjs/features/profile/model/ProfileLanguageModel.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/model/ProfileModel.d.ts +26 -0
- package/dist/typescript/commonjs/features/profile/model/ProfileModel.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/model/profileModal.types.d.ts +14 -0
- package/dist/typescript/commonjs/features/profile/model/profileModal.types.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/shared/JoinClassError.d.ts +5 -0
- package/dist/typescript/commonjs/features/profile/shared/JoinClassError.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/shared/profile.constants.d.ts +20 -0
- package/dist/typescript/commonjs/features/profile/shared/profile.constants.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/shared/profile.helpers.d.ts +6 -0
- package/dist/typescript/commonjs/features/profile/shared/profile.helpers.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/profile/shared/profile.types.d.ts +27 -0
- package/dist/typescript/commonjs/features/profile/shared/profile.types.d.ts.map +1 -0
- package/dist/typescript/commonjs/index.d.ts +1 -0
- package/dist/typescript/commonjs/index.d.ts.map +1 -1
- package/dist/typescript/commonjs/shared/hooks/useKeyboardOffset.d.ts +11 -0
- package/dist/typescript/commonjs/shared/hooks/useKeyboardOffset.d.ts.map +1 -0
- package/dist/typescript/module/features/chatbot/components/ChatInput/FreeText/ChatTextInput.d.ts.map +1 -1
- package/dist/typescript/module/features/profile/Profile.d.ts +6 -0
- package/dist/typescript/module/features/profile/Profile.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/__tests__/ProfileModel.test.d.ts +2 -0
- package/dist/typescript/module/features/profile/__tests__/ProfileModel.test.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/__tests__/profile.helpers.test.d.ts +2 -0
- package/dist/typescript/module/features/profile/__tests__/profile.helpers.test.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/components/actions/ProfileActions.d.ts +3 -0
- package/dist/typescript/module/features/profile/components/actions/ProfileActions.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/components/cards/ProfileCard.d.ts +10 -0
- package/dist/typescript/module/features/profile/components/cards/ProfileCard.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/components/info/AccountInfoCard.d.ts +3 -0
- package/dist/typescript/module/features/profile/components/info/AccountInfoCard.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/components/modals/JoinClassModal.d.ts +4 -0
- package/dist/typescript/module/features/profile/components/modals/JoinClassModal.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/components/modals/LogoutConfirmModal.d.ts +4 -0
- package/dist/typescript/module/features/profile/components/modals/LogoutConfirmModal.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/components/modals/TranslationLanguageModal.d.ts +4 -0
- package/dist/typescript/module/features/profile/components/modals/TranslationLanguageModal.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/components/settings/LanguageSettingsCard.d.ts +3 -0
- package/dist/typescript/module/features/profile/components/settings/LanguageSettingsCard.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/components/settings/PreferencesCard.d.ts +3 -0
- package/dist/typescript/module/features/profile/components/settings/PreferencesCard.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/components/settings/SettingRow.d.ts +11 -0
- package/dist/typescript/module/features/profile/components/settings/SettingRow.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/components/settings/TextSettingsCard.d.ts +3 -0
- package/dist/typescript/module/features/profile/components/settings/TextSettingsCard.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/components/settings/TextSizeSlider.d.ts +8 -0
- package/dist/typescript/module/features/profile/components/settings/TextSizeSlider.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/components/settings/TextToSpeechRow.d.ts +3 -0
- package/dist/typescript/module/features/profile/components/settings/TextToSpeechRow.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/components/settings/TranslationLanguageRow.d.ts +3 -0
- package/dist/typescript/module/features/profile/components/settings/TranslationLanguageRow.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/context/ProfileContext.d.ts +13 -0
- package/dist/typescript/module/features/profile/context/ProfileContext.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/index.d.ts +12 -0
- package/dist/typescript/module/features/profile/index.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/model/ProfileApi.d.ts +11 -0
- package/dist/typescript/module/features/profile/model/ProfileApi.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/model/ProfileJoinClassModel.d.ts +14 -0
- package/dist/typescript/module/features/profile/model/ProfileJoinClassModel.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/model/ProfileLanguageModel.d.ts +13 -0
- package/dist/typescript/module/features/profile/model/ProfileLanguageModel.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/model/ProfileModel.d.ts +26 -0
- package/dist/typescript/module/features/profile/model/ProfileModel.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/model/profileModal.types.d.ts +14 -0
- package/dist/typescript/module/features/profile/model/profileModal.types.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/shared/JoinClassError.d.ts +5 -0
- package/dist/typescript/module/features/profile/shared/JoinClassError.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/shared/profile.constants.d.ts +20 -0
- package/dist/typescript/module/features/profile/shared/profile.constants.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/shared/profile.helpers.d.ts +6 -0
- package/dist/typescript/module/features/profile/shared/profile.helpers.d.ts.map +1 -0
- package/dist/typescript/module/features/profile/shared/profile.types.d.ts +27 -0
- package/dist/typescript/module/features/profile/shared/profile.types.d.ts.map +1 -0
- package/dist/typescript/module/index.d.ts +1 -0
- package/dist/typescript/module/index.d.ts.map +1 -1
- package/dist/typescript/module/shared/hooks/useKeyboardOffset.d.ts +11 -0
- package/dist/typescript/module/shared/hooks/useKeyboardOffset.d.ts.map +1 -0
- package/package.json +4 -2
- package/src/features/chatbot/components/ChatInput/FreeText/ChatTextInput.tsx +0 -1
- package/src/features/profile/Profile.tsx +83 -0
- package/src/features/profile/__tests__/ProfileModel.test.ts +99 -0
- package/src/features/profile/__tests__/profile.helpers.test.ts +57 -0
- package/src/features/profile/components/actions/ProfileActions.tsx +68 -0
- package/src/features/profile/components/cards/ProfileCard.tsx +53 -0
- package/src/features/profile/components/info/AccountInfoCard.tsx +74 -0
- package/src/features/profile/components/modals/JoinClassModal.tsx +180 -0
- package/src/features/profile/components/modals/LogoutConfirmModal.tsx +64 -0
- package/src/features/profile/components/modals/TranslationLanguageModal.tsx +126 -0
- package/src/features/profile/components/settings/LanguageSettingsCard.tsx +16 -0
- package/src/features/profile/components/settings/PreferencesCard.tsx +67 -0
- package/src/features/profile/components/settings/SettingRow.tsx +42 -0
- package/src/features/profile/components/settings/TextSettingsCard.tsx +64 -0
- package/src/features/profile/components/settings/TextSizeSlider.tsx +117 -0
- package/src/features/profile/components/settings/TextToSpeechRow.tsx +29 -0
- package/src/features/profile/components/settings/TranslationLanguageRow.tsx +60 -0
- package/src/features/profile/context/ProfileContext.tsx +24 -0
- package/src/features/profile/index.ts +11 -0
- package/src/features/profile/model/ProfileApi.ts +23 -0
- package/src/features/profile/model/ProfileJoinClassModel.ts +58 -0
- package/src/features/profile/model/ProfileLanguageModel.ts +26 -0
- package/src/features/profile/model/ProfileModel.ts +57 -0
- package/src/features/profile/model/profileModal.types.ts +16 -0
- package/src/features/profile/shared/JoinClassError.ts +7 -0
- package/src/features/profile/shared/profile.constants.ts +23 -0
- package/src/features/profile/shared/profile.helpers.ts +36 -0
- package/src/features/profile/shared/profile.types.ts +34 -0
- package/src/i18n/.generated/schema.json +9 -1
- package/src/index.ts +1 -0
- 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.
|
|
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.
|
|
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 }
|