@magmamath/students-features 0.9.107-rc.2 → 0.9.107-rc.21

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 (242) hide show
  1. package/dist/commonjs/features/chatbot/components/Chat/Chat.js +29 -18
  2. package/dist/commonjs/features/chatbot/components/Chat/Chat.js.map +1 -1
  3. package/dist/commonjs/features/chatbot/components/ChatControls/ChatControls.js +7 -2
  4. package/dist/commonjs/features/chatbot/components/ChatControls/ChatControls.js.map +1 -1
  5. package/dist/commonjs/features/chatbot/components/Chatbot.js +14 -3
  6. package/dist/commonjs/features/chatbot/components/Chatbot.js.map +1 -1
  7. package/dist/commonjs/features/chatbot/model/ChatBotModel.js +11 -2
  8. package/dist/commonjs/features/chatbot/model/ChatBotModel.js.map +1 -1
  9. package/dist/commonjs/features/chatbot/model/ChatsCacheModel.js.map +1 -1
  10. package/dist/commonjs/features/chatbot/types/model.types.js.map +1 -1
  11. package/dist/commonjs/features/voiceRecord/components/VoiceRecord.js +25 -17
  12. package/dist/commonjs/features/voiceRecord/components/VoiceRecord.js.map +1 -1
  13. package/dist/commonjs/features/voiceRecord/components/VoiceRecordButton.js +22 -8
  14. package/dist/commonjs/features/voiceRecord/components/VoiceRecordButton.js.map +1 -1
  15. package/dist/commonjs/features/voiceRecord/components/{VoiceRecordDelete.js → VoiceRecordDeleteButton.js} +9 -6
  16. package/dist/commonjs/features/voiceRecord/components/VoiceRecordDeleteButton.js.map +1 -0
  17. package/dist/commonjs/features/voiceRecord/components/VoiceRecordTimer.js +7 -2
  18. package/dist/commonjs/features/voiceRecord/components/VoiceRecordTimer.js.map +1 -1
  19. package/dist/commonjs/features/voiceRecord/constants.js +8 -1
  20. package/dist/commonjs/features/voiceRecord/constants.js.map +1 -1
  21. package/dist/commonjs/features/voiceRecord/helpers.js +89 -3
  22. package/dist/commonjs/features/voiceRecord/helpers.js.map +1 -1
  23. package/dist/commonjs/features/voiceRecord/hooks/useVoiceRecorder.js +45 -33
  24. package/dist/commonjs/features/voiceRecord/hooks/useVoiceRecorder.js.map +1 -1
  25. package/dist/commonjs/features/voiceRecord/hooks/useVoiceRecorderAnimation.js +3 -3
  26. package/dist/commonjs/features/voiceRecord/hooks/useVoiceRecorderAnimation.js.map +1 -1
  27. package/dist/commonjs/features/voiceRecord/index.js +44 -0
  28. package/dist/commonjs/features/voiceRecord/index.js.map +1 -1
  29. package/dist/commonjs/features/voiceRecord/modal/VoiceRecordDeleteModal.js +93 -0
  30. package/dist/commonjs/features/voiceRecord/modal/VoiceRecordDeleteModal.js.map +1 -0
  31. package/dist/commonjs/features/voiceRecord/modal/VoiceRecordUndoModal.js +75 -0
  32. package/dist/commonjs/features/voiceRecord/modal/VoiceRecordUndoModal.js.map +1 -0
  33. package/dist/commonjs/features/voiceRecord/model/Recorder.model.js +54 -0
  34. package/dist/commonjs/features/voiceRecord/model/Recorder.model.js.map +1 -0
  35. package/dist/commonjs/features/voiceRecord/model/VoiceRecord.model.js +162 -21
  36. package/dist/commonjs/features/voiceRecord/model/VoiceRecord.model.js.map +1 -1
  37. package/dist/commonjs/features/voiceRecord/model/VoiceRecordApi.js +21 -1
  38. package/dist/commonjs/features/voiceRecord/model/VoiceRecordApi.js.map +1 -1
  39. package/dist/commonjs/features/voiceRecord/model/VoiceRecordCollection.js +30 -0
  40. package/dist/commonjs/features/voiceRecord/model/VoiceRecordCollection.js.map +1 -0
  41. package/dist/commonjs/features/voiceRecord/types.js +6 -0
  42. package/dist/commonjs/features/voiceRecord/types.js.map +1 -0
  43. package/dist/commonjs/lib/components/ConditionalWrapper.js +23 -0
  44. package/dist/commonjs/lib/components/ConditionalWrapper.js.map +1 -0
  45. package/dist/commonjs/shared/translation/localization/ca.json +13 -0
  46. package/dist/commonjs/shared/translation/localization/de.json +13 -0
  47. package/dist/commonjs/shared/translation/localization/en.json +13 -0
  48. package/dist/commonjs/shared/translation/localization/gb.json +13 -0
  49. package/dist/commonjs/shared/translation/localization/sct.json +13 -0
  50. package/dist/commonjs/shared/translation/localization/sw.json +13 -0
  51. package/dist/module/features/chatbot/components/Chat/Chat.js +29 -18
  52. package/dist/module/features/chatbot/components/Chat/Chat.js.map +1 -1
  53. package/dist/module/features/chatbot/components/ChatControls/ChatControls.js +5 -2
  54. package/dist/module/features/chatbot/components/ChatControls/ChatControls.js.map +1 -1
  55. package/dist/module/features/chatbot/components/Chatbot.js +14 -3
  56. package/dist/module/features/chatbot/components/Chatbot.js.map +1 -1
  57. package/dist/module/features/chatbot/model/ChatBotModel.js +11 -2
  58. package/dist/module/features/chatbot/model/ChatBotModel.js.map +1 -1
  59. package/dist/module/features/chatbot/model/ChatsCacheModel.js.map +1 -1
  60. package/dist/module/features/chatbot/types/model.types.js.map +1 -1
  61. package/dist/module/features/voiceRecord/components/VoiceRecord.js +24 -16
  62. package/dist/module/features/voiceRecord/components/VoiceRecord.js.map +1 -1
  63. package/dist/module/features/voiceRecord/components/VoiceRecordButton.js +22 -8
  64. package/dist/module/features/voiceRecord/components/VoiceRecordButton.js.map +1 -1
  65. package/dist/module/features/voiceRecord/components/{VoiceRecordDelete.js → VoiceRecordDeleteButton.js} +8 -5
  66. package/dist/module/features/voiceRecord/components/VoiceRecordDeleteButton.js.map +1 -0
  67. package/dist/module/features/voiceRecord/components/VoiceRecordTimer.js +7 -2
  68. package/dist/module/features/voiceRecord/components/VoiceRecordTimer.js.map +1 -1
  69. package/dist/module/features/voiceRecord/constants.js +7 -0
  70. package/dist/module/features/voiceRecord/constants.js.map +1 -1
  71. package/dist/module/features/voiceRecord/helpers.js +84 -2
  72. package/dist/module/features/voiceRecord/helpers.js.map +1 -1
  73. package/dist/module/features/voiceRecord/hooks/useVoiceRecorder.js +47 -35
  74. package/dist/module/features/voiceRecord/hooks/useVoiceRecorder.js.map +1 -1
  75. package/dist/module/features/voiceRecord/hooks/useVoiceRecorderAnimation.js +3 -3
  76. package/dist/module/features/voiceRecord/hooks/useVoiceRecorderAnimation.js.map +1 -1
  77. package/dist/module/features/voiceRecord/index.js +4 -0
  78. package/dist/module/features/voiceRecord/index.js.map +1 -1
  79. package/dist/module/features/voiceRecord/modal/VoiceRecordDeleteModal.js +86 -0
  80. package/dist/module/features/voiceRecord/modal/VoiceRecordDeleteModal.js.map +1 -0
  81. package/dist/module/features/voiceRecord/modal/VoiceRecordUndoModal.js +69 -0
  82. package/dist/module/features/voiceRecord/modal/VoiceRecordUndoModal.js.map +1 -0
  83. package/dist/module/features/voiceRecord/model/Recorder.model.js +49 -0
  84. package/dist/module/features/voiceRecord/model/Recorder.model.js.map +1 -0
  85. package/dist/module/features/voiceRecord/model/VoiceRecord.model.js +163 -22
  86. package/dist/module/features/voiceRecord/model/VoiceRecord.model.js.map +1 -1
  87. package/dist/module/features/voiceRecord/model/VoiceRecordApi.js +21 -1
  88. package/dist/module/features/voiceRecord/model/VoiceRecordApi.js.map +1 -1
  89. package/dist/module/features/voiceRecord/model/VoiceRecordCollection.js +25 -0
  90. package/dist/module/features/voiceRecord/model/VoiceRecordCollection.js.map +1 -0
  91. package/dist/module/features/voiceRecord/types.js +4 -0
  92. package/dist/module/features/voiceRecord/types.js.map +1 -0
  93. package/dist/module/lib/components/ConditionalWrapper.js +17 -0
  94. package/dist/module/lib/components/ConditionalWrapper.js.map +1 -0
  95. package/dist/module/shared/translation/localization/ca.json +13 -0
  96. package/dist/module/shared/translation/localization/de.json +13 -0
  97. package/dist/module/shared/translation/localization/en.json +13 -0
  98. package/dist/module/shared/translation/localization/gb.json +13 -0
  99. package/dist/module/shared/translation/localization/sct.json +13 -0
  100. package/dist/module/shared/translation/localization/sw.json +13 -0
  101. package/dist/typescript/commonjs/features/chatbot/components/Chat/Chat.d.ts +4 -2
  102. package/dist/typescript/commonjs/features/chatbot/components/Chat/Chat.d.ts.map +1 -1
  103. package/dist/typescript/commonjs/features/chatbot/components/ChatControls/ChatControls.d.ts +2 -1
  104. package/dist/typescript/commonjs/features/chatbot/components/ChatControls/ChatControls.d.ts.map +1 -1
  105. package/dist/typescript/commonjs/features/chatbot/components/Chatbot.d.ts +2 -1
  106. package/dist/typescript/commonjs/features/chatbot/components/Chatbot.d.ts.map +1 -1
  107. package/dist/typescript/commonjs/features/chatbot/model/ChatBotModel.d.ts +1 -0
  108. package/dist/typescript/commonjs/features/chatbot/model/ChatBotModel.d.ts.map +1 -1
  109. package/dist/typescript/commonjs/features/chatbot/model/ChatsCacheModel.d.ts.map +1 -1
  110. package/dist/typescript/commonjs/features/chatbot/types/api.types.d.ts +1 -0
  111. package/dist/typescript/commonjs/features/chatbot/types/api.types.d.ts.map +1 -1
  112. package/dist/typescript/commonjs/features/chatbot/types/model.types.d.ts +1 -0
  113. package/dist/typescript/commonjs/features/chatbot/types/model.types.d.ts.map +1 -1
  114. package/dist/typescript/commonjs/features/voiceRecord/components/VoiceRecord.d.ts.map +1 -1
  115. package/dist/typescript/commonjs/features/voiceRecord/components/VoiceRecordButton.d.ts +1 -5
  116. package/dist/typescript/commonjs/features/voiceRecord/components/VoiceRecordButton.d.ts.map +1 -1
  117. package/dist/typescript/commonjs/features/voiceRecord/components/VoiceRecordDeleteButton.d.ts +8 -0
  118. package/dist/typescript/commonjs/features/voiceRecord/components/VoiceRecordDeleteButton.d.ts.map +1 -0
  119. package/dist/typescript/commonjs/features/voiceRecord/components/VoiceRecordTimer.d.ts +4 -2
  120. package/dist/typescript/commonjs/features/voiceRecord/components/VoiceRecordTimer.d.ts.map +1 -1
  121. package/dist/typescript/commonjs/features/voiceRecord/constants.d.ts +6 -0
  122. package/dist/typescript/commonjs/features/voiceRecord/constants.d.ts.map +1 -1
  123. package/dist/typescript/commonjs/features/voiceRecord/helpers.d.ts +26 -2
  124. package/dist/typescript/commonjs/features/voiceRecord/helpers.d.ts.map +1 -1
  125. package/dist/typescript/commonjs/features/voiceRecord/hooks/useVoiceRecorder.d.ts +0 -4
  126. package/dist/typescript/commonjs/features/voiceRecord/hooks/useVoiceRecorder.d.ts.map +1 -1
  127. package/dist/typescript/commonjs/features/voiceRecord/hooks/useVoiceRecorderAnimation.d.ts +1 -2
  128. package/dist/typescript/commonjs/features/voiceRecord/hooks/useVoiceRecorderAnimation.d.ts.map +1 -1
  129. package/dist/typescript/commonjs/features/voiceRecord/index.d.ts +4 -0
  130. package/dist/typescript/commonjs/features/voiceRecord/index.d.ts.map +1 -1
  131. package/dist/typescript/commonjs/features/voiceRecord/modal/VoiceRecordDeleteModal.d.ts +13 -0
  132. package/dist/typescript/commonjs/features/voiceRecord/modal/VoiceRecordDeleteModal.d.ts.map +1 -0
  133. package/dist/typescript/commonjs/features/voiceRecord/modal/VoiceRecordUndoModal.d.ts +13 -0
  134. package/dist/typescript/commonjs/features/voiceRecord/modal/VoiceRecordUndoModal.d.ts.map +1 -0
  135. package/dist/typescript/commonjs/features/voiceRecord/model/Recorder.model.d.ts +20 -0
  136. package/dist/typescript/commonjs/features/voiceRecord/model/Recorder.model.d.ts.map +1 -0
  137. package/dist/typescript/commonjs/features/voiceRecord/model/VoiceRecord.model.d.ts +38 -20
  138. package/dist/typescript/commonjs/features/voiceRecord/model/VoiceRecord.model.d.ts.map +1 -1
  139. package/dist/typescript/commonjs/features/voiceRecord/model/VoiceRecordApi.d.ts +2 -0
  140. package/dist/typescript/commonjs/features/voiceRecord/model/VoiceRecordApi.d.ts.map +1 -1
  141. package/dist/typescript/commonjs/features/voiceRecord/model/VoiceRecordCollection.d.ts +10 -0
  142. package/dist/typescript/commonjs/features/voiceRecord/model/VoiceRecordCollection.d.ts.map +1 -0
  143. package/dist/typescript/commonjs/features/voiceRecord/types.d.ts +43 -0
  144. package/dist/typescript/commonjs/features/voiceRecord/types.d.ts.map +1 -0
  145. package/dist/typescript/commonjs/lib/components/ConditionalWrapper.d.ts +9 -0
  146. package/dist/typescript/commonjs/lib/components/ConditionalWrapper.d.ts.map +1 -0
  147. package/dist/typescript/commonjs/shared/translation/constants.d.ts +78 -0
  148. package/dist/typescript/commonjs/shared/translation/constants.d.ts.map +1 -1
  149. package/dist/typescript/commonjs/shared/translation/model.d.ts +26 -0
  150. package/dist/typescript/commonjs/shared/translation/model.d.ts.map +1 -1
  151. package/dist/typescript/commonjs/types/common.types.d.ts +3 -0
  152. package/dist/typescript/commonjs/types/common.types.d.ts.map +1 -1
  153. package/dist/typescript/module/features/chatbot/components/Chat/Chat.d.ts +4 -2
  154. package/dist/typescript/module/features/chatbot/components/Chat/Chat.d.ts.map +1 -1
  155. package/dist/typescript/module/features/chatbot/components/ChatControls/ChatControls.d.ts +2 -1
  156. package/dist/typescript/module/features/chatbot/components/ChatControls/ChatControls.d.ts.map +1 -1
  157. package/dist/typescript/module/features/chatbot/components/Chatbot.d.ts +2 -1
  158. package/dist/typescript/module/features/chatbot/components/Chatbot.d.ts.map +1 -1
  159. package/dist/typescript/module/features/chatbot/model/ChatBotModel.d.ts +1 -0
  160. package/dist/typescript/module/features/chatbot/model/ChatBotModel.d.ts.map +1 -1
  161. package/dist/typescript/module/features/chatbot/model/ChatsCacheModel.d.ts.map +1 -1
  162. package/dist/typescript/module/features/chatbot/types/api.types.d.ts +1 -0
  163. package/dist/typescript/module/features/chatbot/types/api.types.d.ts.map +1 -1
  164. package/dist/typescript/module/features/chatbot/types/model.types.d.ts +1 -0
  165. package/dist/typescript/module/features/chatbot/types/model.types.d.ts.map +1 -1
  166. package/dist/typescript/module/features/voiceRecord/components/VoiceRecord.d.ts.map +1 -1
  167. package/dist/typescript/module/features/voiceRecord/components/VoiceRecordButton.d.ts +1 -5
  168. package/dist/typescript/module/features/voiceRecord/components/VoiceRecordButton.d.ts.map +1 -1
  169. package/dist/typescript/module/features/voiceRecord/components/VoiceRecordDeleteButton.d.ts +8 -0
  170. package/dist/typescript/module/features/voiceRecord/components/VoiceRecordDeleteButton.d.ts.map +1 -0
  171. package/dist/typescript/module/features/voiceRecord/components/VoiceRecordTimer.d.ts +4 -2
  172. package/dist/typescript/module/features/voiceRecord/components/VoiceRecordTimer.d.ts.map +1 -1
  173. package/dist/typescript/module/features/voiceRecord/constants.d.ts +6 -0
  174. package/dist/typescript/module/features/voiceRecord/constants.d.ts.map +1 -1
  175. package/dist/typescript/module/features/voiceRecord/helpers.d.ts +26 -2
  176. package/dist/typescript/module/features/voiceRecord/helpers.d.ts.map +1 -1
  177. package/dist/typescript/module/features/voiceRecord/hooks/useVoiceRecorder.d.ts +0 -4
  178. package/dist/typescript/module/features/voiceRecord/hooks/useVoiceRecorder.d.ts.map +1 -1
  179. package/dist/typescript/module/features/voiceRecord/hooks/useVoiceRecorderAnimation.d.ts +1 -2
  180. package/dist/typescript/module/features/voiceRecord/hooks/useVoiceRecorderAnimation.d.ts.map +1 -1
  181. package/dist/typescript/module/features/voiceRecord/index.d.ts +4 -0
  182. package/dist/typescript/module/features/voiceRecord/index.d.ts.map +1 -1
  183. package/dist/typescript/module/features/voiceRecord/modal/VoiceRecordDeleteModal.d.ts +13 -0
  184. package/dist/typescript/module/features/voiceRecord/modal/VoiceRecordDeleteModal.d.ts.map +1 -0
  185. package/dist/typescript/module/features/voiceRecord/modal/VoiceRecordUndoModal.d.ts +13 -0
  186. package/dist/typescript/module/features/voiceRecord/modal/VoiceRecordUndoModal.d.ts.map +1 -0
  187. package/dist/typescript/module/features/voiceRecord/model/Recorder.model.d.ts +20 -0
  188. package/dist/typescript/module/features/voiceRecord/model/Recorder.model.d.ts.map +1 -0
  189. package/dist/typescript/module/features/voiceRecord/model/VoiceRecord.model.d.ts +38 -20
  190. package/dist/typescript/module/features/voiceRecord/model/VoiceRecord.model.d.ts.map +1 -1
  191. package/dist/typescript/module/features/voiceRecord/model/VoiceRecordApi.d.ts +2 -0
  192. package/dist/typescript/module/features/voiceRecord/model/VoiceRecordApi.d.ts.map +1 -1
  193. package/dist/typescript/module/features/voiceRecord/model/VoiceRecordCollection.d.ts +10 -0
  194. package/dist/typescript/module/features/voiceRecord/model/VoiceRecordCollection.d.ts.map +1 -0
  195. package/dist/typescript/module/features/voiceRecord/types.d.ts +43 -0
  196. package/dist/typescript/module/features/voiceRecord/types.d.ts.map +1 -0
  197. package/dist/typescript/module/lib/components/ConditionalWrapper.d.ts +9 -0
  198. package/dist/typescript/module/lib/components/ConditionalWrapper.d.ts.map +1 -0
  199. package/dist/typescript/module/shared/translation/constants.d.ts +78 -0
  200. package/dist/typescript/module/shared/translation/constants.d.ts.map +1 -1
  201. package/dist/typescript/module/shared/translation/model.d.ts +26 -0
  202. package/dist/typescript/module/shared/translation/model.d.ts.map +1 -1
  203. package/dist/typescript/module/types/common.types.d.ts +3 -0
  204. package/dist/typescript/module/types/common.types.d.ts.map +1 -1
  205. package/package.json +3 -1
  206. package/src/features/chatbot/components/Chat/Chat.tsx +30 -19
  207. package/src/features/chatbot/components/ChatControls/ChatControls.tsx +9 -2
  208. package/src/features/chatbot/components/Chatbot.tsx +15 -2
  209. package/src/features/chatbot/model/ChatBotModel.ts +12 -3
  210. package/src/features/chatbot/model/ChatsCacheModel.ts +4 -0
  211. package/src/features/chatbot/types/api.types.ts +1 -0
  212. package/src/features/chatbot/types/model.types.ts +1 -0
  213. package/src/features/voiceRecord/components/VoiceRecord.tsx +29 -18
  214. package/src/features/voiceRecord/components/VoiceRecordButton.tsx +33 -17
  215. package/src/features/voiceRecord/components/{VoiceRecordDelete.tsx → VoiceRecordDeleteButton.tsx} +5 -4
  216. package/src/features/voiceRecord/components/VoiceRecordTimer.tsx +19 -3
  217. package/src/features/voiceRecord/constants.ts +6 -0
  218. package/src/features/voiceRecord/helpers.ts +115 -1
  219. package/src/features/voiceRecord/hooks/useVoiceRecorder.ts +50 -36
  220. package/src/features/voiceRecord/hooks/useVoiceRecorderAnimation.ts +3 -4
  221. package/src/features/voiceRecord/index.ts +4 -0
  222. package/src/features/voiceRecord/modal/VoiceRecordDeleteModal.tsx +105 -0
  223. package/src/features/voiceRecord/modal/VoiceRecordUndoModal.tsx +82 -0
  224. package/src/features/voiceRecord/model/Recorder.model.ts +62 -0
  225. package/src/features/voiceRecord/model/VoiceRecord.model.ts +193 -32
  226. package/src/features/voiceRecord/model/VoiceRecordApi.ts +25 -1
  227. package/src/features/voiceRecord/model/VoiceRecordCollection.ts +26 -0
  228. package/src/features/voiceRecord/types.ts +49 -0
  229. package/src/lib/components/ConditionalWrapper.tsx +15 -0
  230. package/src/shared/translation/localization/ca.json +13 -0
  231. package/src/shared/translation/localization/de.json +13 -0
  232. package/src/shared/translation/localization/en.json +13 -0
  233. package/src/shared/translation/localization/gb.json +13 -0
  234. package/src/shared/translation/localization/sct.json +13 -0
  235. package/src/shared/translation/localization/sw.json +13 -0
  236. package/src/types/common.types.ts +1 -0
  237. package/dist/commonjs/features/voiceRecord/components/VoiceRecordDelete.js.map +0 -1
  238. package/dist/module/features/voiceRecord/components/VoiceRecordDelete.js.map +0 -1
  239. package/dist/typescript/commonjs/features/voiceRecord/components/VoiceRecordDelete.d.ts +0 -7
  240. package/dist/typescript/commonjs/features/voiceRecord/components/VoiceRecordDelete.d.ts.map +0 -1
  241. package/dist/typescript/module/features/voiceRecord/components/VoiceRecordDelete.d.ts +0 -7
  242. package/dist/typescript/module/features/voiceRecord/components/VoiceRecordDelete.d.ts.map +0 -1
@@ -0,0 +1,105 @@
1
+ import React, { useEffect } from 'react'
2
+ import {
3
+ BaseModal,
4
+ Button,
5
+ ButtonColor,
6
+ ButtonSize,
7
+ ButtonVariant,
8
+ SPACING,
9
+ } from '@magmamath/react-native-ui'
10
+ import { View, StyleSheet } from 'react-native'
11
+ import { useUnit } from 'effector-react'
12
+ import { VoiceRecordModel } from '../model/VoiceRecord.model'
13
+ import { VoiceRecorderState } from '../constants'
14
+ import { useText } from '../../../shared/translation'
15
+
16
+ type VoiceRecordDeleteModalComponentProps = {
17
+ modal: {
18
+ closeModal: () => void
19
+ params: {
20
+ model: VoiceRecordModel
21
+ }
22
+ }
23
+ }
24
+
25
+ type FooterButtonsProps = {
26
+ onClose: () => void
27
+ onDelete: () => void
28
+ }
29
+
30
+ const FooterButtons = ({ onClose, onDelete }: FooterButtonsProps) => {
31
+ const t = useText()
32
+
33
+ return (
34
+ <View style={styles.footerContainer}>
35
+ <Button variant={ButtonVariant.TERTIARY} size={ButtonSize.LARGE} onPress={onClose}>
36
+ {t('voiceRecorder.cancel')}
37
+ </Button>
38
+ <Button
39
+ colorScheme={ButtonColor.RED}
40
+ variant={ButtonVariant.PRIMARY}
41
+ size={ButtonSize.LARGE}
42
+ onPress={onDelete}
43
+ >
44
+ {t('voiceRecorder.delete')}
45
+ </Button>
46
+ </View>
47
+ )
48
+ }
49
+
50
+ export const VoiceRecordDeleteModal = ({
51
+ modal: { closeModal, params },
52
+ }: VoiceRecordDeleteModalComponentProps) => {
53
+ const t = useText()
54
+ const { model } = params
55
+
56
+ const recorderState = useUnit(model.recorderModel.$voiceRecordState)
57
+
58
+ const handleDelete = async () => {
59
+ if (recorderState === VoiceRecorderState.RECORDING) {
60
+ await model.stopAndDiscard()
61
+ }
62
+ await model.deleteCurrentRecording()
63
+
64
+ closeModal()
65
+ }
66
+
67
+ useEffect(() => {
68
+ if (recorderState === VoiceRecorderState.RECORDING) {
69
+ model.recorderModel.recorder.pause()
70
+ }
71
+
72
+ return () => {
73
+ if (model.recorderModel.$voiceRecordState.getState() === VoiceRecorderState.PAUSED) {
74
+ try {
75
+ model.recorderModel.recorder.record()
76
+ } catch (error) {
77
+ console.error(`Failed to resume recording: ${error}`)
78
+ }
79
+ }
80
+ }
81
+ }, [])
82
+
83
+ return (
84
+ <BaseModal
85
+ style={{
86
+ container: styles.modalContainer,
87
+ }}
88
+ header={t('voiceRecorder.deleteRecording')}
89
+ content={t('voiceRecorder.youWontBeAbleToRecover')}
90
+ footer={<FooterButtons onClose={closeModal} onDelete={handleDelete} />}
91
+ />
92
+ )
93
+ }
94
+
95
+ const styles = StyleSheet.create({
96
+ modalContainer: {
97
+ width: 600,
98
+ height: 268,
99
+ },
100
+ footerContainer: {
101
+ flexDirection: 'row',
102
+ alignItems: 'center',
103
+ gap: SPACING[200],
104
+ },
105
+ })
@@ -0,0 +1,82 @@
1
+ import React from 'react'
2
+ import {
3
+ BaseModal,
4
+ Button,
5
+ ButtonColor,
6
+ ButtonSize,
7
+ ButtonVariant,
8
+ SPACING,
9
+ } from '@magmamath/react-native-ui'
10
+ import { StyleSheet, View } from 'react-native'
11
+ import { useText } from '../../../shared/translation'
12
+ import { VoiceRecordModel } from '../model/VoiceRecord.model'
13
+
14
+ type VoiceRecordUndoModalProps = {
15
+ modal: {
16
+ closeModal: () => void
17
+ params: {
18
+ model: VoiceRecordModel
19
+ }
20
+ }
21
+ }
22
+
23
+ type FooterButtonsProps = {
24
+ onClose: () => void
25
+ onRedo: () => void
26
+ }
27
+
28
+ const FooterButtons = ({ onClose, onRedo }: FooterButtonsProps) => {
29
+ const t = useText()
30
+
31
+ return (
32
+ <View style={styles.footerContainer}>
33
+ <Button variant={ButtonVariant.TERTIARY} size={ButtonSize.LARGE} onPress={onClose}>
34
+ {t('voiceRecorder.cancel')}
35
+ </Button>
36
+ <Button
37
+ colorScheme={ButtonColor.BLUE}
38
+ variant={ButtonVariant.PRIMARY}
39
+ size={ButtonSize.LARGE}
40
+ onPress={onRedo}
41
+ >
42
+ {t('voiceRecorder.redo')}
43
+ </Button>
44
+ </View>
45
+ )
46
+ }
47
+
48
+ export const VoiceRecordUndoModal = ({
49
+ modal: { closeModal, params },
50
+ }: VoiceRecordUndoModalProps) => {
51
+ const t = useText()
52
+ const { model } = params
53
+
54
+ const handleRedo = () => {
55
+ model.deleteCurrentRecording({ notification: false })
56
+ closeModal()
57
+ model.initializeRecording()
58
+ }
59
+
60
+ return (
61
+ <BaseModal
62
+ style={{
63
+ container: styles.modalStyle,
64
+ }}
65
+ header={t('voiceRecorder.redoRecording')}
66
+ content={t('voiceRecorder.thisWillRemoveRecording')}
67
+ footer={<FooterButtons onClose={closeModal} onRedo={handleRedo} />}
68
+ />
69
+ )
70
+ }
71
+
72
+ const styles = StyleSheet.create({
73
+ modalStyle: {
74
+ width: 600,
75
+ height: 268,
76
+ },
77
+ footerContainer: {
78
+ flexDirection: 'row',
79
+ alignItems: 'center',
80
+ gap: SPACING[200],
81
+ },
82
+ })
@@ -0,0 +1,62 @@
1
+ import { createEffect, createEvent, restore } from 'effector'
2
+ import { AudioRecorder, setAudioModeAsync } from 'expo-audio'
3
+ import { VoiceRecorderState } from '../constants'
4
+ import { AvailableAudioInputs, VoiceRecordCollectionItem } from '../types'
5
+
6
+ const AUDIO_MODE_CONFIG = {
7
+ playsInSilentMode: true,
8
+ allowsRecording: true,
9
+ } as const
10
+
11
+ type StopResolver = (item: VoiceRecordCollectionItem) => void
12
+
13
+ export class RecorderModel {
14
+ public recorder!: AudioRecorder
15
+ private stopResolver: StopResolver | null = null
16
+
17
+ public readonly reset = createEvent()
18
+ public readonly setAudioFileDurationMs = createEvent<number>()
19
+ public readonly setVoiceRecordState = createEvent<VoiceRecorderState>()
20
+ public readonly setAvailableInputs = createEvent<AvailableAudioInputs>()
21
+
22
+ public readonly $audioFileDurationMs = restore(this.setAudioFileDurationMs, 0).reset(this.reset)
23
+ public readonly $voiceRecordState = restore(
24
+ this.setVoiceRecordState,
25
+ VoiceRecorderState.IDLE,
26
+ ).reset(this.reset)
27
+ public readonly $availableInputs = restore(this.setAvailableInputs, [])
28
+
29
+ public readonly start = createEffect(async () => {
30
+ try {
31
+ await setAudioModeAsync(AUDIO_MODE_CONFIG)
32
+ await this.recorder.prepareToRecordAsync()
33
+ this.recorder.record()
34
+ } catch (error) {
35
+ console.error('Error starting recording:', error)
36
+ throw error
37
+ }
38
+ })
39
+
40
+ public readonly stop = createEffect(() => {
41
+ if (!this.recorder.isRecording) return Promise.resolve()
42
+ return this.recorder.stop()
43
+ })
44
+
45
+ public stopAndGetCollectionItem(): Promise<VoiceRecordCollectionItem> {
46
+ return new Promise((resolve) => {
47
+ this.stopResolver = resolve
48
+ this.recorder.isRecording && this.recorder.stop()
49
+ })
50
+ }
51
+
52
+ public resolveStopWithCollectionItem(item: VoiceRecordCollectionItem): void {
53
+ if (this.stopResolver) {
54
+ this.stopResolver(item)
55
+ this.stopResolver = null
56
+ }
57
+ }
58
+
59
+ public clearStopResolver(): void {
60
+ this.stopResolver = null
61
+ }
62
+ }
@@ -1,51 +1,212 @@
1
- import { createEffect, createEvent, createStore, restore, sample } from 'effector'
2
- import { getAvailableInputs } from '../helpers'
3
- import { RecordingInput } from 'expo-audio'
4
-
5
- type ShowToastVariants = {
6
- info: (message: string) => void
7
- error: (message: string) => void
8
- warning: (message: string) => void
9
- }
1
+ import { attach, createEffect, createEvent, createStore, restore, sample } from 'effector'
2
+ import {
3
+ ensureRecordingPermissions,
4
+ generateAudioFormData,
5
+ getAudioDurationMs,
6
+ getAvailableInputs,
7
+ uploadAudioFileRequestGenerator,
8
+ } from '../helpers'
9
+ import { AudioApi, VoiceRecordNotificationTypes, VoiceRecordCollectionItem } from '../types'
10
+ import { t } from '../../../shared/translation'
11
+ import { RecorderModel } from './Recorder.model'
12
+ import { VoiceRecordsCollection } from './VoiceRecordCollection'
13
+ import { VoiceRecorderState } from '../constants'
10
14
 
11
15
  type VoiceRecordModelParams = {
12
- showToast: ShowToastVariants
16
+ api: AudioApi
17
+ notification: VoiceRecordNotificationTypes
18
+ events: VoiceRecordEvents
19
+ }
20
+
21
+ type UploadParams = {
22
+ uri: string
23
+ currentKey: string
24
+ }
25
+
26
+ type VoiceRecordEvents = {
27
+ deleteRecord: () => void
28
+ undoRecord: () => void
29
+ }
30
+
31
+ type DeleteCurrentRecordingProps = {
32
+ notification?: boolean
33
+ }
34
+ type DeleteCurrentRecordingValues = {
35
+ currentKey: string
36
+ notification?: boolean
13
37
  }
14
38
 
15
39
  export class VoiceRecordModel {
16
- public readonly startRecord = createEvent()
17
- public readonly stop = createEvent()
40
+ public readonly recorderModel: RecorderModel
41
+ public readonly collection: VoiceRecordsCollection
42
+ public readonly api: AudioApi
43
+ public readonly notification: VoiceRecordNotificationTypes
44
+ public readonly events: VoiceRecordEvents
45
+ private shouldDiscardRecording = false
46
+
47
+ public readonly setCurrentKey = createEvent<string>()
48
+ public readonly setCurrentRecord = createEvent<any>()
49
+ public readonly reset = createEvent()
50
+ public readonly setIsDisabled = createEvent<boolean>()
51
+ public readonly startAudioUpload = createEvent<string>()
52
+
53
+ public readonly $currentKey = restore(this.setCurrentKey, '')
54
+ public readonly $currentRecord = restore(this.setCurrentRecord, null).reset(this.reset)
18
55
 
19
- public readonly deleteFile = createEvent()
56
+ public readonly $isDisabled = restore(this.setIsDisabled, false)
20
57
 
21
- public readonly setAudioFilePath = createEvent<string>()
22
- public readonly clearAudioFilePath = createEvent()
23
- public readonly $audioFilePath = restore(this.setAudioFilePath, '').reset(this.clearAudioFilePath)
58
+ public readonly deleteCurrentRecording = attach({
59
+ source: this.$currentKey,
60
+ mapParams: (props: DeleteCurrentRecordingProps | void, currentKey) => ({
61
+ notification: true,
62
+ ...props,
63
+ currentKey,
64
+ }),
65
+ effect: createEffect(({ currentKey, notification }: DeleteCurrentRecordingValues) => {
66
+ const recordingItem = this.collection.get(currentKey)
24
67
 
25
- public readonly showToast: ShowToastVariants
68
+ this.abortUploadIfInProgress(recordingItem)
69
+ this.resetRecordingState(currentKey)
26
70
 
27
- public readonly resetIsRecording = createEvent()
28
- public readonly setIsRecording = createEvent<boolean>()
29
- public readonly $isRecording = restore(this.setIsRecording, false).reset(this.resetIsRecording)
71
+ if (notification) {
72
+ this.notification.info(t('voiceRecorder.recordingDeleted'))
73
+ }
74
+ }),
75
+ })
30
76
 
31
- public readonly setAvailableInputs = createEvent<Awaited<ReturnType<typeof getAvailableInputs>>>()
32
- public readonly $availableInputs = restore(this.setAvailableInputs, [])
77
+ public readonly initializeRecording = createEffect(async () => {
78
+ this.deleteCurrentRecording({ notification: false })
33
79
 
34
- constructor({ showToast }: VoiceRecordModelParams) {
35
- this.showToast = showToast
80
+ const hasPermissions = await ensureRecordingPermissions()
81
+ if (!hasPermissions) {
82
+ this.notification.error(t('voiceRecorder.noMicrophoneAccess'))
83
+ return
84
+ }
85
+
86
+ const inputs = await getAvailableInputs(this.recorderModel.recorder)
87
+ this.recorderModel.setAvailableInputs(inputs)
88
+
89
+ if (inputs.length === 0) {
90
+ this.notification.error(t('voiceRecorder.noMicrophoneFound'))
91
+ return
92
+ }
93
+
94
+ return this.recorderModel.start()
95
+ })
96
+
97
+ public readonly stopAndDiscard = createEffect(async () => {
98
+ this.shouldDiscardRecording = true
99
+ await this.recorderModel.stop()
100
+ this.recorderModel.clearStopResolver()
101
+ this.recorderModel.reset()
102
+ })
103
+
104
+ public readonly recordingStatusUpdate = (status: any) => {
105
+ if (status.error) {
106
+ this.notification.error(`Recorder error: ${status.error}`)
107
+ this.recorderModel.clearStopResolver()
108
+ this.shouldDiscardRecording = false
109
+ return
110
+ }
111
+ if (status.isFinished && status.url) {
112
+ if (!this.shouldDiscardRecording) {
113
+ this.startAudioUpload(status.url)
114
+ }
115
+ this.shouldDiscardRecording = false
116
+ }
117
+ }
118
+
119
+ constructor({ api, notification, events }: VoiceRecordModelParams) {
120
+ this.api = api
121
+ this.notification = notification
122
+ this.events = events
123
+ this.recorderModel = new RecorderModel()
124
+ this.collection = new VoiceRecordsCollection()
125
+
126
+ this.setupUploadHandler()
127
+ this.setupCurrentRecordSync()
128
+ this.setupUriSync()
129
+ }
36
130
 
131
+ private abortUploadIfInProgress(recordingItem: ReturnType<VoiceRecordsCollection['get']>) {
132
+ if (!recordingItem?.id && recordingItem?.controller) {
133
+ recordingItem.controller.abort()
134
+ }
135
+ }
136
+
137
+ private resetRecordingState(currentKey: string) {
138
+ this.recorderModel.reset()
139
+ this.reset()
140
+ this.collection.delete(currentKey)
141
+ }
142
+
143
+ private async handleAudioUpload({ uri, currentKey }: UploadParams) {
144
+ const durationMs = await getAudioDurationMs(uri)
145
+ const formData = await generateAudioFormData({ audioFileUri: uri, durationMs })
146
+ const { uploadAudio, controller } = uploadAudioFileRequestGenerator(formData, this.api)
147
+
148
+ const audioUploadPromise = uploadAudio()
149
+ .then((data) => {
150
+ // Check if the item still exists (wasn't deleted during upload)
151
+ if (this.collection.get(currentKey)) {
152
+ this.collection.update(currentKey, { id: data.id })
153
+ }
154
+ return data
155
+ })
156
+ .catch((error) => {
157
+ if (error.name === 'CanceledError') return
158
+ this.notification.error(t('voiceRecorder.uploadFailed'))
159
+ })
160
+
161
+ const collectionItem: VoiceRecordCollectionItem = {
162
+ durationMs,
163
+ audioUploadPromise,
164
+ controller,
165
+ uri,
166
+ }
167
+
168
+ this.collection.add(currentKey, collectionItem)
169
+ this.setCurrentRecord(collectionItem)
170
+ this.recorderModel.resolveStopWithCollectionItem(collectionItem)
171
+
172
+ return audioUploadPromise
173
+ }
174
+
175
+ private setupUploadHandler() {
37
176
  sample({
38
- clock: this.deleteFile,
39
- target: createEffect(() => {
40
- this.clearAudioFilePath()
41
- showToast.info('Recording deleted')
42
- }),
177
+ clock: this.startAudioUpload,
178
+ source: this.$currentKey,
179
+ filter: (currentKey, uri) => !!uri && !!currentKey,
180
+ fn: (currentKey, uri) => ({ currentKey, uri }),
181
+ target: createEffect(({ uri, currentKey }: UploadParams) =>
182
+ this.handleAudioUpload({ uri, currentKey }),
183
+ ),
43
184
  })
185
+ }
44
186
 
187
+ private setupUriSync() {
45
188
  sample({
46
- clock: this.startRecord,
47
- target: createEffect(async () => {
48
- showToast.info('Recording started')
189
+ source: this.$currentRecord,
190
+ fn: (record) => record?.uri || '',
191
+ target: createStore<string>('').reset(this.reset),
192
+ })
193
+ }
194
+
195
+ private setupCurrentRecordSync() {
196
+ sample({
197
+ source: this.$currentKey,
198
+ fn: (currentKey) => this.collection.get(currentKey),
199
+ target: createEffect((recordingItem?: VoiceRecordCollectionItem) => {
200
+ if (!recordingItem) {
201
+ this.reset()
202
+ return
203
+ }
204
+
205
+ this.setCurrentRecord(recordingItem)
206
+
207
+ if (recordingItem.durationMs) {
208
+ this.recorderModel.setAudioFileDurationMs(recordingItem.durationMs)
209
+ }
49
210
  }),
50
211
  })
51
212
  }
@@ -1 +1,25 @@
1
- export class VoiceRecordApi {}
1
+ import { createEffect } from 'effector'
2
+ import { AudioApi } from '../types'
3
+ import { createControllerEffect } from './../../../lib/effector/createControllerEffect'
4
+
5
+ export class VoiceRecordApi {
6
+ // public readonly uploadAudioFileFx
7
+
8
+ // public readonly abortUpload = createEffect(() => {
9
+ // if (this.uploadAudioFileFx.pending.getState()) {
10
+ // this.uploadAudioFileFx.controller.abort()
11
+ // }
12
+ // })
13
+
14
+ constructor(api: AudioApi) {
15
+ // this.uploadAudioFileFx = createControllerEffect((audioFileFormData: FormData, config) =>
16
+ // api.uploadAudioFile(
17
+ // { audioFileFormData },
18
+ // //@ts-ignore
19
+ // {
20
+ // signal: config?.signal,
21
+ // },
22
+ // ),
23
+ // )
24
+ }
25
+ }
@@ -0,0 +1,26 @@
1
+ import { VoiceRecordCollectionItem } from '../types'
2
+
3
+ export class VoiceRecordsCollection {
4
+ public readonly collection: Map<string, VoiceRecordCollectionItem> = new Map()
5
+
6
+ public add(key: string, item: VoiceRecordCollectionItem) {
7
+ this.collection.set(key, item)
8
+ }
9
+
10
+ public update(key: string, item: Partial<VoiceRecordCollectionItem>) {
11
+ const existingItem = this.collection.get(key) || {}
12
+ this.collection.set(key, { ...existingItem, ...item })
13
+ }
14
+
15
+ public delete(key: string) {
16
+ this.collection.delete(key)
17
+ }
18
+
19
+ public get(key: string) {
20
+ return this.collection.get(key)
21
+ }
22
+
23
+ public clear() {
24
+ this.collection.clear()
25
+ }
26
+ }
@@ -0,0 +1,49 @@
1
+ import { WithAbortSignal } from 'src/types/common.types'
2
+ import { getAvailableInputs } from './helpers'
3
+ import { VoiceRecordModel } from './model/VoiceRecord.model'
4
+
5
+ export type VoiceRecordNotificationTypes = {
6
+ info: (message: string) => void
7
+ error: (message: string) => void
8
+ warning: (message: string) => void
9
+ }
10
+
11
+ export type UploadAudioPayload = WithAbortSignal<{
12
+ audioFileFormData: FormData
13
+ }>
14
+
15
+ type AudioTranscriptResponse = {
16
+ text: string
17
+ }
18
+ type UploadAudioResponse = {
19
+ id: string
20
+ fileName: string
21
+ fileType: string
22
+ }
23
+
24
+ export type AudioApi = {
25
+ uploadAudioFile: (data: UploadAudioPayload) => Promise<UploadAudioResponse>
26
+ getAudioFileTranscript: (audioFileId: string) => Promise<AudioTranscriptResponse>
27
+ }
28
+
29
+ export type VoiceRecordCollectionItem = {
30
+ solutionId?: string
31
+ id?: string //it exists after upload to server
32
+ durationMs?: number
33
+ uri?: string
34
+ audioUploadPromise?: Promise<void | UploadAudioResponse>
35
+ controller?: AbortController
36
+ }
37
+
38
+ export type VoiceRecordStack = {
39
+ [problemId: string]: VoiceRecordCollectionItem
40
+ }
41
+
42
+ export type AvailableAudioInputs = Awaited<ReturnType<typeof getAvailableInputs>>
43
+
44
+ export type VoiceRecordDeleteModalParams = {
45
+ model: VoiceRecordModel
46
+ }
47
+ export type VoiceRecordUndoModalParams = {
48
+ model: VoiceRecordModel
49
+ }
@@ -0,0 +1,15 @@
1
+ import React, { ComponentType, PropsWithChildren, ReactNode } from 'react'
2
+
3
+ type ConditionalWrapperProps = {
4
+ children: ReactNode
5
+ withWrapper?: boolean
6
+ Wrapper?: ComponentType<PropsWithChildren>
7
+ }
8
+
9
+ export const ConditionalWrapper = ({ withWrapper, Wrapper, children }: ConditionalWrapperProps) => {
10
+ if (withWrapper && Wrapper) {
11
+ return <Wrapper>{children}</Wrapper>
12
+ }
13
+
14
+ return children
15
+ }
@@ -39,5 +39,18 @@
39
39
  "volume": "Volume",
40
40
  "weight": "Weight",
41
41
  "time": "Time"
42
+ },
43
+ "voiceRecorder": {
44
+ "deleteRecording": "Delete recording?",
45
+ "youWontBeAbleToRecover": "You won't be able to recover this file once it's gone.",
46
+ "recordingDeleted": "Recording successfully deleted",
47
+ "delete": "Delete",
48
+ "cancel": "Cancel",
49
+ "noMicrophoneAccess": "No microphone access",
50
+ "noMicrophoneFound": "No microphone found",
51
+ "uploadFailed": "Upload failed. Please try again.",
52
+ "redoRecording": "Redo recording?",
53
+ "redo": "Redo",
54
+ "thisWillRemoveRecording": "This will remove your previous recording."
42
55
  }
43
56
  }
@@ -39,5 +39,18 @@
39
39
  "volume": "Volumen",
40
40
  "weight": "Gewicht",
41
41
  "time": "Zeit"
42
+ },
43
+ "voiceRecorder": {
44
+ "deleteRecording": "Aufnahme löschen?",
45
+ "youWontBeAbleToRecover": "Du kannst die Datei nicht wiederherstellen, wenn sie einmal gelöscht ist.",
46
+ "recordingDeleted": "Aufnahme gelöscht.",
47
+ "delete": "Löschen",
48
+ "cancel": "Abbrechen",
49
+ "noMicrophoneAccess": "Kein Mikrofon-Zugriff.",
50
+ "noMicrophoneFound": "Kein Mikrofon gefunden.",
51
+ "uploadFailed": "Hochladen fehlgeschlagen. Bitte versuche es erneut.",
52
+ "redoRecording": "Redo recording?",
53
+ "redo": "Redo",
54
+ "thisWillRemoveRecording": "This will remove your previous recording."
42
55
  }
43
56
  }
@@ -39,5 +39,18 @@
39
39
  "volume": "Volume",
40
40
  "weight": "Weight",
41
41
  "time": "Time"
42
+ },
43
+ "voiceRecorder": {
44
+ "deleteRecording": "Delete recording?",
45
+ "youWontBeAbleToRecover": "You won't be able to recover this file once it's gone.",
46
+ "recordingDeleted": "Recording successfully deleted",
47
+ "delete": "Delete",
48
+ "cancel": "Cancel",
49
+ "noMicrophoneAccess": "No microphone access",
50
+ "noMicrophoneFound": "No microphone found",
51
+ "uploadFailed": "Upload failed. Please try again.",
52
+ "redoRecording": "Redo recording?",
53
+ "redo": "Redo",
54
+ "thisWillRemoveRecording": "This will remove your previous recording."
42
55
  }
43
56
  }