@magmamath/students-features 0.9.113 → 0.9.114-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) hide show
  1. package/dist/commonjs/features/chatbot/components/ChatInput/RequestHintButton.js.map +1 -1
  2. package/dist/commonjs/features/chatbot/components/ChatMessage/MessageButtonsBlock.js.map +1 -1
  3. package/dist/commonjs/features/chatbot/components/ChatMessage/MessageTextToSpeech.js +8 -48
  4. package/dist/commonjs/features/chatbot/components/ChatMessage/MessageTextToSpeech.js.map +1 -1
  5. package/dist/commonjs/features/chatbot/constants.js +4 -4
  6. package/dist/commonjs/features/chatbot/constants.js.map +1 -1
  7. package/dist/commonjs/features/gifCelebrations/helpers.js +1 -0
  8. package/dist/commonjs/features/gifCelebrations/helpers.js.map +1 -1
  9. package/dist/commonjs/features/voice/constants.js +22 -0
  10. package/dist/commonjs/features/voice/constants.js.map +1 -0
  11. package/dist/commonjs/features/voice/helpers.js +211 -0
  12. package/dist/commonjs/features/voice/helpers.js.map +1 -0
  13. package/dist/commonjs/features/voice/index.js +138 -0
  14. package/dist/commonjs/features/voice/index.js.map +1 -0
  15. package/dist/commonjs/features/voice/model/index.js +68 -0
  16. package/dist/commonjs/features/voice/model/index.js.map +1 -0
  17. package/dist/commonjs/features/voice/playing/components/VoiceIcon.js +49 -0
  18. package/dist/commonjs/features/voice/playing/components/VoiceIcon.js.map +1 -0
  19. package/dist/commonjs/features/voice/playing/components/VoiceTranscriptionsDropdown.js +208 -0
  20. package/dist/commonjs/features/voice/playing/components/VoiceTranscriptionsDropdown.js.map +1 -0
  21. package/dist/commonjs/features/voice/playing/components/VoiceTranscriptionsDropdownItem.js +108 -0
  22. package/dist/commonjs/features/voice/playing/components/VoiceTranscriptionsDropdownItem.js.map +1 -0
  23. package/dist/commonjs/features/voice/playing/hooks/useVoiceTranscriptionsDropdown.js +50 -0
  24. package/dist/commonjs/features/voice/playing/hooks/useVoiceTranscriptionsDropdown.js.map +1 -0
  25. package/dist/commonjs/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.js +34 -0
  26. package/dist/commonjs/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.js.map +1 -0
  27. package/dist/commonjs/features/voice/playing/model/Player.model.js +87 -0
  28. package/dist/commonjs/features/voice/playing/model/Player.model.js.map +1 -0
  29. package/dist/commonjs/features/voice/playing/model/VoicePlayer.model.js +244 -0
  30. package/dist/commonjs/features/voice/playing/model/VoicePlayer.model.js.map +1 -0
  31. package/dist/commonjs/features/voice/playing/model/VoiceTranscriptionsCollection.js +41 -0
  32. package/dist/commonjs/features/voice/playing/model/VoiceTranscriptionsCollection.js.map +1 -0
  33. package/dist/commonjs/features/voice/playing/model/VoiceTranscriptionsDownloader.model.js +39 -0
  34. package/dist/commonjs/features/voice/playing/model/VoiceTranscriptionsDownloader.model.js.map +1 -0
  35. package/dist/commonjs/features/voice/playing/model/VoiceTranscriptionsDropdown.model.js +15 -0
  36. package/dist/commonjs/features/voice/playing/model/VoiceTranscriptionsDropdown.model.js.map +1 -0
  37. package/dist/commonjs/features/voice/playing/model/index.js +47 -0
  38. package/dist/commonjs/features/voice/playing/model/index.js.map +1 -0
  39. package/dist/commonjs/features/voice/recording/components/VoiceRecord.js +74 -0
  40. package/dist/commonjs/features/voice/recording/components/VoiceRecord.js.map +1 -0
  41. package/dist/commonjs/features/voice/recording/components/VoiceRecordButton.js +61 -0
  42. package/dist/commonjs/features/voice/recording/components/VoiceRecordButton.js.map +1 -0
  43. package/dist/commonjs/features/voice/recording/components/VoiceRecordDeleteButton.js +35 -0
  44. package/dist/commonjs/features/voice/recording/components/VoiceRecordDeleteButton.js.map +1 -0
  45. package/dist/commonjs/features/voice/recording/components/VoiceRecordDevider.js +27 -0
  46. package/dist/commonjs/features/voice/recording/components/VoiceRecordDevider.js.map +1 -0
  47. package/dist/commonjs/features/voice/recording/components/VoiceRecordTimer.js +50 -0
  48. package/dist/commonjs/features/voice/recording/components/VoiceRecordTimer.js.map +1 -0
  49. package/dist/commonjs/features/voice/recording/hooks/useVoiceRecorder.js +59 -0
  50. package/dist/commonjs/features/voice/recording/hooks/useVoiceRecorder.js.map +1 -0
  51. package/dist/commonjs/features/voice/recording/hooks/useVoiceRecorderAnimation.js +22 -0
  52. package/dist/commonjs/features/voice/recording/hooks/useVoiceRecorderAnimation.js.map +1 -0
  53. package/dist/commonjs/features/voice/recording/modal/ModalFooterButtons.js +53 -0
  54. package/dist/commonjs/features/voice/recording/modal/ModalFooterButtons.js.map +1 -0
  55. package/dist/commonjs/features/voice/recording/modal/VoiceRecordDeleteModal.js +78 -0
  56. package/dist/commonjs/features/voice/recording/modal/VoiceRecordDeleteModal.js.map +1 -0
  57. package/dist/commonjs/features/voice/recording/modal/VoiceRecordUndoModal.js +51 -0
  58. package/dist/commonjs/features/voice/recording/modal/VoiceRecordUndoModal.js.map +1 -0
  59. package/dist/commonjs/features/voice/recording/model/Recorder.model.js +54 -0
  60. package/dist/commonjs/features/voice/recording/model/Recorder.model.js.map +1 -0
  61. package/dist/commonjs/features/voice/recording/model/Uploader.model.js +44 -0
  62. package/dist/commonjs/features/voice/recording/model/Uploader.model.js.map +1 -0
  63. package/dist/commonjs/features/voice/recording/model/VoiceRecord.model.js +239 -0
  64. package/dist/commonjs/features/voice/recording/model/VoiceRecord.model.js.map +1 -0
  65. package/dist/commonjs/features/voice/recording/model/VoiceRecordCollection.js +30 -0
  66. package/dist/commonjs/features/voice/recording/model/VoiceRecordCollection.js.map +1 -0
  67. package/dist/commonjs/features/voice/recording/model/index.js +34 -0
  68. package/dist/commonjs/features/voice/recording/model/index.js.map +1 -0
  69. package/dist/commonjs/features/voice/types.js +6 -0
  70. package/dist/commonjs/features/voice/types.js.map +1 -0
  71. package/dist/commonjs/index.js +12 -0
  72. package/dist/commonjs/index.js.map +1 -1
  73. package/dist/commonjs/shared/components/PlayButton.js +70 -0
  74. package/dist/commonjs/shared/components/PlayButton.js.map +1 -0
  75. package/dist/commonjs/shared/translation/localization/ca.json +17 -0
  76. package/dist/commonjs/shared/translation/localization/de.json +17 -0
  77. package/dist/commonjs/shared/translation/localization/en.json +17 -0
  78. package/dist/commonjs/shared/translation/localization/gb.json +17 -0
  79. package/dist/commonjs/shared/translation/localization/sct.json +17 -0
  80. package/dist/commonjs/shared/translation/localization/sw.json +17 -0
  81. package/dist/module/features/chatbot/components/ChatInput/RequestHintButton.js.map +1 -1
  82. package/dist/module/features/chatbot/components/ChatMessage/MessageButtonsBlock.js.map +1 -1
  83. package/dist/module/features/chatbot/components/ChatMessage/MessageTextToSpeech.js +9 -49
  84. package/dist/module/features/chatbot/components/ChatMessage/MessageTextToSpeech.js.map +1 -1
  85. package/dist/module/features/chatbot/constants.js +5 -5
  86. package/dist/module/features/chatbot/constants.js.map +1 -1
  87. package/dist/module/features/gifCelebrations/helpers.js +1 -0
  88. package/dist/module/features/gifCelebrations/helpers.js.map +1 -1
  89. package/dist/module/features/voice/constants.js +18 -0
  90. package/dist/module/features/voice/constants.js.map +1 -0
  91. package/dist/module/features/voice/helpers.js +194 -0
  92. package/dist/module/features/voice/helpers.js.map +1 -0
  93. package/dist/module/features/voice/index.js +20 -0
  94. package/dist/module/features/voice/index.js.map +1 -0
  95. package/dist/module/features/voice/model/index.js +15 -0
  96. package/dist/module/features/voice/model/index.js.map +1 -0
  97. package/dist/module/features/voice/playing/components/VoiceIcon.js +41 -0
  98. package/dist/module/features/voice/playing/components/VoiceIcon.js.map +1 -0
  99. package/dist/module/features/voice/playing/components/VoiceTranscriptionsDropdown.js +201 -0
  100. package/dist/module/features/voice/playing/components/VoiceTranscriptionsDropdown.js.map +1 -0
  101. package/dist/module/features/voice/playing/components/VoiceTranscriptionsDropdownItem.js +102 -0
  102. package/dist/module/features/voice/playing/components/VoiceTranscriptionsDropdownItem.js.map +1 -0
  103. package/dist/module/features/voice/playing/hooks/useVoiceTranscriptionsDropdown.js +45 -0
  104. package/dist/module/features/voice/playing/hooks/useVoiceTranscriptionsDropdown.js.map +1 -0
  105. package/dist/module/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.js +29 -0
  106. package/dist/module/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.js.map +1 -0
  107. package/dist/module/features/voice/playing/model/Player.model.js +82 -0
  108. package/dist/module/features/voice/playing/model/Player.model.js.map +1 -0
  109. package/dist/module/features/voice/playing/model/VoicePlayer.model.js +234 -0
  110. package/dist/module/features/voice/playing/model/VoicePlayer.model.js.map +1 -0
  111. package/dist/module/features/voice/playing/model/VoiceTranscriptionsCollection.js +36 -0
  112. package/dist/module/features/voice/playing/model/VoiceTranscriptionsCollection.js.map +1 -0
  113. package/dist/module/features/voice/playing/model/VoiceTranscriptionsDownloader.model.js +34 -0
  114. package/dist/module/features/voice/playing/model/VoiceTranscriptionsDownloader.model.js.map +1 -0
  115. package/dist/module/features/voice/playing/model/VoiceTranscriptionsDropdown.model.js +10 -0
  116. package/dist/module/features/voice/playing/model/VoiceTranscriptionsDropdown.model.js.map +1 -0
  117. package/dist/module/features/voice/playing/model/index.js +8 -0
  118. package/dist/module/features/voice/playing/model/index.js.map +1 -0
  119. package/dist/module/features/voice/recording/components/VoiceRecord.js +67 -0
  120. package/dist/module/features/voice/recording/components/VoiceRecord.js.map +1 -0
  121. package/dist/module/features/voice/recording/components/VoiceRecordButton.js +55 -0
  122. package/dist/module/features/voice/recording/components/VoiceRecordButton.js.map +1 -0
  123. package/dist/module/features/voice/recording/components/VoiceRecordDeleteButton.js +29 -0
  124. package/dist/module/features/voice/recording/components/VoiceRecordDeleteButton.js.map +1 -0
  125. package/dist/module/features/voice/recording/components/VoiceRecordDevider.js +21 -0
  126. package/dist/module/features/voice/recording/components/VoiceRecordDevider.js.map +1 -0
  127. package/dist/module/features/voice/recording/components/VoiceRecordTimer.js +44 -0
  128. package/dist/module/features/voice/recording/components/VoiceRecordTimer.js.map +1 -0
  129. package/dist/module/features/voice/recording/hooks/useVoiceRecorder.js +54 -0
  130. package/dist/module/features/voice/recording/hooks/useVoiceRecorder.js.map +1 -0
  131. package/dist/module/features/voice/recording/hooks/useVoiceRecorderAnimation.js +17 -0
  132. package/dist/module/features/voice/recording/hooks/useVoiceRecorderAnimation.js.map +1 -0
  133. package/dist/module/features/voice/recording/modal/ModalFooterButtons.js +47 -0
  134. package/dist/module/features/voice/recording/modal/ModalFooterButtons.js.map +1 -0
  135. package/dist/module/features/voice/recording/modal/VoiceRecordDeleteModal.js +71 -0
  136. package/dist/module/features/voice/recording/modal/VoiceRecordDeleteModal.js.map +1 -0
  137. package/dist/module/features/voice/recording/modal/VoiceRecordUndoModal.js +45 -0
  138. package/dist/module/features/voice/recording/modal/VoiceRecordUndoModal.js.map +1 -0
  139. package/dist/module/features/voice/recording/model/Recorder.model.js +49 -0
  140. package/dist/module/features/voice/recording/model/Recorder.model.js.map +1 -0
  141. package/dist/module/features/voice/recording/model/Uploader.model.js +39 -0
  142. package/dist/module/features/voice/recording/model/Uploader.model.js.map +1 -0
  143. package/dist/module/features/voice/recording/model/VoiceRecord.model.js +234 -0
  144. package/dist/module/features/voice/recording/model/VoiceRecord.model.js.map +1 -0
  145. package/dist/module/features/voice/recording/model/VoiceRecordCollection.js +25 -0
  146. package/dist/module/features/voice/recording/model/VoiceRecordCollection.js.map +1 -0
  147. package/dist/module/features/voice/recording/model/index.js +7 -0
  148. package/dist/module/features/voice/recording/model/index.js.map +1 -0
  149. package/dist/module/features/voice/types.js +4 -0
  150. package/dist/module/features/voice/types.js.map +1 -0
  151. package/dist/module/index.js +1 -0
  152. package/dist/module/index.js.map +1 -1
  153. package/dist/module/shared/components/PlayButton.js +64 -0
  154. package/dist/module/shared/components/PlayButton.js.map +1 -0
  155. package/dist/module/shared/translation/localization/ca.json +17 -0
  156. package/dist/module/shared/translation/localization/de.json +17 -0
  157. package/dist/module/shared/translation/localization/en.json +17 -0
  158. package/dist/module/shared/translation/localization/gb.json +17 -0
  159. package/dist/module/shared/translation/localization/sct.json +17 -0
  160. package/dist/module/shared/translation/localization/sw.json +17 -0
  161. package/dist/typescript/commonjs/features/chatbot/components/ChatInput/RequestHintButton.d.ts.map +1 -1
  162. package/dist/typescript/commonjs/features/chatbot/components/ChatMessage/MessageButtonsBlock.d.ts.map +1 -1
  163. package/dist/typescript/commonjs/features/chatbot/components/ChatMessage/MessageTextToSpeech.d.ts +2 -2
  164. package/dist/typescript/commonjs/features/chatbot/components/ChatMessage/MessageTextToSpeech.d.ts.map +1 -1
  165. package/dist/typescript/commonjs/features/chatbot/constants.d.ts +5 -5
  166. package/dist/typescript/commonjs/features/chatbot/constants.d.ts.map +1 -1
  167. package/dist/typescript/commonjs/features/gifCelebrations/helpers.d.ts.map +1 -1
  168. package/dist/typescript/commonjs/features/voice/constants.d.ts +15 -0
  169. package/dist/typescript/commonjs/features/voice/constants.d.ts.map +1 -0
  170. package/dist/typescript/commonjs/features/voice/helpers.d.ts +46 -0
  171. package/dist/typescript/commonjs/features/voice/helpers.d.ts.map +1 -0
  172. package/dist/typescript/commonjs/features/voice/index.d.ts +13 -0
  173. package/dist/typescript/commonjs/features/voice/index.d.ts.map +1 -0
  174. package/dist/typescript/commonjs/features/voice/model/index.d.ts +11 -0
  175. package/dist/typescript/commonjs/features/voice/model/index.d.ts.map +1 -0
  176. package/dist/typescript/commonjs/features/voice/playing/components/VoiceIcon.d.ts +8 -0
  177. package/dist/typescript/commonjs/features/voice/playing/components/VoiceIcon.d.ts.map +1 -0
  178. package/dist/typescript/commonjs/features/voice/playing/components/VoiceTranscriptionsDropdown.d.ts +15 -0
  179. package/dist/typescript/commonjs/features/voice/playing/components/VoiceTranscriptionsDropdown.d.ts.map +1 -0
  180. package/dist/typescript/commonjs/features/voice/playing/components/VoiceTranscriptionsDropdownItem.d.ts +12 -0
  181. package/dist/typescript/commonjs/features/voice/playing/components/VoiceTranscriptionsDropdownItem.d.ts.map +1 -0
  182. package/dist/typescript/commonjs/features/voice/playing/hooks/useVoiceTranscriptionsDropdown.d.ts +16 -0
  183. package/dist/typescript/commonjs/features/voice/playing/hooks/useVoiceTranscriptionsDropdown.d.ts.map +1 -0
  184. package/dist/typescript/commonjs/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.d.ts +11 -0
  185. package/dist/typescript/commonjs/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.d.ts.map +1 -0
  186. package/dist/typescript/commonjs/features/voice/playing/model/Player.model.d.ts +21 -0
  187. package/dist/typescript/commonjs/features/voice/playing/model/Player.model.d.ts.map +1 -0
  188. package/dist/typescript/commonjs/features/voice/playing/model/VoicePlayer.model.d.ts +41 -0
  189. package/dist/typescript/commonjs/features/voice/playing/model/VoicePlayer.model.d.ts.map +1 -0
  190. package/dist/typescript/commonjs/features/voice/playing/model/VoiceTranscriptionsCollection.d.ts +26 -0
  191. package/dist/typescript/commonjs/features/voice/playing/model/VoiceTranscriptionsCollection.d.ts.map +1 -0
  192. package/dist/typescript/commonjs/features/voice/playing/model/VoiceTranscriptionsDownloader.model.d.ts +18 -0
  193. package/dist/typescript/commonjs/features/voice/playing/model/VoiceTranscriptionsDownloader.model.d.ts.map +1 -0
  194. package/dist/typescript/commonjs/features/voice/playing/model/VoiceTranscriptionsDropdown.model.d.ts +7 -0
  195. package/dist/typescript/commonjs/features/voice/playing/model/VoiceTranscriptionsDropdown.model.d.ts.map +1 -0
  196. package/dist/typescript/commonjs/features/voice/playing/model/index.d.ts +7 -0
  197. package/dist/typescript/commonjs/features/voice/playing/model/index.d.ts.map +1 -0
  198. package/dist/typescript/commonjs/features/voice/recording/components/VoiceRecord.d.ts +10 -0
  199. package/dist/typescript/commonjs/features/voice/recording/components/VoiceRecord.d.ts.map +1 -0
  200. package/dist/typescript/commonjs/features/voice/recording/components/VoiceRecordButton.d.ts +10 -0
  201. package/dist/typescript/commonjs/features/voice/recording/components/VoiceRecordButton.d.ts.map +1 -0
  202. package/dist/typescript/commonjs/features/voice/recording/components/VoiceRecordDeleteButton.d.ts +8 -0
  203. package/dist/typescript/commonjs/features/voice/recording/components/VoiceRecordDeleteButton.d.ts.map +1 -0
  204. package/dist/typescript/commonjs/features/voice/recording/components/VoiceRecordDevider.d.ts +3 -0
  205. package/dist/typescript/commonjs/features/voice/recording/components/VoiceRecordDevider.d.ts.map +1 -0
  206. package/dist/typescript/commonjs/features/voice/recording/components/VoiceRecordTimer.d.ts +11 -0
  207. package/dist/typescript/commonjs/features/voice/recording/components/VoiceRecordTimer.d.ts.map +1 -0
  208. package/dist/typescript/commonjs/features/voice/recording/hooks/useVoiceRecorder.d.ts +5 -0
  209. package/dist/typescript/commonjs/features/voice/recording/hooks/useVoiceRecorder.d.ts.map +1 -0
  210. package/dist/typescript/commonjs/features/voice/recording/hooks/useVoiceRecorderAnimation.d.ts +6 -0
  211. package/dist/typescript/commonjs/features/voice/recording/hooks/useVoiceRecorderAnimation.d.ts.map +1 -0
  212. package/dist/typescript/commonjs/features/voice/recording/modal/ModalFooterButtons.d.ts +12 -0
  213. package/dist/typescript/commonjs/features/voice/recording/modal/ModalFooterButtons.d.ts.map +1 -0
  214. package/dist/typescript/commonjs/features/voice/recording/modal/VoiceRecordDeleteModal.d.ts +11 -0
  215. package/dist/typescript/commonjs/features/voice/recording/modal/VoiceRecordDeleteModal.d.ts.map +1 -0
  216. package/dist/typescript/commonjs/features/voice/recording/modal/VoiceRecordUndoModal.d.ts +11 -0
  217. package/dist/typescript/commonjs/features/voice/recording/modal/VoiceRecordUndoModal.d.ts.map +1 -0
  218. package/dist/typescript/commonjs/features/voice/recording/model/Recorder.model.d.ts +19 -0
  219. package/dist/typescript/commonjs/features/voice/recording/model/Recorder.model.d.ts.map +1 -0
  220. package/dist/typescript/commonjs/features/voice/recording/model/Uploader.model.d.ts +18 -0
  221. package/dist/typescript/commonjs/features/voice/recording/model/Uploader.model.d.ts.map +1 -0
  222. package/dist/typescript/commonjs/features/voice/recording/model/VoiceRecord.model.d.ts +50 -0
  223. package/dist/typescript/commonjs/features/voice/recording/model/VoiceRecord.model.d.ts.map +1 -0
  224. package/dist/typescript/commonjs/features/voice/recording/model/VoiceRecordCollection.d.ts +10 -0
  225. package/dist/typescript/commonjs/features/voice/recording/model/VoiceRecordCollection.d.ts.map +1 -0
  226. package/dist/typescript/commonjs/features/voice/recording/model/index.d.ts +5 -0
  227. package/dist/typescript/commonjs/features/voice/recording/model/index.d.ts.map +1 -0
  228. package/dist/typescript/commonjs/features/voice/types.d.ts +58 -0
  229. package/dist/typescript/commonjs/features/voice/types.d.ts.map +1 -0
  230. package/dist/typescript/commonjs/index.d.ts +1 -0
  231. package/dist/typescript/commonjs/index.d.ts.map +1 -1
  232. package/dist/typescript/commonjs/shared/components/PlayButton.d.ts +18 -0
  233. package/dist/typescript/commonjs/shared/components/PlayButton.d.ts.map +1 -0
  234. package/dist/typescript/commonjs/shared/translation/constants.d.ts +102 -0
  235. package/dist/typescript/commonjs/shared/translation/constants.d.ts.map +1 -1
  236. package/dist/typescript/commonjs/shared/translation/model.d.ts +34 -0
  237. package/dist/typescript/commonjs/shared/translation/model.d.ts.map +1 -1
  238. package/dist/typescript/commonjs/types/common.types.d.ts +3 -0
  239. package/dist/typescript/commonjs/types/common.types.d.ts.map +1 -1
  240. package/dist/typescript/module/features/chatbot/components/ChatInput/RequestHintButton.d.ts.map +1 -1
  241. package/dist/typescript/module/features/chatbot/components/ChatMessage/MessageButtonsBlock.d.ts.map +1 -1
  242. package/dist/typescript/module/features/chatbot/components/ChatMessage/MessageTextToSpeech.d.ts +2 -2
  243. package/dist/typescript/module/features/chatbot/components/ChatMessage/MessageTextToSpeech.d.ts.map +1 -1
  244. package/dist/typescript/module/features/chatbot/constants.d.ts +5 -5
  245. package/dist/typescript/module/features/chatbot/constants.d.ts.map +1 -1
  246. package/dist/typescript/module/features/gifCelebrations/helpers.d.ts.map +1 -1
  247. package/dist/typescript/module/features/voice/constants.d.ts +15 -0
  248. package/dist/typescript/module/features/voice/constants.d.ts.map +1 -0
  249. package/dist/typescript/module/features/voice/helpers.d.ts +46 -0
  250. package/dist/typescript/module/features/voice/helpers.d.ts.map +1 -0
  251. package/dist/typescript/module/features/voice/index.d.ts +13 -0
  252. package/dist/typescript/module/features/voice/index.d.ts.map +1 -0
  253. package/dist/typescript/module/features/voice/model/index.d.ts +11 -0
  254. package/dist/typescript/module/features/voice/model/index.d.ts.map +1 -0
  255. package/dist/typescript/module/features/voice/playing/components/VoiceIcon.d.ts +8 -0
  256. package/dist/typescript/module/features/voice/playing/components/VoiceIcon.d.ts.map +1 -0
  257. package/dist/typescript/module/features/voice/playing/components/VoiceTranscriptionsDropdown.d.ts +15 -0
  258. package/dist/typescript/module/features/voice/playing/components/VoiceTranscriptionsDropdown.d.ts.map +1 -0
  259. package/dist/typescript/module/features/voice/playing/components/VoiceTranscriptionsDropdownItem.d.ts +12 -0
  260. package/dist/typescript/module/features/voice/playing/components/VoiceTranscriptionsDropdownItem.d.ts.map +1 -0
  261. package/dist/typescript/module/features/voice/playing/hooks/useVoiceTranscriptionsDropdown.d.ts +16 -0
  262. package/dist/typescript/module/features/voice/playing/hooks/useVoiceTranscriptionsDropdown.d.ts.map +1 -0
  263. package/dist/typescript/module/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.d.ts +11 -0
  264. package/dist/typescript/module/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.d.ts.map +1 -0
  265. package/dist/typescript/module/features/voice/playing/model/Player.model.d.ts +21 -0
  266. package/dist/typescript/module/features/voice/playing/model/Player.model.d.ts.map +1 -0
  267. package/dist/typescript/module/features/voice/playing/model/VoicePlayer.model.d.ts +41 -0
  268. package/dist/typescript/module/features/voice/playing/model/VoicePlayer.model.d.ts.map +1 -0
  269. package/dist/typescript/module/features/voice/playing/model/VoiceTranscriptionsCollection.d.ts +26 -0
  270. package/dist/typescript/module/features/voice/playing/model/VoiceTranscriptionsCollection.d.ts.map +1 -0
  271. package/dist/typescript/module/features/voice/playing/model/VoiceTranscriptionsDownloader.model.d.ts +18 -0
  272. package/dist/typescript/module/features/voice/playing/model/VoiceTranscriptionsDownloader.model.d.ts.map +1 -0
  273. package/dist/typescript/module/features/voice/playing/model/VoiceTranscriptionsDropdown.model.d.ts +7 -0
  274. package/dist/typescript/module/features/voice/playing/model/VoiceTranscriptionsDropdown.model.d.ts.map +1 -0
  275. package/dist/typescript/module/features/voice/playing/model/index.d.ts +7 -0
  276. package/dist/typescript/module/features/voice/playing/model/index.d.ts.map +1 -0
  277. package/dist/typescript/module/features/voice/recording/components/VoiceRecord.d.ts +10 -0
  278. package/dist/typescript/module/features/voice/recording/components/VoiceRecord.d.ts.map +1 -0
  279. package/dist/typescript/module/features/voice/recording/components/VoiceRecordButton.d.ts +10 -0
  280. package/dist/typescript/module/features/voice/recording/components/VoiceRecordButton.d.ts.map +1 -0
  281. package/dist/typescript/module/features/voice/recording/components/VoiceRecordDeleteButton.d.ts +8 -0
  282. package/dist/typescript/module/features/voice/recording/components/VoiceRecordDeleteButton.d.ts.map +1 -0
  283. package/dist/typescript/module/features/voice/recording/components/VoiceRecordDevider.d.ts +3 -0
  284. package/dist/typescript/module/features/voice/recording/components/VoiceRecordDevider.d.ts.map +1 -0
  285. package/dist/typescript/module/features/voice/recording/components/VoiceRecordTimer.d.ts +11 -0
  286. package/dist/typescript/module/features/voice/recording/components/VoiceRecordTimer.d.ts.map +1 -0
  287. package/dist/typescript/module/features/voice/recording/hooks/useVoiceRecorder.d.ts +5 -0
  288. package/dist/typescript/module/features/voice/recording/hooks/useVoiceRecorder.d.ts.map +1 -0
  289. package/dist/typescript/module/features/voice/recording/hooks/useVoiceRecorderAnimation.d.ts +6 -0
  290. package/dist/typescript/module/features/voice/recording/hooks/useVoiceRecorderAnimation.d.ts.map +1 -0
  291. package/dist/typescript/module/features/voice/recording/modal/ModalFooterButtons.d.ts +12 -0
  292. package/dist/typescript/module/features/voice/recording/modal/ModalFooterButtons.d.ts.map +1 -0
  293. package/dist/typescript/module/features/voice/recording/modal/VoiceRecordDeleteModal.d.ts +11 -0
  294. package/dist/typescript/module/features/voice/recording/modal/VoiceRecordDeleteModal.d.ts.map +1 -0
  295. package/dist/typescript/module/features/voice/recording/modal/VoiceRecordUndoModal.d.ts +11 -0
  296. package/dist/typescript/module/features/voice/recording/modal/VoiceRecordUndoModal.d.ts.map +1 -0
  297. package/dist/typescript/module/features/voice/recording/model/Recorder.model.d.ts +19 -0
  298. package/dist/typescript/module/features/voice/recording/model/Recorder.model.d.ts.map +1 -0
  299. package/dist/typescript/module/features/voice/recording/model/Uploader.model.d.ts +18 -0
  300. package/dist/typescript/module/features/voice/recording/model/Uploader.model.d.ts.map +1 -0
  301. package/dist/typescript/module/features/voice/recording/model/VoiceRecord.model.d.ts +50 -0
  302. package/dist/typescript/module/features/voice/recording/model/VoiceRecord.model.d.ts.map +1 -0
  303. package/dist/typescript/module/features/voice/recording/model/VoiceRecordCollection.d.ts +10 -0
  304. package/dist/typescript/module/features/voice/recording/model/VoiceRecordCollection.d.ts.map +1 -0
  305. package/dist/typescript/module/features/voice/recording/model/index.d.ts +5 -0
  306. package/dist/typescript/module/features/voice/recording/model/index.d.ts.map +1 -0
  307. package/dist/typescript/module/features/voice/types.d.ts +58 -0
  308. package/dist/typescript/module/features/voice/types.d.ts.map +1 -0
  309. package/dist/typescript/module/index.d.ts +1 -0
  310. package/dist/typescript/module/index.d.ts.map +1 -1
  311. package/dist/typescript/module/shared/components/PlayButton.d.ts +18 -0
  312. package/dist/typescript/module/shared/components/PlayButton.d.ts.map +1 -0
  313. package/dist/typescript/module/shared/translation/constants.d.ts +102 -0
  314. package/dist/typescript/module/shared/translation/constants.d.ts.map +1 -1
  315. package/dist/typescript/module/shared/translation/model.d.ts +34 -0
  316. package/dist/typescript/module/shared/translation/model.d.ts.map +1 -1
  317. package/dist/typescript/module/types/common.types.d.ts +3 -0
  318. package/dist/typescript/module/types/common.types.d.ts.map +1 -1
  319. package/package.json +11 -4
  320. package/src/features/chatbot/components/ChatInput/RequestHintButton.tsx +0 -1
  321. package/src/features/chatbot/components/ChatMessage/MessageButtonsBlock.tsx +1 -1
  322. package/src/features/chatbot/components/ChatMessage/MessageTextToSpeech.tsx +9 -51
  323. package/src/features/chatbot/constants.ts +4 -4
  324. package/src/features/gifCelebrations/helpers.ts +1 -0
  325. package/src/features/voice/constants.ts +14 -0
  326. package/src/features/voice/helpers.ts +254 -0
  327. package/src/features/voice/index.ts +17 -0
  328. package/src/features/voice/model/index.ts +13 -0
  329. package/src/features/voice/playing/components/VoiceIcon.tsx +38 -0
  330. package/src/features/voice/playing/components/VoiceTranscriptionsDropdown.tsx +239 -0
  331. package/src/features/voice/playing/components/VoiceTranscriptionsDropdownItem.tsx +127 -0
  332. package/src/features/voice/playing/hooks/useVoiceTranscriptionsDropdown.ts +54 -0
  333. package/src/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.ts +40 -0
  334. package/src/features/voice/playing/model/Player.model.ts +92 -0
  335. package/src/features/voice/playing/model/VoicePlayer.model.ts +282 -0
  336. package/src/features/voice/playing/model/VoiceTranscriptionsCollection.ts +53 -0
  337. package/src/features/voice/playing/model/VoiceTranscriptionsDownloader.model.ts +43 -0
  338. package/src/features/voice/playing/model/VoiceTranscriptionsDropdown.model.ts +12 -0
  339. package/src/features/voice/playing/model/index.ts +6 -0
  340. package/src/features/voice/recording/components/VoiceRecord.tsx +65 -0
  341. package/src/features/voice/recording/components/VoiceRecordButton.tsx +63 -0
  342. package/src/features/voice/recording/components/VoiceRecordDeleteButton.tsx +25 -0
  343. package/src/features/voice/recording/components/VoiceRecordDevider.tsx +17 -0
  344. package/src/features/voice/recording/components/VoiceRecordTimer.tsx +49 -0
  345. package/src/features/voice/recording/hooks/useVoiceRecorder.ts +62 -0
  346. package/src/features/voice/recording/hooks/useVoiceRecorderAnimation.ts +19 -0
  347. package/src/features/voice/recording/modal/ModalFooterButtons.tsx +54 -0
  348. package/src/features/voice/recording/modal/VoiceRecordDeleteModal.tsx +82 -0
  349. package/src/features/voice/recording/modal/VoiceRecordUndoModal.tsx +50 -0
  350. package/src/features/voice/recording/model/Recorder.model.ts +62 -0
  351. package/src/features/voice/recording/model/Uploader.model.ts +48 -0
  352. package/src/features/voice/recording/model/VoiceRecord.model.ts +278 -0
  353. package/src/features/voice/recording/model/VoiceRecordCollection.ts +26 -0
  354. package/src/features/voice/recording/model/index.ts +4 -0
  355. package/src/features/voice/types.ts +69 -0
  356. package/src/index.ts +1 -0
  357. package/src/shared/components/PlayButton.tsx +84 -0
  358. package/src/shared/translation/localization/ca.json +17 -0
  359. package/src/shared/translation/localization/de.json +17 -0
  360. package/src/shared/translation/localization/en.json +17 -0
  361. package/src/shared/translation/localization/gb.json +17 -0
  362. package/src/shared/translation/localization/sct.json +17 -0
  363. package/src/shared/translation/localization/sw.json +17 -0
  364. package/src/types/common.types.ts +1 -0
  365. package/dist/commonjs/features/chatbot/TTS_HIGHLIGHTING.md +0 -120
  366. package/dist/module/features/chatbot/TTS_HIGHLIGHTING.md +0 -120
  367. package/src/features/chatbot/TTS_HIGHLIGHTING.md +0 -120
@@ -0,0 +1,282 @@
1
+ import { attach, createEffect, createEvent, restore } from 'effector'
2
+ import {
3
+ VoiceTranscriptionsCollection,
4
+ VoiceTranscriptionItem,
5
+ } from './VoiceTranscriptionsCollection'
6
+ import { VoiceTranscriptionsDownloaderModel } from './VoiceTranscriptionsDownloader.model'
7
+ import { VoiceTranscriptionsDropdownModel } from './VoiceTranscriptionsDropdown.model'
8
+ import { AnswerAudio, VoicePlayerApi } from '../../types'
9
+ import { PlayerModel, PlayerState } from './Player.model'
10
+ import {
11
+ NO_AUDIO_BE_MESSAGE,
12
+ TRANSCRIPT_MAX_RETRIES,
13
+ TRANSCRIPT_RETRY_INTERVAL_MS,
14
+ } from '../../constants'
15
+
16
+ type VoiceTranscriptionsPlayerModelParams = {
17
+ api: VoicePlayerApi
18
+ audios?: AnswerAudio[]
19
+ }
20
+ type TogglePlayPauseParams = {
21
+ attemptNumber: number
22
+ playerState: PlayerState
23
+ }
24
+
25
+ export { PlayerState }
26
+
27
+ export class VoicePlayerModel {
28
+ public readonly collection = new VoiceTranscriptionsCollection()
29
+ public readonly downloader = new VoiceTranscriptionsDownloaderModel()
30
+ public readonly dropdown = new VoiceTranscriptionsDropdownModel()
31
+ public readonly playerModel = new PlayerModel()
32
+ public readonly api: VoicePlayerApi
33
+ private currentPlayingAttempt: number | null = null
34
+
35
+ public readonly setCurrentAttempt = createEvent<number | null>()
36
+ public readonly setTranscriptsLoaded = createEvent<boolean>()
37
+ public readonly reset = createEvent()
38
+
39
+ public readonly $currentAttempt = restore(this.setCurrentAttempt, null).reset(this.reset)
40
+ public readonly $transcriptsLoaded = restore(this.setTranscriptsLoaded, false).reset(this.reset)
41
+ public readonly $playerState = this.playerModel.$playerState
42
+
43
+ public readonly pauseAudio = this.playerModel.pause
44
+
45
+ public readonly stopAudio = createEffect(() => {
46
+ this.playerModel.stop()
47
+ this.currentPlayingAttempt = null
48
+ this.setCurrentAttempt(null)
49
+ })
50
+
51
+ constructor(params: VoiceTranscriptionsPlayerModelParams) {
52
+ this.api = params.api
53
+ this.downloader.setApi(params.api)
54
+
55
+ this.playerModel.playbackFinished.watch(() => {
56
+ this.currentPlayingAttempt = null
57
+ this.setCurrentAttempt(null)
58
+ })
59
+
60
+ if (params.audios) {
61
+ this.initializeCollectionFromAudios(params.audios)
62
+ }
63
+ }
64
+
65
+ public initializeWithAudios(answerAudio: AnswerAudio[]) {
66
+ this.setTranscriptsLoaded(false)
67
+ this.collection.clear()
68
+ this.initializeCollectionFromAudios(answerAudio)
69
+ }
70
+
71
+ private initializeCollectionFromAudios(audios: AnswerAudio[]) {
72
+ if (!audios) {
73
+ console.warn('VoicePlayerModel: audios is not provided')
74
+ return
75
+ }
76
+ audios.forEach((audio, index) => {
77
+ const attemptNumber = index + 1
78
+ if (!audio.audioFileId) {
79
+ console.warn('VoicePlayerModel: audioFileId is missing for audio', audio)
80
+ return
81
+ }
82
+ this.collection.add(attemptNumber, {
83
+ attemptNumber,
84
+ audioFileId: audio.audioFileId,
85
+ _id: audio._id,
86
+ hasDrawing: !!audio.drawing,
87
+ })
88
+ })
89
+ }
90
+
91
+ private async fetchTranscriptWithRetry(audioFileId: string): Promise<string> {
92
+ for (let attempt = 0; attempt < TRANSCRIPT_MAX_RETRIES; attempt++) {
93
+ const response = await this.api.getAudioFileTranscript(audioFileId)
94
+
95
+ if (response.status === 'completed') {
96
+ return response.text
97
+ }
98
+
99
+ if (response.status === 'failed') {
100
+ return NO_AUDIO_BE_MESSAGE
101
+ }
102
+
103
+ if (attempt < TRANSCRIPT_MAX_RETRIES - 1) {
104
+ await new Promise((resolve) => setTimeout(resolve, TRANSCRIPT_RETRY_INTERVAL_MS))
105
+ }
106
+ }
107
+ return NO_AUDIO_BE_MESSAGE
108
+ }
109
+
110
+ public readonly loadAllTranscripts = createEffect(async () => {
111
+ const attempts = this.collection.getAll()
112
+
113
+ const transcriptPromises = attempts.map(async (item) => {
114
+ if (this.collection.hasTranscript(item.attemptNumber)) {
115
+ return
116
+ }
117
+
118
+ this.collection.update(item.attemptNumber, { transcriptLoading: true })
119
+
120
+ try {
121
+ const text = await this.fetchTranscriptWithRetry(item.audioFileId)
122
+
123
+ this.collection.update(item.attemptNumber, {
124
+ transcript: text,
125
+ transcriptLoading: false,
126
+ transcriptError: undefined,
127
+ })
128
+ } catch (error) {
129
+ this.collection.update(item.attemptNumber, {
130
+ transcriptLoading: false,
131
+ transcriptError: error instanceof Error ? error.message : 'Failed to load transcript',
132
+ })
133
+ }
134
+ })
135
+
136
+ await Promise.all(transcriptPromises)
137
+ this.setTranscriptsLoaded(true)
138
+ })
139
+
140
+ private readonly handlePlayAttempt = createEffect(async (attemptNumber: number) => {
141
+ const item = this.collection.get(attemptNumber)
142
+ if (!item) return
143
+
144
+ if (this.currentPlayingAttempt !== null && this.currentPlayingAttempt !== attemptNumber) {
145
+ const previousItem = this.collection.get(this.currentPlayingAttempt)
146
+ if (previousItem?.controller && previousItem?.audioLoading) {
147
+ this.downloader.abortDownload(previousItem.controller)
148
+ this.collection.update(this.currentPlayingAttempt, {
149
+ audioLoading: false,
150
+ controller: undefined,
151
+ audioDownloadPromise: undefined,
152
+ })
153
+ }
154
+ }
155
+
156
+ if (this.collection.hasAudio(attemptNumber)) {
157
+ this.playAudioFx(attemptNumber)
158
+ return
159
+ }
160
+
161
+ try {
162
+ this.playerModel.setPlayerState(PlayerState.LOADING)
163
+ const { collectionItem, downloadPromise } = await this.downloader.download({
164
+ audioFileId: item.audioFileId,
165
+ attemptNumber,
166
+ })
167
+
168
+ this.collection.update(attemptNumber, collectionItem)
169
+
170
+ const result = await downloadPromise
171
+
172
+ this.collection.update(attemptNumber, {
173
+ audioUri: result.uri,
174
+ audioLoading: false,
175
+ audioError: undefined,
176
+ controller: undefined,
177
+ audioDownloadPromise: undefined,
178
+ })
179
+
180
+ this.playerModel.setPlayerState(PlayerState.IDLE)
181
+ this.playAudioFx(attemptNumber)
182
+ } catch (error) {
183
+ console.error('Audio download error:', error)
184
+ if (error instanceof Error && error.name === 'AbortError') {
185
+ return
186
+ }
187
+
188
+ this.collection.update(attemptNumber, {
189
+ audioLoading: false,
190
+ audioError: error instanceof Error ? error.message : 'Failed to download audio',
191
+ controller: undefined,
192
+ audioDownloadPromise: undefined,
193
+ })
194
+ }
195
+ })
196
+
197
+ private playAudioFx = createEffect(async (attemptNumber: number) => {
198
+ const item = this.collection.get(attemptNumber)
199
+ if (!item?.audioUri) {
200
+ console.warn('No audio URI available for attempt:', attemptNumber)
201
+ return
202
+ }
203
+
204
+ try {
205
+ if (this.currentPlayingAttempt !== null && this.currentPlayingAttempt !== attemptNumber) {
206
+ this.playerModel.player?.remove()
207
+ }
208
+
209
+ if (this.currentPlayingAttempt === attemptNumber && this.playerModel.player?.playing) {
210
+ this.playerModel.pause()
211
+ return
212
+ }
213
+
214
+ if (this.currentPlayingAttempt === attemptNumber && !this.playerModel.player?.playing) {
215
+ this.playerModel.resume()
216
+ return
217
+ }
218
+
219
+ this.currentPlayingAttempt = attemptNumber
220
+ this.setCurrentAttempt(attemptNumber)
221
+ await this.playerModel.play(item.audioUri)
222
+ } catch (error) {
223
+ console.error('Error playing audio:', error)
224
+ this.playerModel.setPlayerState(PlayerState.IDLE)
225
+
226
+ this.collection.update(attemptNumber, {
227
+ audioError: error instanceof Error ? error.message : 'Failed to play audio',
228
+ })
229
+ }
230
+ })
231
+
232
+ public getTranscript(attemptNumber: number): string | undefined {
233
+ return this.collection.get(attemptNumber)?.transcript
234
+ }
235
+
236
+ public isTranscriptLoading(attemptNumber: number): boolean {
237
+ return this.collection.get(attemptNumber)?.transcriptLoading || false
238
+ }
239
+
240
+ public isAudioLoading(attemptNumber: number): boolean {
241
+ return this.collection.get(attemptNumber)?.audioLoading || false
242
+ }
243
+
244
+ public getAllAttempts(): VoiceTranscriptionItem[] {
245
+ return this.collection.getAll()
246
+ }
247
+
248
+ public reinitializePlayer() {
249
+ this.playerModel.reinitialize()
250
+ }
251
+
252
+ public cleanup() {
253
+ this.collection.getAll().forEach((item) => {
254
+ if (item.audioUri && item.audioUri.startsWith('blob:')) {
255
+ try {
256
+ URL.revokeObjectURL(item.audioUri)
257
+ } catch (e) {}
258
+ }
259
+ if (item.controller) {
260
+ this.downloader.abortDownload(item.controller)
261
+ }
262
+ })
263
+
264
+ this.collection.clear()
265
+ this.currentPlayingAttempt = null
266
+ this.reset()
267
+ this.dropdown.reset()
268
+ this.playerModel.release()
269
+ }
270
+
271
+ public togglePlayPause = attach({
272
+ source: this.playerModel.$playerState,
273
+ mapParams: (attemptNumber: number, playerState) => ({ attemptNumber, playerState }),
274
+ effect: createEffect(({ attemptNumber, playerState }: TogglePlayPauseParams) => {
275
+ if (this.currentPlayingAttempt === attemptNumber && playerState === PlayerState.PLAYING) {
276
+ this.pauseAudio()
277
+ } else {
278
+ this.handlePlayAttempt(attemptNumber)
279
+ }
280
+ }),
281
+ })
282
+ }
@@ -0,0 +1,53 @@
1
+ export type VoiceTranscriptionItem = {
2
+ attemptNumber: number
3
+ audioFileId: string
4
+ _id: string
5
+ transcript?: string
6
+ transcriptLoading?: boolean
7
+ transcriptError?: string
8
+ audioUri?: string
9
+ audioLoading?: boolean
10
+ audioError?: string
11
+ audioDownloadPromise?: Promise<any>
12
+ controller?: AbortController
13
+ hasDrawing?: boolean
14
+ }
15
+
16
+ export class VoiceTranscriptionsCollection {
17
+ public readonly collection: Map<number, VoiceTranscriptionItem> = new Map()
18
+
19
+ public add(attemptNumber: number, item: VoiceTranscriptionItem) {
20
+ this.collection.set(attemptNumber, item)
21
+ }
22
+
23
+ public update(attemptNumber: number, item: Partial<VoiceTranscriptionItem>) {
24
+ const existingItem = this.collection.get(attemptNumber) || ({} as VoiceTranscriptionItem)
25
+ this.collection.set(attemptNumber, { ...existingItem, ...item })
26
+ }
27
+
28
+ public delete(attemptNumber: number) {
29
+ this.collection.delete(attemptNumber)
30
+ }
31
+
32
+ public get(attemptNumber: number) {
33
+ return this.collection.get(attemptNumber)
34
+ }
35
+
36
+ public getAll() {
37
+ return Array.from(this.collection.values())
38
+ }
39
+
40
+ public clear() {
41
+ this.collection.clear()
42
+ }
43
+
44
+ public hasTranscript(attemptNumber: number) {
45
+ const item = this.collection.get(attemptNumber)
46
+ return !!item?.transcript
47
+ }
48
+
49
+ public hasAudio(attemptNumber: number) {
50
+ const item = this.collection.get(attemptNumber)
51
+ return !!item?.audioUri
52
+ }
53
+ }
@@ -0,0 +1,43 @@
1
+ import { createEffect } from 'effector'
2
+ import { VoiceTranscriptionItem } from './VoiceTranscriptionsCollection'
3
+ import { VoicePlayerApi } from '../../types'
4
+ import { downloadAudioFileRequestGenerator } from '../../helpers'
5
+
6
+ type DownloadParams = {
7
+ audioFileId: string
8
+ attemptNumber: number
9
+ }
10
+
11
+ type DownloadResult = {
12
+ collectionItem: Partial<VoiceTranscriptionItem>
13
+ downloadPromise: Promise<any>
14
+ }
15
+
16
+ export class VoiceTranscriptionsDownloaderModel {
17
+ private api!: VoicePlayerApi
18
+
19
+ public readonly setApi = (api: VoicePlayerApi) => {
20
+ this.api = api
21
+ }
22
+
23
+ public readonly download = createEffect(
24
+ async ({ audioFileId, attemptNumber }: DownloadParams): Promise<DownloadResult> => {
25
+ const { downloadAudio, controller } = downloadAudioFileRequestGenerator(audioFileId, this.api)
26
+ const downloadPromise = downloadAudio()
27
+
28
+ const collectionItem: Partial<VoiceTranscriptionItem> = {
29
+ attemptNumber,
30
+ audioFileId,
31
+ audioLoading: true,
32
+ audioDownloadPromise: downloadPromise,
33
+ controller,
34
+ }
35
+
36
+ return { collectionItem, downloadPromise }
37
+ },
38
+ )
39
+
40
+ public readonly abortDownload = (controller?: AbortController) => {
41
+ controller?.abort()
42
+ }
43
+ }
@@ -0,0 +1,12 @@
1
+ import { createEvent, createStore } from 'effector'
2
+
3
+ export class VoiceTranscriptionsDropdownModel {
4
+ public readonly toggleExpand = createEvent()
5
+ public readonly setExpanded = createEvent<boolean>()
6
+ public readonly reset = createEvent()
7
+
8
+ public readonly $isExpanded = createStore(false)
9
+ .on(this.setExpanded, (_, value) => value)
10
+ .on(this.toggleExpand, (state) => !state)
11
+ .reset(this.reset)
12
+ }
@@ -0,0 +1,6 @@
1
+ export { VoicePlayerModel } from './VoicePlayer.model'
2
+ export { PlayerModel, PlayerState } from './Player.model'
3
+ export { VoiceTranscriptionsCollection } from './VoiceTranscriptionsCollection'
4
+ export type { VoiceTranscriptionItem } from './VoiceTranscriptionsCollection'
5
+ export { VoiceTranscriptionsDownloaderModel } from './VoiceTranscriptionsDownloader.model'
6
+ export { VoiceTranscriptionsDropdownModel } from './VoiceTranscriptionsDropdown.model'
@@ -0,0 +1,65 @@
1
+ import { StyleProp, ViewStyle, StyleSheet } from 'react-native'
2
+ import { VoiceRecordButton } from './VoiceRecordButton'
3
+ import React, { useEffect } from 'react'
4
+ import { COLORS, SPACING } from '@magmamath/react-native-ui'
5
+ import { VoiceRecordDeleteButton } from './VoiceRecordDeleteButton'
6
+ import { VoiceRecordDevider } from './VoiceRecordDevider'
7
+ import { VoiceRecordTimer } from './VoiceRecordTimer'
8
+ import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'
9
+ import { VoiceRecordModel } from '../model/VoiceRecord.model'
10
+ import { useVoiceRecorder } from '../hooks/useVoiceRecorder'
11
+ import { useVoiceRecorderAnimation } from '../hooks/useVoiceRecorderAnimation'
12
+ import { useUnit } from 'effector-react'
13
+
14
+ type VoiceRecordProps = {
15
+ style?: StyleProp<ViewStyle>
16
+ model: VoiceRecordModel
17
+ }
18
+
19
+ export const VoiceRecord = ({ style, model }: VoiceRecordProps) => {
20
+ const { recorderState } = useVoiceRecorder(model)
21
+ const [isExpanded, isDisabled] = useUnit([model.$isExpanded, model.$isButtonDisabled])
22
+
23
+ const { containerAnimatedStyle } = useVoiceRecorderAnimation(isExpanded)
24
+
25
+ useEffect(() => {
26
+ return () => {
27
+ model.cleanup()
28
+ }
29
+ }, [model])
30
+
31
+ return (
32
+ <Animated.View style={[styles.container, style, containerAnimatedStyle]}>
33
+ <VoiceRecordButton model={model} />
34
+ {isExpanded && (
35
+ <Animated.View entering={FadeIn} exiting={FadeOut} style={styles.expendedContainer}>
36
+ <VoiceRecordTimer
37
+ style={styles.timer}
38
+ model={model}
39
+ recordingFileDurationMs={recorderState.durationMillis}
40
+ />
41
+ <VoiceRecordDevider />
42
+ <VoiceRecordDeleteButton isDisabled={isDisabled} onPress={model.events.deleteRecord} />
43
+ </Animated.View>
44
+ )}
45
+ </Animated.View>
46
+ )
47
+ }
48
+
49
+ const styles = StyleSheet.create({
50
+ container: {
51
+ alignItems: 'center',
52
+ padding: 3,
53
+ paddingBottom: 6,
54
+ backgroundColor: COLORS.NEUTRAL_3,
55
+ borderRadius: 32,
56
+ boxShadow: '0 1px 3px 0 rgba(51, 51, 51, 0.10), 0 0 1px 0 rgba(51, 51, 51, 0.40)',
57
+ overflow: 'hidden',
58
+ },
59
+ timer: {
60
+ marginTop: SPACING[400],
61
+ },
62
+ expendedContainer: {
63
+ alignItems: 'center',
64
+ },
65
+ })
@@ -0,0 +1,63 @@
1
+ import React, { useMemo } from 'react'
2
+ import {
3
+ Button,
4
+ ButtonColor,
5
+ ButtonSize,
6
+ ButtonStyle,
7
+ ButtonVariant,
8
+ IS_WEB,
9
+ } from '@magmamath/react-native-ui'
10
+ import { Pressable } from 'react-native'
11
+ import { MicrophoneIcon, CheckIcon } from '@magmamath/react-native-ui'
12
+ import { VoiceRecordModel } from '../model/VoiceRecord.model'
13
+ import { useUnit } from 'effector-react'
14
+ import { VoiceRecorderState } from '../../constants'
15
+ import { useText } from '../../../../shared/translation'
16
+
17
+ type VoiceRecordButtonProps = {
18
+ style?: ButtonStyle
19
+ model: VoiceRecordModel
20
+ }
21
+
22
+ export const VoiceRecordButton = ({ style, model }: VoiceRecordButtonProps) => {
23
+ const t = useText()
24
+
25
+ const availableInputs = useUnit(model.recorderModel.$availableInputs)
26
+ const voiceRecordState = useUnit(model.recorderModel.$voiceRecordState)
27
+ const isButtonDisabled = useUnit(model.$isButtonDisabled)
28
+
29
+ const combinedStyles = useMemo((): ButtonStyle => {
30
+ return {
31
+ button: [{ width: 50 }, style?.button],
32
+ container: [style?.container],
33
+ text: [style?.text],
34
+ }
35
+ }, [style])
36
+
37
+ return (
38
+ <Pressable
39
+ onPressIn={(e) => {
40
+ if (availableInputs.length) return
41
+ e.preventDefault()
42
+ model.notification.warning(t('voice.noMicrophoneFound'))
43
+ }}
44
+ pointerEvents={!availableInputs.length ? 'box-only' : 'auto'}
45
+ >
46
+ <Button
47
+ disabled={isButtonDisabled || !availableInputs.length}
48
+ onPressIn={() => model.recorderButtonHandler()}
49
+ style={combinedStyles}
50
+ size={ButtonSize.LARGE}
51
+ variant={ButtonVariant.SECONDARY}
52
+ colorScheme={ButtonColor.WHITE}
53
+ icon={
54
+ voiceRecordState === VoiceRecorderState.RECORDING ? (
55
+ <CheckIcon size={25} />
56
+ ) : (
57
+ <MicrophoneIcon style={IS_WEB ? { width: 21 } : { minWidth: 23, minHeight: 23 }} />
58
+ )
59
+ }
60
+ />
61
+ </Pressable>
62
+ )
63
+ }
@@ -0,0 +1,25 @@
1
+ import { StyleSheet, TouchableOpacity } from 'react-native'
2
+ import { COLORS, TrashcanIcon } from '@magmamath/react-native-ui'
3
+ import React from 'react'
4
+
5
+ type VoiceRecordDeleteProps = {
6
+ onPress?: () => void
7
+ isDisabled?: boolean
8
+ }
9
+
10
+ export const VoiceRecordDeleteButton = ({ onPress, isDisabled }: VoiceRecordDeleteProps) => {
11
+ return (
12
+ <TouchableOpacity style={styles.container} onPress={onPress} disabled={isDisabled}>
13
+ <TrashcanIcon size={20} color={isDisabled ? COLORS.NEUTRAL_5 : COLORS.NEUTRAL_9} />
14
+ </TouchableOpacity>
15
+ )
16
+ }
17
+
18
+ const styles = StyleSheet.create({
19
+ container: {
20
+ width: 36,
21
+ height: 36,
22
+ alignItems: 'center',
23
+ justifyContent: 'center',
24
+ },
25
+ })
@@ -0,0 +1,17 @@
1
+ import { StyleSheet, View } from 'react-native'
2
+ import React from 'react'
3
+ import { COLORS } from '@magmamath/react-native-ui'
4
+
5
+ export const VoiceRecordDevider = () => {
6
+ return <View style={styles.container} />
7
+ }
8
+
9
+ const styles = StyleSheet.create({
10
+ container: {
11
+ height: 1,
12
+ borderRadius: 1,
13
+ width: 28,
14
+ margin: 8,
15
+ backgroundColor: COLORS.NEUTRAL_6,
16
+ },
17
+ })
@@ -0,0 +1,49 @@
1
+ import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'
2
+ import { COLORS, Typography } from '@magmamath/react-native-ui'
3
+ import React from 'react'
4
+ import { VoiceRecordModel } from '../model/VoiceRecord.model'
5
+ import { useUnit } from 'effector-react'
6
+ import { formatDuration } from '../../helpers'
7
+
8
+ type VoiceRecordTimerProps = {
9
+ style?: StyleProp<ViewStyle>
10
+ model: VoiceRecordModel
11
+ recordingFileDurationMs?: number
12
+ }
13
+
14
+ export const VoiceRecordTimer = ({
15
+ style,
16
+ model,
17
+ recordingFileDurationMs,
18
+ }: VoiceRecordTimerProps) => {
19
+ const savedAudioFileDurationMs = useUnit(model.$currentRecord)?.durationMs || 0
20
+ const displayDurationMs = recordingFileDurationMs || savedAudioFileDurationMs
21
+
22
+ return (
23
+ <View style={[styles.container, style]}>
24
+ <Typography variant="h9" style={styles.text}>
25
+ {displayDurationMs ? formatDuration(displayDurationMs) : '0:00'}
26
+ </Typography>
27
+ </View>
28
+ )
29
+ }
30
+
31
+ const styles = StyleSheet.create({
32
+ container: {
33
+ backgroundColor: COLORS.NEUTRAL_1,
34
+ paddingHorizontal: 8,
35
+ paddingVertical: 2,
36
+ alignItems: 'center',
37
+ borderWidth: 0.5,
38
+ borderColor: COLORS.NEUTRAL_4,
39
+ borderRadius: 18,
40
+ minWidth: 45,
41
+ },
42
+ text: {
43
+ color: COLORS.NEUTRAL_10,
44
+ fontSize: 13,
45
+ fontWeight: '600',
46
+ lineHeight: 18,
47
+ textTransform: 'uppercase',
48
+ },
49
+ })
@@ -0,0 +1,62 @@
1
+ import { RecordingPresets, useAudioRecorder, useAudioRecorderState } from 'expo-audio'
2
+ import { VoiceRecordModel } from '../model/VoiceRecord.model'
3
+ import { useEffect } from 'react'
4
+ import { IS_WEB } from '@magmamath/react-native-ui'
5
+ import { VOICE_RECORDER_MAX_DURATION_MS, VoiceRecorderState } from '../../constants'
6
+ import { addAudioInputsListenerWeb, getAvailableInputs } from '../../helpers'
7
+
8
+ export const useVoiceRecorder = (model: VoiceRecordModel) => {
9
+ const recorder = useAudioRecorder(RecordingPresets.LOW_QUALITY, model.recordingStatusUpdate)
10
+ const recorderState = useAudioRecorderState(recorder)
11
+
12
+ useEffect(() => {
13
+ if (
14
+ recorderState.durationMillis >= VOICE_RECORDER_MAX_DURATION_MS &&
15
+ recorderState.isRecording
16
+ ) {
17
+ model.recorderModel.stop()
18
+ }
19
+ // Track the last known duration while recording (for old browsers where getAudioDurationMs returns Infinity)
20
+ if (recorderState.isRecording && recorderState.durationMillis > 0) {
21
+ model.recorderModel.setLastKnownDurationMs(recorderState.durationMillis)
22
+ }
23
+ }, [recorderState.durationMillis])
24
+
25
+ useEffect(() => {
26
+ if (!IS_WEB) {
27
+ //only once for iPad and listener for WEB
28
+ getAvailableInputs(recorder).then((inputs) => {
29
+ model.recorderModel.setAvailableInputs(inputs)
30
+ })
31
+ return
32
+ }
33
+
34
+ const removeListener = addAudioInputsListenerWeb((devices) => {
35
+ model.recorderModel.setAvailableInputs(devices)
36
+ })
37
+
38
+ return removeListener
39
+ }, [recorder, model.recorderModel])
40
+
41
+ useEffect(() => {
42
+ if (recorderState.isRecording) {
43
+ model.recorderModel.setVoiceRecordState(VoiceRecorderState.RECORDING)
44
+ return
45
+ }
46
+ if (recorderState.durationMillis > 0) {
47
+ model.recorderModel.setVoiceRecordState(VoiceRecorderState.PAUSED)
48
+ return
49
+ }
50
+ model.recorderModel.setVoiceRecordState(VoiceRecorderState.IDLE)
51
+
52
+ return () => {
53
+ model.recorderModel.reset()
54
+ }
55
+ }, [recorderState, model.recorderModel])
56
+
57
+ useEffect(() => {
58
+ model.recorderModel.recorder = recorder
59
+ }, [recorder])
60
+
61
+ return { recorderState }
62
+ }