@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
@@ -1,4 +1,4 @@
1
- import React, { ComponentType, PropsWithChildren } from 'react'
1
+ import React, { ComponentType, PropsWithChildren, useMemo } from 'react'
2
2
  import { useStoreMap, useUnit } from 'effector-react'
3
3
 
4
4
  import { ChatbotModel } from '../model/ChatBotModel'
@@ -18,6 +18,7 @@ type ChatbotProps = {
18
18
  }
19
19
  ButtonWrapper?: ComponentType<PropsWithChildren>
20
20
  iconSize?: { width: number; height: number } // TODO: use number instead
21
+ isInputDisabled?: boolean
21
22
  colorScheme?: ColorScheme
22
23
  }
23
24
 
@@ -25,6 +26,7 @@ export const Chatbot = ({
25
26
  model,
26
27
  isHintFeedback,
27
28
  isTextToSpeechEnabled = false,
29
+ isInputDisabled,
28
30
  textToSpeechConfig,
29
31
  ButtonWrapper,
30
32
  iconSize,
@@ -34,6 +36,14 @@ export const Chatbot = ({
34
36
  const isAvailable = useStoreMap(model.$currentChatData, ({ isAvailable }) => isAvailable)
35
37
  model.t2s.setAudioConfig(textToSpeechConfig ?? null)
36
38
 
39
+ const shouldAnimateCloseButton = useStoreMap({
40
+ store: model.$currentChatData,
41
+ fn: (chat, [isInputDisabled]) => {
42
+ return isInputDisabled && chat.messages.length === 1
43
+ },
44
+ keys: [isInputDisabled],
45
+ })
46
+
37
47
  const openChat = () => model.setIsOpen(true)
38
48
  const closeChat = () => model.setIsOpen(false)
39
49
 
@@ -48,14 +58,17 @@ export const Chatbot = ({
48
58
  colorScheme={colorScheme}
49
59
  isHintFeedback={isHintFeedback}
50
60
  isTextToSpeechEnabled={isTextToSpeechEnabled}
61
+ isInputDisabled={isInputDisabled}
62
+ MessageWrapper={ButtonWrapper}
51
63
  />
52
64
 
53
65
  <ChatControls
54
66
  model={model}
55
67
  onPressChatIcon={openChat}
56
68
  onPressClose={closeChat}
57
- ButtonWrapper={ButtonWrapper}
58
69
  iconSize={iconSize}
70
+ ButtonWrapper={isInputDisabled ? undefined : ButtonWrapper}
71
+ shouldAnimateCloseButton={shouldAnimateCloseButton}
59
72
  />
60
73
  </ChatBotModelContext.Provider>
61
74
  )
@@ -7,7 +7,7 @@ import {
7
7
  restore,
8
8
  sample,
9
9
  type Store,
10
- StoreWritable
10
+ StoreWritable,
11
11
  } from 'effector'
12
12
  import {
13
13
  ChatbotContext,
@@ -156,7 +156,12 @@ export class ChatbotModel {
156
156
  ...source,
157
157
  }),
158
158
  effect: createEffect(
159
- ({ currentChatData, initOnly, chatAccess }: StartConversationFxData) => {
159
+ ({
160
+ currentChatData,
161
+ initOnly,
162
+ chatAccess,
163
+ withGreetingMessage = true,
164
+ }: StartConversationFxData) => {
160
165
  const { key, messages } = currentChatData
161
166
 
162
167
  if (!chatAccess) return
@@ -166,7 +171,7 @@ export class ChatbotModel {
166
171
  return this.reinitConversation({ key })
167
172
  }
168
173
  this.cache.add(key, {
169
- messages: [this.defaultMessages.start],
174
+ messages: withGreetingMessage ? [this.defaultMessages.start] : [],
170
175
  isAvailable: true,
171
176
  currentThread: null,
172
177
  })
@@ -294,6 +299,10 @@ export class ChatbotModel {
294
299
  this.cache.setSingleMessage(key, { ...message, options })
295
300
  }
296
301
 
302
+ public addMessage({ key, ...message }: SetHintMessageProps) {
303
+ this.cache.addMessages(key, { messages: [message] })
304
+ }
305
+
297
306
  public readonly reinitConversation = ({ key }: ReinitConversationProps) => {
298
307
  this.cache.reinitChatThread(key, { initialOption: this.defaultMessages.anotherHintOption })
299
308
  this.threadManager.reset(key)
@@ -17,6 +17,10 @@ type AddMessagesProps = {
17
17
  thread?: ThreadItem | null
18
18
  }
19
19
 
20
+ type AddMessageProps = {
21
+ message: ConversationMessage
22
+ }
23
+
20
24
  export function defaultChatData(props: Partial<ChatItem> = {}): ChatItem {
21
25
  return {
22
26
  messages: [],
@@ -16,6 +16,7 @@ export type GetAlternativesResponse = {
16
16
  */
17
17
  chatAccess?: boolean
18
18
  isOpenChatEnabled?: boolean
19
+ municipalityChatEnable: boolean
19
20
  version: string
20
21
  options: AlternativesOptions
21
22
  }
@@ -75,6 +75,7 @@ export type SetHintMessageProps = {
75
75
 
76
76
  export type StartConversationProps = {
77
77
  initOnly?: boolean
78
+ withGreetingMessage?: boolean
78
79
  }
79
80
 
80
81
  export type StartConversationFxData = {
@@ -1,8 +1,8 @@
1
1
  import { StyleProp, ViewStyle, StyleSheet } from 'react-native'
2
2
  import { VoiceRecordButton } from './VoiceRecordButton'
3
- import React from 'react'
3
+ import React, { useEffect } from 'react'
4
4
  import { COLORS, SPACING } from '@magmamath/react-native-ui'
5
- import { VoiceRecordDelete } from './VoiceRecordDelete'
5
+ import { VoiceRecordDeleteButton } from './VoiceRecordDeleteButton'
6
6
  import { VoiceRecordDevider } from './VoiceRecordDevider'
7
7
  import VoiceRecordTimer from './VoiceRecordTimer'
8
8
  import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'
@@ -17,32 +17,43 @@ type VoiceRecordProps = {
17
17
  }
18
18
 
19
19
  export const VoiceRecord = ({ style, model }: VoiceRecordProps) => {
20
- const { recorder, recorderState, player, startRecording, stopRecording } = useVoiceRecorder(model)
20
+ const { recorderState } = useVoiceRecorder(model)
21
+ const [isRecording, currentRecord] = useUnit([
22
+ model.recorderModel.$voiceRecordState,
23
+ model.$currentRecord,
24
+ ])
21
25
 
22
- const { containerAnimatedStyle } = useVoiceRecorderAnimation(recorderState)
26
+ const isDisabled = useUnit(model.$isDisabled)
23
27
 
24
- const availableInputs = useUnit(model.$availableInputs)
28
+ const isExpanded = !!(isRecording || currentRecord)
29
+
30
+ const { containerAnimatedStyle } = useVoiceRecorderAnimation(isExpanded)
31
+
32
+ const cleanup = async () => {
33
+ if (model.recorderModel.recorder.getStatus().isRecording) {
34
+ await model.recorderModel.recorder.stop()
35
+ }
36
+ model.recorderModel.reset()
37
+ }
38
+
39
+ useEffect(() => {
40
+ return () => {
41
+ cleanup()
42
+ }
43
+ }, [])
25
44
 
26
45
  return (
27
46
  <Animated.View style={[styles.container, style, containerAnimatedStyle]}>
28
- <VoiceRecordButton
29
- onPress={recorderState.isRecording ? stopRecording : startRecording}
30
- isRecording={recorderState.isRecording}
31
- noAudioInputs={!availableInputs.length}
32
- model={model}
33
- />
34
- {recorderState.isRecording && (
47
+ <VoiceRecordButton model={model} />
48
+ {isExpanded && (
35
49
  <Animated.View entering={FadeIn} exiting={FadeOut} style={styles.expendedContainer}>
36
50
  <VoiceRecordTimer
37
51
  style={styles.timer}
38
- time={
39
- recorderState.durationMillis
40
- ? new Date(recorderState.durationMillis).toISOString().substring(14, 19)
41
- : '0:00'
42
- }
52
+ model={model}
53
+ recordingFileDurationMs={recorderState.durationMillis}
43
54
  />
44
55
  <VoiceRecordDevider />
45
- <VoiceRecordDelete onPress={() => model.deleteFile()} />
56
+ <VoiceRecordDeleteButton isDisabled={isDisabled} onPress={model.events.deleteRecord} />
46
57
  </Animated.View>
47
58
  )}
48
59
  </Animated.View>
@@ -6,28 +6,38 @@ import {
6
6
  ButtonStyle,
7
7
  ButtonVariant,
8
8
  } from '@magmamath/react-native-ui'
9
- import { Pressable, View, ViewStyle } from 'react-native'
9
+ import { Pressable, ViewStyle } from 'react-native'
10
10
  import { MicrophoneIcon, CheckIcon } from '@magmamath/react-native-ui'
11
11
  import { VoiceRecordModel } from '../model/VoiceRecord.model'
12
12
  import { useUnit } from 'effector-react'
13
+ import { VoiceRecorderState } from '../constants'
14
+ import { useText } from '../../../shared/translation'
13
15
 
14
16
  type VoiceRecordButtonProps = {
15
17
  style?: ButtonStyle
16
- onPress?: () => void
17
- isRecording: boolean
18
- disabled?: boolean
19
- noAudioInputs?: boolean
20
18
  model: VoiceRecordModel
21
19
  }
22
20
 
23
- export const VoiceRecordButton = ({
24
- style,
25
- onPress,
26
- isRecording,
27
- disabled,
28
- model,
29
- }: VoiceRecordButtonProps) => {
30
- const noAvailabelInputs = useUnit(model.$availableInputs).length === 0
21
+ export const VoiceRecordButton = ({ style, model }: VoiceRecordButtonProps) => {
22
+ const t = useText()
23
+
24
+ const noAvailabelInputs = useUnit(model.recorderModel.$availableInputs).length === 0
25
+ const voiceRecordState = useUnit(model.recorderModel.$voiceRecordState)
26
+ const currentKey = useUnit(model.$currentKey)
27
+ const isDisabled = useUnit(model.$isDisabled)
28
+
29
+ const onButtonPress = () => {
30
+ if (model.recorderModel.recorder.isRecording) {
31
+ model.recorderModel.stop()
32
+ return
33
+ }
34
+ if (model.collection.get(currentKey)) {
35
+ model.events.undoRecord()
36
+ return
37
+ }
38
+
39
+ model.initializeRecording()
40
+ }
31
41
 
32
42
  const combinedStyles = useMemo((): ButtonStyle => {
33
43
  return {
@@ -50,20 +60,26 @@ export const VoiceRecordButton = ({
50
60
  noAvailabelInputs
51
61
  ? (e) => {
52
62
  e.preventDefault()
53
- model.showToast.warning('availableInputs')
63
+ model.notification.warning(t('voiceRecorder.noMicrophoneFound'))
54
64
  }
55
65
  : undefined
56
66
  }
57
67
  pointerEvents={noAvailabelInputs ? 'box-only' : 'auto'}
58
68
  >
59
69
  <Button
60
- disabled={disabled || noAvailabelInputs}
61
- onPress={onPress}
70
+ disabled={isDisabled || noAvailabelInputs}
71
+ onPress={onButtonPress}
62
72
  style={combinedStyles}
63
73
  size={ButtonSize.LARGE}
64
74
  variant={ButtonVariant.SECONDARY}
65
75
  colorScheme={ButtonColor.WHITE}
66
- icon={isRecording ? <CheckIcon size={25} /> : <MicrophoneIcon size={19} />}
76
+ icon={
77
+ voiceRecordState === VoiceRecorderState.RECORDING ? (
78
+ <CheckIcon size={25} />
79
+ ) : (
80
+ <MicrophoneIcon size={19} />
81
+ )
82
+ }
67
83
  />
68
84
  </Pressable>
69
85
  )
@@ -1,15 +1,16 @@
1
1
  import { StyleSheet, TouchableOpacity } from 'react-native'
2
- import { TrashcanIcon } from '@magmamath/react-native-ui'
2
+ import { COLORS, TrashcanIcon } from '@magmamath/react-native-ui'
3
3
  import React from 'react'
4
4
 
5
5
  type VoiceRecordDeleteProps = {
6
6
  onPress?: () => void
7
+ isDisabled?: boolean
7
8
  }
8
9
 
9
- export const VoiceRecordDelete = ({ onPress }: VoiceRecordDeleteProps) => {
10
+ export const VoiceRecordDeleteButton = ({ onPress, isDisabled }: VoiceRecordDeleteProps) => {
10
11
  return (
11
- <TouchableOpacity style={styles.container} onPress={onPress}>
12
- <TrashcanIcon size={20} />
12
+ <TouchableOpacity style={styles.container} onPress={onPress} disabled={isDisabled}>
13
+ <TrashcanIcon size={20} color={isDisabled ? COLORS.NEUTRAL_5 : COLORS.NEUTRAL_9} />
13
14
  </TouchableOpacity>
14
15
  )
15
16
  }
@@ -1,16 +1,32 @@
1
1
  import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'
2
2
  import { COLORS, Typography } from '@magmamath/react-native-ui'
3
3
  import React from 'react'
4
+ import { VoiceRecordModel } from '../model/VoiceRecord.model'
5
+ import { useUnit } from 'effector-react'
6
+ import { formatDuration } from '../helpers'
4
7
 
5
8
  type VoiceRecordTimerProps = {
6
9
  style?: StyleProp<ViewStyle>
7
- time: string
10
+ model: VoiceRecordModel
11
+ recordingFileDurationMs?: number
8
12
  }
9
13
 
10
- export const VoiceRecordTimer = ({ style, time }: VoiceRecordTimerProps) => {
14
+ export const VoiceRecordTimer = ({
15
+ style,
16
+ model,
17
+ recordingFileDurationMs,
18
+ }: VoiceRecordTimerProps) => {
19
+ const savedAudioFileDurationMs = useUnit(model.recorderModel.$audioFileDurationMs)
20
+
21
+ const displayDurationMs = recordingFileDurationMs
22
+ ? recordingFileDurationMs
23
+ : savedAudioFileDurationMs
24
+
11
25
  return (
12
26
  <View style={[styles.container, style]}>
13
- <Typography style={styles.text}>{time}</Typography>
27
+ <Typography style={styles.text}>
28
+ {displayDurationMs ? formatDuration(displayDurationMs) : '0:00'}
29
+ </Typography>
14
30
  </View>
15
31
  )
16
32
  }
@@ -2,3 +2,9 @@ export const VOICE_RECORDER_CONTAINER_SIZES = {
2
2
  COLLAPSED: 61,
3
3
  EXPANDED: 152,
4
4
  }
5
+ export const VOICE_RECORDER_MAX_DURATION_MS = 300000 // 5 minutes
6
+ export enum VoiceRecorderState {
7
+ IDLE,
8
+ RECORDING,
9
+ PAUSED,
10
+ }
@@ -1,9 +1,19 @@
1
1
  import { IS_MOBILE, IS_WEB } from '@magmamath/react-native-ui'
2
2
  import {
3
+ AudioPlayer,
3
4
  AudioRecorder,
5
+ createAudioPlayer,
4
6
  getRecordingPermissionsAsync,
5
7
  requestRecordingPermissionsAsync,
6
8
  } from 'expo-audio'
9
+ import { AudioApi } from './types'
10
+
11
+ export const formatDuration = (ms: number): string => {
12
+ const totalSeconds = Math.floor(ms / 1000)
13
+ const minutes = Math.floor(totalSeconds / 60)
14
+ const seconds = totalSeconds % 60
15
+ return `${minutes}:${seconds.toString().padStart(2, '0')}`
16
+ }
7
17
 
8
18
  export const ensureRecordingPermissions = async () => {
9
19
  const { status } = await getRecordingPermissionsAsync()
@@ -33,7 +43,7 @@ export const getAvailableInputs = async (recorder: AudioRecorder) => {
33
43
  return []
34
44
  }
35
45
 
36
- export const addInputsListenerWeb = (callback: (devices: MediaDeviceInfo[]) => void) => {
46
+ export const addAudioInputsListenerWeb = (callback: (devices: MediaDeviceInfo[]) => void) => {
37
47
  if (!IS_WEB || !navigator.mediaDevices?.addEventListener) {
38
48
  return () => {}
39
49
  }
@@ -51,3 +61,107 @@ export const addInputsListenerWeb = (callback: (devices: MediaDeviceInfo[]) => v
51
61
  navigator.mediaDevices.removeEventListener('devicechange', handleDeviceChange)
52
62
  }
53
63
  }
64
+ /**
65
+ *
66
+ * @param fileUri
67
+ * @param externalPlayer - is optional expo-audio player, but should be provided
68
+ * because creating player is resource consuming operation
69
+ */
70
+ export const getAudioDurationMs = (
71
+ fileUri: string,
72
+ externalPlayer?: AudioPlayer,
73
+ ): Promise<number | undefined> => {
74
+ if (!fileUri) return Promise.resolve(undefined)
75
+
76
+ const player = externalPlayer ?? createAudioPlayer()
77
+ const isInternalPlayer = !externalPlayer
78
+
79
+ player.replace(fileUri)
80
+
81
+ return new Promise((resolve, reject) => {
82
+ const subscription = player.addListener('playbackStatusUpdate', () => {
83
+ if (player.duration !== undefined && player.duration > 0) {
84
+ subscription.remove()
85
+
86
+ // player.duration is in seconds (e.g., 1.234567)
87
+ const durationMs = Math.round(player.duration * 1000) // convert to milliseconds
88
+
89
+ if (isInternalPlayer) {
90
+ player.remove()
91
+ }
92
+
93
+ resolve(durationMs)
94
+ }
95
+ })
96
+
97
+ setTimeout(() => {
98
+ subscription.remove()
99
+
100
+ if (isInternalPlayer) {
101
+ player.remove()
102
+ }
103
+
104
+ reject(new Error('Timeout loading audio metadata'))
105
+ }, 3000)
106
+ })
107
+ }
108
+
109
+ type GenerateAudioFormDataParams = {
110
+ audioFileUri: string
111
+ durationMs?: number
112
+ fileName?: string
113
+ }
114
+
115
+ export const generateAudioFormData = async ({
116
+ audioFileUri,
117
+ durationMs,
118
+ fileName,
119
+ }: GenerateAudioFormDataParams): Promise<FormData> => {
120
+ //todo: add web or ios prefix to filename
121
+
122
+ const formData = new FormData()
123
+ const duration = durationMs?.toString() || '0'
124
+
125
+ if (IS_WEB) {
126
+ const response = await fetch(audioFileUri)
127
+ const blob = await response.blob()
128
+ const filename = fileName || 'solution_voice_record.webm'
129
+
130
+ formData.append('file', blob, filename)
131
+ formData.append('filename', filename)
132
+ formData.append('duration', duration)
133
+
134
+ return formData
135
+ }
136
+
137
+ const filename = fileName || audioFileUri.split('/').pop() || 'solution_voice_record.m4a'
138
+ const file = {
139
+ uri: audioFileUri,
140
+ type: 'audio/m4a',
141
+ name: filename,
142
+ } as any
143
+
144
+ formData.append('file', file, filename)
145
+ formData.append('filename', filename)
146
+ formData.append('duration', duration)
147
+
148
+ return formData
149
+ }
150
+
151
+ export const uploadAudioFileRequestGenerator = (audioFileFormData: FormData, api: AudioApi) => {
152
+ const controller = new AbortController()
153
+
154
+ return {
155
+ uploadAudio: () =>
156
+ api.uploadAudioFile(
157
+ {
158
+ audioFileFormData,
159
+ },
160
+ //@ts-ignore
161
+ {
162
+ signal: controller.signal,
163
+ },
164
+ ),
165
+ controller,
166
+ }
167
+ }
@@ -1,63 +1,77 @@
1
1
  import {
2
2
  RecordingPresets,
3
- setAudioModeAsync,
4
3
  useAudioPlayer,
5
4
  useAudioRecorder,
6
5
  useAudioRecorderState,
7
6
  } from 'expo-audio'
8
- import { addInputsListenerWeb, ensureRecordingPermissions, getAvailableInputs } from '../helpers'
7
+ import { addAudioInputsListenerWeb, getAudioDurationMs, getAvailableInputs } from '../helpers'
9
8
  import { VoiceRecordModel } from '../model/VoiceRecord.model'
10
9
  import { useEffect } from 'react'
10
+ import { IS_WEB } from '@magmamath/react-native-ui'
11
+ import { VOICE_RECORDER_MAX_DURATION_MS, VoiceRecorderState } from '../constants'
12
+ import { useUnit } from 'effector-react'
11
13
 
12
14
  export const useVoiceRecorder = (model: VoiceRecordModel) => {
13
- const recorder = useAudioRecorder(RecordingPresets.LOW_QUALITY, (status) =>
14
- console.log('Recording status:', status),
15
- )
16
- const recorderState = useAudioRecorderState(recorder)
17
-
18
15
  const player = useAudioPlayer()
16
+ const currentRecord = useUnit(model.$currentRecord)
19
17
 
20
- const startRecording = async () => {
21
- if (!(await ensureRecordingPermissions())) return model.showToast.error('No microphone access')
18
+ const recorder = useAudioRecorder(RecordingPresets.LOW_QUALITY, model.recordingStatusUpdate)
19
+ const recorderState = useAudioRecorderState(recorder)
22
20
 
23
- await recorder.prepareToRecordAsync()
21
+ useEffect(() => {
22
+ if (
23
+ recorderState.durationMillis >= VOICE_RECORDER_MAX_DURATION_MS &&
24
+ recorderState.isRecording
25
+ ) {
26
+ model.recorderModel.stop()
27
+ }
28
+ }, [recorderState.durationMillis])
24
29
 
25
- if (await getAvailableInputs(recorder).then((inputs) => inputs.length === 0)) {
26
- return model.showToast.error('No microphone found')
30
+ useEffect(() => {
31
+ if (!IS_WEB) {
32
+ //only once for iPad and listener for WEB
33
+ getAvailableInputs(recorder).then((inputs) => {
34
+ model.recorderModel.setAvailableInputs(inputs)
35
+ })
36
+ return
27
37
  }
28
- const availabelInputs = await getAvailableInputs(recorder)
29
- console.log('availabelInputs: ', availabelInputs)
30
- await setAudioModeAsync({
31
- playsInSilentMode: true,
32
- allowsRecording: true,
33
- })
34
38
 
35
- recorder.record()
36
- }
39
+ const removeListener = addAudioInputsListenerWeb((devices) => {
40
+ model.recorderModel.setAvailableInputs(devices)
41
+ })
37
42
 
38
- const stopRecording = async () => {
39
- await recorder.stop()
40
- recorder.uri && console.log('Recording stopped and stored at', recorder.uri)
43
+ return removeListener
44
+ }, [])
41
45
 
42
- player.replace(recorder.uri)
43
- player.play()
44
- }
46
+ useEffect(() => {
47
+ if (currentRecord?.uri) {
48
+ const getDuration = async () => {
49
+ const durationMs = await getAudioDurationMs(currentRecord.uri!, player)
50
+ model.recorderModel.setAudioFileDurationMs(durationMs || 0)
51
+ }
52
+ getDuration()
53
+ }
54
+ }, [currentRecord?.uri])
45
55
 
46
56
  useEffect(() => {
47
- model.setIsRecording(recorderState.isRecording)
57
+ if (recorderState.isRecording) {
58
+ model.recorderModel.setVoiceRecordState(VoiceRecorderState.RECORDING)
59
+ return
60
+ }
61
+ if (recorderState.durationMillis > 0) {
62
+ model.recorderModel.setVoiceRecordState(VoiceRecorderState.PAUSED)
63
+ return
64
+ }
65
+ model.recorderModel.setVoiceRecordState(VoiceRecorderState.IDLE)
48
66
 
49
67
  return () => {
50
- model.resetIsRecording()
68
+ model.recorderModel.reset()
51
69
  }
52
- }, [recorderState.isRecording])
70
+ }, [recorderState])
53
71
 
54
72
  useEffect(() => {
55
- const removeListener = addInputsListenerWeb((devices) => {
56
- model.setAvailableInputs(devices)
57
- })
58
-
59
- return removeListener
60
- }, [])
73
+ model.recorderModel.recorder = recorder
74
+ }, [recorder])
61
75
 
62
- return { recorder, recorderState, player, startRecording, stopRecording }
76
+ return { recorderState }
63
77
  }
@@ -1,12 +1,11 @@
1
1
  import { useAnimatedStyle, withTiming } from 'react-native-reanimated'
2
2
  import { VOICE_RECORDER_CONTAINER_SIZES } from '../constants'
3
- import { RecorderState } from 'expo-audio'
4
3
 
5
- export const useVoiceRecorderAnimation = (recorderState: RecorderState) => {
4
+ export const useVoiceRecorderAnimation = (isExpanded: boolean) => {
6
5
  const containerAnimatedStyle = useAnimatedStyle(() => {
7
6
  return {
8
7
  height: withTiming(
9
- recorderState.isRecording
8
+ isExpanded
10
9
  ? VOICE_RECORDER_CONTAINER_SIZES.EXPANDED
11
10
  : VOICE_RECORDER_CONTAINER_SIZES.COLLAPSED,
12
11
  {
@@ -14,7 +13,7 @@ export const useVoiceRecorderAnimation = (recorderState: RecorderState) => {
14
13
  },
15
14
  ),
16
15
  }
17
- }, [recorderState])
16
+ }, [isExpanded])
18
17
 
19
18
  return { containerAnimatedStyle }
20
19
  }
@@ -1,2 +1,6 @@
1
1
  export * from './components/VoiceRecord'
2
2
  export * from './model/VoiceRecord.model'
3
+ export * from './modal/VoiceRecordDeleteModal'
4
+ export * from './modal/VoiceRecordUndoModal'
5
+ export * from './constants'
6
+ export * from './types'