stream-chat-react-native-core 9.3.1-beta.6 → 9.3.1-beta.8

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 (329) hide show
  1. package/lib/commonjs/a11y/hooks/useAnnounceOnShow.js +27 -0
  2. package/lib/commonjs/a11y/hooks/useAnnounceOnShow.js.map +1 -0
  3. package/lib/commonjs/a11y/index.js +11 -0
  4. package/lib/commonjs/a11y/index.js.map +1 -1
  5. package/lib/commonjs/components/Accessibility/CompositeAccessibilityProbe.js +29 -0
  6. package/lib/commonjs/components/Accessibility/CompositeAccessibilityProbe.js.map +1 -0
  7. package/lib/commonjs/components/Accessibility/HiddenA11yText.js +30 -0
  8. package/lib/commonjs/components/Accessibility/HiddenA11yText.js.map +1 -0
  9. package/lib/commonjs/components/Accessibility/OverlayA11yShield.js +37 -0
  10. package/lib/commonjs/components/Accessibility/OverlayA11yShield.js.map +1 -0
  11. package/lib/commonjs/components/Accessibility/index.js +22 -0
  12. package/lib/commonjs/components/Accessibility/index.js.map +1 -1
  13. package/lib/commonjs/components/Attachment/Gallery.js +10 -0
  14. package/lib/commonjs/components/Attachment/Gallery.js.map +1 -1
  15. package/lib/commonjs/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js +31 -29
  16. package/lib/commonjs/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js.map +1 -1
  17. package/lib/commonjs/components/ChannelPreview/ChannelPreviewMutedStatus.js +11 -5
  18. package/lib/commonjs/components/ChannelPreview/ChannelPreviewMutedStatus.js.map +1 -1
  19. package/lib/commonjs/components/ChannelPreview/ChannelPreviewStatus.js +18 -2
  20. package/lib/commonjs/components/ChannelPreview/ChannelPreviewStatus.js.map +1 -1
  21. package/lib/commonjs/components/ChannelPreview/ChannelPreviewUnreadCount.js +8 -2
  22. package/lib/commonjs/components/ChannelPreview/ChannelPreviewUnreadCount.js.map +1 -1
  23. package/lib/commonjs/components/ChannelPreview/ChannelPreviewView.js +3 -0
  24. package/lib/commonjs/components/ChannelPreview/ChannelPreviewView.js.map +1 -1
  25. package/lib/commonjs/components/ImageGallery/ImageGallery.js +56 -1
  26. package/lib/commonjs/components/ImageGallery/ImageGallery.js.map +1 -1
  27. package/lib/commonjs/components/Message/MessageItemView/Headers/MessageReminderHeader.js +2 -0
  28. package/lib/commonjs/components/Message/MessageItemView/Headers/MessageReminderHeader.js.map +1 -1
  29. package/lib/commonjs/components/Message/MessageItemView/Headers/SentToChannelHeader.js +2 -0
  30. package/lib/commonjs/components/Message/MessageItemView/Headers/SentToChannelHeader.js.map +1 -1
  31. package/lib/commonjs/components/Message/MessageItemView/MessageContent.js +26 -4
  32. package/lib/commonjs/components/Message/MessageItemView/MessageContent.js.map +1 -1
  33. package/lib/commonjs/components/Message/MessageItemView/MessageFooter.js +2 -0
  34. package/lib/commonjs/components/Message/MessageItemView/MessageFooter.js.map +1 -1
  35. package/lib/commonjs/components/Message/MessageItemView/MessageReplies.js +7 -3
  36. package/lib/commonjs/components/Message/MessageItemView/MessageReplies.js.map +1 -1
  37. package/lib/commonjs/components/Message/MessageItemView/MessageRepliesAvatars.js +2 -0
  38. package/lib/commonjs/components/Message/MessageItemView/MessageRepliesAvatars.js.map +1 -1
  39. package/lib/commonjs/components/Message/MessageItemView/MessageStatus.js +35 -30
  40. package/lib/commonjs/components/Message/MessageItemView/MessageStatus.js.map +1 -1
  41. package/lib/commonjs/components/Message/MessageItemView/MessageTextContainer.js +16 -6
  42. package/lib/commonjs/components/Message/MessageItemView/MessageTextContainer.js.map +1 -1
  43. package/lib/commonjs/components/Message/MessageItemView/ReactionList/ReactionListClustered.js +19 -6
  44. package/lib/commonjs/components/Message/MessageItemView/ReactionList/ReactionListClustered.js.map +1 -1
  45. package/lib/commonjs/components/Message/MessageItemView/ReactionList/ReactionListItem.js +10 -1
  46. package/lib/commonjs/components/Message/MessageItemView/ReactionList/ReactionListItem.js.map +1 -1
  47. package/lib/commonjs/components/Message/hooks/useCreateMessageContext.js +6 -1
  48. package/lib/commonjs/components/Message/hooks/useCreateMessageContext.js.map +1 -1
  49. package/lib/commonjs/components/Message/hooks/useMessageActionHandlers.js +8 -0
  50. package/lib/commonjs/components/Message/hooks/useMessageActionHandlers.js.map +1 -1
  51. package/lib/commonjs/components/MessageList/InlineDateSeparator.js +8 -1
  52. package/lib/commonjs/components/MessageList/InlineDateSeparator.js.map +1 -1
  53. package/lib/commonjs/components/MessageMenu/ReactionButton.js +0 -7
  54. package/lib/commonjs/components/MessageMenu/ReactionButton.js.map +1 -1
  55. package/lib/commonjs/components/Poll/Poll.js +2 -17
  56. package/lib/commonjs/components/Poll/Poll.js.map +1 -1
  57. package/lib/commonjs/components/Poll/components/PollOption.js +6 -1
  58. package/lib/commonjs/components/Poll/components/PollOption.js.map +1 -1
  59. package/lib/commonjs/components/Reply/Reply.js +43 -6
  60. package/lib/commonjs/components/Reply/Reply.js.map +1 -1
  61. package/lib/commonjs/components/ui/Avatar/ChannelAvatar.js +17 -11
  62. package/lib/commonjs/components/ui/Avatar/ChannelAvatar.js.map +1 -1
  63. package/lib/commonjs/components/ui/Badge/BadgeNotification.js +3 -1
  64. package/lib/commonjs/components/ui/Badge/BadgeNotification.js.map +1 -1
  65. package/lib/commonjs/contexts/accessibilityContext/AccessibilityContext.js +6 -22
  66. package/lib/commonjs/contexts/accessibilityContext/AccessibilityContext.js.map +1 -1
  67. package/lib/commonjs/contexts/messageContext/MessageContext.js.map +1 -1
  68. package/lib/commonjs/contexts/overlayContext/MessageOverlayHostLayer.js +1 -0
  69. package/lib/commonjs/contexts/overlayContext/MessageOverlayHostLayer.js.map +1 -1
  70. package/lib/commonjs/contexts/overlayContext/OverlayProvider.js +4 -1
  71. package/lib/commonjs/contexts/overlayContext/OverlayProvider.js.map +1 -1
  72. package/lib/commonjs/i18n/ar.json +26 -8
  73. package/lib/commonjs/i18n/en.json +23 -5
  74. package/lib/commonjs/i18n/es.json +23 -5
  75. package/lib/commonjs/i18n/fr.json +23 -5
  76. package/lib/commonjs/i18n/he.json +23 -5
  77. package/lib/commonjs/i18n/hi.json +24 -6
  78. package/lib/commonjs/i18n/it.json +23 -5
  79. package/lib/commonjs/i18n/ja.json +23 -5
  80. package/lib/commonjs/i18n/ko.json +23 -5
  81. package/lib/commonjs/i18n/nl.json +24 -6
  82. package/lib/commonjs/i18n/pt-br.json +25 -7
  83. package/lib/commonjs/i18n/ru.json +23 -5
  84. package/lib/commonjs/i18n/tr.json +24 -6
  85. package/lib/commonjs/state-store/image-gallery-state-store.js +13 -0
  86. package/lib/commonjs/state-store/image-gallery-state-store.js.map +1 -1
  87. package/lib/commonjs/utils/i18n/getDateString.js +26 -1
  88. package/lib/commonjs/utils/i18n/getDateString.js.map +1 -1
  89. package/lib/commonjs/version.json +1 -1
  90. package/lib/module/a11y/hooks/useAnnounceOnShow.js +27 -0
  91. package/lib/module/a11y/hooks/useAnnounceOnShow.js.map +1 -0
  92. package/lib/module/a11y/index.js +11 -0
  93. package/lib/module/a11y/index.js.map +1 -1
  94. package/lib/module/components/Accessibility/CompositeAccessibilityProbe.js +29 -0
  95. package/lib/module/components/Accessibility/CompositeAccessibilityProbe.js.map +1 -0
  96. package/lib/module/components/Accessibility/HiddenA11yText.js +30 -0
  97. package/lib/module/components/Accessibility/HiddenA11yText.js.map +1 -0
  98. package/lib/module/components/Accessibility/OverlayA11yShield.js +37 -0
  99. package/lib/module/components/Accessibility/OverlayA11yShield.js.map +1 -0
  100. package/lib/module/components/Accessibility/index.js +22 -0
  101. package/lib/module/components/Accessibility/index.js.map +1 -1
  102. package/lib/module/components/Attachment/Gallery.js +10 -0
  103. package/lib/module/components/Attachment/Gallery.js.map +1 -1
  104. package/lib/module/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js +31 -29
  105. package/lib/module/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js.map +1 -1
  106. package/lib/module/components/ChannelPreview/ChannelPreviewMutedStatus.js +11 -5
  107. package/lib/module/components/ChannelPreview/ChannelPreviewMutedStatus.js.map +1 -1
  108. package/lib/module/components/ChannelPreview/ChannelPreviewStatus.js +18 -2
  109. package/lib/module/components/ChannelPreview/ChannelPreviewStatus.js.map +1 -1
  110. package/lib/module/components/ChannelPreview/ChannelPreviewUnreadCount.js +8 -2
  111. package/lib/module/components/ChannelPreview/ChannelPreviewUnreadCount.js.map +1 -1
  112. package/lib/module/components/ChannelPreview/ChannelPreviewView.js +3 -0
  113. package/lib/module/components/ChannelPreview/ChannelPreviewView.js.map +1 -1
  114. package/lib/module/components/ImageGallery/ImageGallery.js +56 -1
  115. package/lib/module/components/ImageGallery/ImageGallery.js.map +1 -1
  116. package/lib/module/components/Message/MessageItemView/Headers/MessageReminderHeader.js +2 -0
  117. package/lib/module/components/Message/MessageItemView/Headers/MessageReminderHeader.js.map +1 -1
  118. package/lib/module/components/Message/MessageItemView/Headers/SentToChannelHeader.js +2 -0
  119. package/lib/module/components/Message/MessageItemView/Headers/SentToChannelHeader.js.map +1 -1
  120. package/lib/module/components/Message/MessageItemView/MessageContent.js +26 -4
  121. package/lib/module/components/Message/MessageItemView/MessageContent.js.map +1 -1
  122. package/lib/module/components/Message/MessageItemView/MessageFooter.js +2 -0
  123. package/lib/module/components/Message/MessageItemView/MessageFooter.js.map +1 -1
  124. package/lib/module/components/Message/MessageItemView/MessageReplies.js +7 -3
  125. package/lib/module/components/Message/MessageItemView/MessageReplies.js.map +1 -1
  126. package/lib/module/components/Message/MessageItemView/MessageRepliesAvatars.js +2 -0
  127. package/lib/module/components/Message/MessageItemView/MessageRepliesAvatars.js.map +1 -1
  128. package/lib/module/components/Message/MessageItemView/MessageStatus.js +35 -30
  129. package/lib/module/components/Message/MessageItemView/MessageStatus.js.map +1 -1
  130. package/lib/module/components/Message/MessageItemView/MessageTextContainer.js +16 -6
  131. package/lib/module/components/Message/MessageItemView/MessageTextContainer.js.map +1 -1
  132. package/lib/module/components/Message/MessageItemView/ReactionList/ReactionListClustered.js +19 -6
  133. package/lib/module/components/Message/MessageItemView/ReactionList/ReactionListClustered.js.map +1 -1
  134. package/lib/module/components/Message/MessageItemView/ReactionList/ReactionListItem.js +10 -1
  135. package/lib/module/components/Message/MessageItemView/ReactionList/ReactionListItem.js.map +1 -1
  136. package/lib/module/components/Message/hooks/useCreateMessageContext.js +6 -1
  137. package/lib/module/components/Message/hooks/useCreateMessageContext.js.map +1 -1
  138. package/lib/module/components/Message/hooks/useMessageActionHandlers.js +8 -0
  139. package/lib/module/components/Message/hooks/useMessageActionHandlers.js.map +1 -1
  140. package/lib/module/components/MessageList/InlineDateSeparator.js +8 -1
  141. package/lib/module/components/MessageList/InlineDateSeparator.js.map +1 -1
  142. package/lib/module/components/MessageMenu/ReactionButton.js +0 -7
  143. package/lib/module/components/MessageMenu/ReactionButton.js.map +1 -1
  144. package/lib/module/components/Poll/Poll.js +2 -17
  145. package/lib/module/components/Poll/Poll.js.map +1 -1
  146. package/lib/module/components/Poll/components/PollOption.js +6 -1
  147. package/lib/module/components/Poll/components/PollOption.js.map +1 -1
  148. package/lib/module/components/Reply/Reply.js +43 -6
  149. package/lib/module/components/Reply/Reply.js.map +1 -1
  150. package/lib/module/components/ui/Avatar/ChannelAvatar.js +17 -11
  151. package/lib/module/components/ui/Avatar/ChannelAvatar.js.map +1 -1
  152. package/lib/module/components/ui/Badge/BadgeNotification.js +3 -1
  153. package/lib/module/components/ui/Badge/BadgeNotification.js.map +1 -1
  154. package/lib/module/contexts/accessibilityContext/AccessibilityContext.js +6 -22
  155. package/lib/module/contexts/accessibilityContext/AccessibilityContext.js.map +1 -1
  156. package/lib/module/contexts/messageContext/MessageContext.js.map +1 -1
  157. package/lib/module/contexts/overlayContext/MessageOverlayHostLayer.js +1 -0
  158. package/lib/module/contexts/overlayContext/MessageOverlayHostLayer.js.map +1 -1
  159. package/lib/module/contexts/overlayContext/OverlayProvider.js +4 -1
  160. package/lib/module/contexts/overlayContext/OverlayProvider.js.map +1 -1
  161. package/lib/module/i18n/ar.json +26 -8
  162. package/lib/module/i18n/en.json +23 -5
  163. package/lib/module/i18n/es.json +23 -5
  164. package/lib/module/i18n/fr.json +23 -5
  165. package/lib/module/i18n/he.json +23 -5
  166. package/lib/module/i18n/hi.json +24 -6
  167. package/lib/module/i18n/it.json +23 -5
  168. package/lib/module/i18n/ja.json +23 -5
  169. package/lib/module/i18n/ko.json +23 -5
  170. package/lib/module/i18n/nl.json +24 -6
  171. package/lib/module/i18n/pt-br.json +25 -7
  172. package/lib/module/i18n/ru.json +23 -5
  173. package/lib/module/i18n/tr.json +24 -6
  174. package/lib/module/state-store/image-gallery-state-store.js +13 -0
  175. package/lib/module/state-store/image-gallery-state-store.js.map +1 -1
  176. package/lib/module/utils/i18n/getDateString.js +26 -1
  177. package/lib/module/utils/i18n/getDateString.js.map +1 -1
  178. package/lib/module/version.json +1 -1
  179. package/lib/typescript/a11y/hooks/useAnnounceOnShow.d.ts +21 -0
  180. package/lib/typescript/a11y/hooks/useAnnounceOnShow.d.ts.map +1 -0
  181. package/lib/typescript/a11y/index.d.ts +1 -0
  182. package/lib/typescript/a11y/index.d.ts.map +1 -1
  183. package/lib/typescript/components/Accessibility/CompositeAccessibilityProbe.d.ts +30 -0
  184. package/lib/typescript/components/Accessibility/CompositeAccessibilityProbe.d.ts.map +1 -0
  185. package/lib/typescript/components/Accessibility/HiddenA11yText.d.ts +25 -0
  186. package/lib/typescript/components/Accessibility/HiddenA11yText.d.ts.map +1 -0
  187. package/lib/typescript/components/Accessibility/OverlayA11yShield.d.ts +20 -0
  188. package/lib/typescript/components/Accessibility/OverlayA11yShield.d.ts.map +1 -0
  189. package/lib/typescript/components/Accessibility/index.d.ts +2 -0
  190. package/lib/typescript/components/Accessibility/index.d.ts.map +1 -1
  191. package/lib/typescript/components/Attachment/Gallery.d.ts.map +1 -1
  192. package/lib/typescript/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.d.ts.map +1 -1
  193. package/lib/typescript/components/ChannelPreview/ChannelPreviewMutedStatus.d.ts.map +1 -1
  194. package/lib/typescript/components/ChannelPreview/ChannelPreviewStatus.d.ts.map +1 -1
  195. package/lib/typescript/components/ChannelPreview/ChannelPreviewUnreadCount.d.ts.map +1 -1
  196. package/lib/typescript/components/ChannelPreview/ChannelPreviewView.d.ts.map +1 -1
  197. package/lib/typescript/components/ImageGallery/ImageGallery.d.ts.map +1 -1
  198. package/lib/typescript/components/Message/MessageItemView/Headers/MessageReminderHeader.d.ts.map +1 -1
  199. package/lib/typescript/components/Message/MessageItemView/Headers/SentToChannelHeader.d.ts.map +1 -1
  200. package/lib/typescript/components/Message/MessageItemView/MessageContent.d.ts +1 -1
  201. package/lib/typescript/components/Message/MessageItemView/MessageContent.d.ts.map +1 -1
  202. package/lib/typescript/components/Message/MessageItemView/MessageFooter.d.ts.map +1 -1
  203. package/lib/typescript/components/Message/MessageItemView/MessageReplies.d.ts.map +1 -1
  204. package/lib/typescript/components/Message/MessageItemView/MessageRepliesAvatars.d.ts.map +1 -1
  205. package/lib/typescript/components/Message/MessageItemView/MessageStatus.d.ts.map +1 -1
  206. package/lib/typescript/components/Message/MessageItemView/MessageTextContainer.d.ts +1 -1
  207. package/lib/typescript/components/Message/MessageItemView/MessageTextContainer.d.ts.map +1 -1
  208. package/lib/typescript/components/Message/MessageItemView/ReactionList/ReactionListClustered.d.ts.map +1 -1
  209. package/lib/typescript/components/Message/MessageItemView/ReactionList/ReactionListItem.d.ts.map +1 -1
  210. package/lib/typescript/components/Message/MessageOverlayWrapper.d.ts +1 -1
  211. package/lib/typescript/components/Message/hooks/useCreateMessageContext.d.ts +1 -1
  212. package/lib/typescript/components/Message/hooks/useCreateMessageContext.d.ts.map +1 -1
  213. package/lib/typescript/components/Message/hooks/useMessageActionHandlers.d.ts.map +1 -1
  214. package/lib/typescript/components/MessageList/InlineDateSeparator.d.ts.map +1 -1
  215. package/lib/typescript/components/MessageMenu/ReactionButton.d.ts.map +1 -1
  216. package/lib/typescript/components/Poll/Poll.d.ts.map +1 -1
  217. package/lib/typescript/components/Poll/components/PollOption.d.ts.map +1 -1
  218. package/lib/typescript/components/Reply/Reply.d.ts.map +1 -1
  219. package/lib/typescript/components/ui/Avatar/ChannelAvatar.d.ts.map +1 -1
  220. package/lib/typescript/components/ui/Badge/BadgeNotification.d.ts +5 -0
  221. package/lib/typescript/components/ui/Badge/BadgeNotification.d.ts.map +1 -1
  222. package/lib/typescript/contexts/accessibilityContext/AccessibilityContext.d.ts +32 -2
  223. package/lib/typescript/contexts/accessibilityContext/AccessibilityContext.d.ts.map +1 -1
  224. package/lib/typescript/contexts/messageContext/MessageContext.d.ts +8 -0
  225. package/lib/typescript/contexts/messageContext/MessageContext.d.ts.map +1 -1
  226. package/lib/typescript/contexts/overlayContext/MessageOverlayHostLayer.d.ts.map +1 -1
  227. package/lib/typescript/contexts/overlayContext/OverlayProvider.d.ts.map +1 -1
  228. package/lib/typescript/hooks/useTranslatedMessage.d.ts +1 -1
  229. package/lib/typescript/i18n/ar.json +26 -8
  230. package/lib/typescript/i18n/en.json +23 -5
  231. package/lib/typescript/i18n/es.json +23 -5
  232. package/lib/typescript/i18n/fr.json +23 -5
  233. package/lib/typescript/i18n/he.json +23 -5
  234. package/lib/typescript/i18n/hi.json +24 -6
  235. package/lib/typescript/i18n/it.json +23 -5
  236. package/lib/typescript/i18n/ja.json +23 -5
  237. package/lib/typescript/i18n/ko.json +23 -5
  238. package/lib/typescript/i18n/nl.json +24 -6
  239. package/lib/typescript/i18n/pt-br.json +25 -7
  240. package/lib/typescript/i18n/ru.json +23 -5
  241. package/lib/typescript/i18n/tr.json +24 -6
  242. package/lib/typescript/state-store/image-gallery-state-store.d.ts +5 -1
  243. package/lib/typescript/state-store/image-gallery-state-store.d.ts.map +1 -1
  244. package/lib/typescript/utils/i18n/Streami18n.d.ts +22 -4
  245. package/lib/typescript/utils/i18n/Streami18n.d.ts.map +1 -1
  246. package/lib/typescript/utils/i18n/getDateString.d.ts +28 -0
  247. package/lib/typescript/utils/i18n/getDateString.d.ts.map +1 -1
  248. package/package.json +1 -1
  249. package/src/a11y/hooks/useAnnounceOnShow.ts +44 -0
  250. package/src/a11y/index.ts +1 -0
  251. package/src/components/Accessibility/CompositeAccessibilityProbe.tsx +48 -0
  252. package/src/components/Accessibility/HiddenA11yText.tsx +49 -0
  253. package/src/components/Accessibility/OverlayA11yShield.tsx +49 -0
  254. package/src/components/Accessibility/__tests__/OverlayA11yShield.test.tsx +83 -0
  255. package/src/components/Accessibility/index.ts +2 -0
  256. package/src/components/Attachment/Gallery.tsx +14 -2
  257. package/src/components/Channel/__tests__/ownCapabilities.test.tsx +25 -2
  258. package/src/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.tsx +19 -16
  259. package/src/components/ChannelPreview/ChannelPreviewMutedStatus.tsx +8 -1
  260. package/src/components/ChannelPreview/ChannelPreviewStatus.tsx +26 -6
  261. package/src/components/ChannelPreview/ChannelPreviewUnreadCount.tsx +5 -1
  262. package/src/components/ChannelPreview/ChannelPreviewView.tsx +3 -0
  263. package/src/components/ImageGallery/ImageGallery.tsx +82 -4
  264. package/src/components/ImageGallery/__tests__/ImageGallery.test.tsx +13 -3
  265. package/src/components/ImageGallery/__tests__/ImageGalleryAdjustable.test.tsx +141 -0
  266. package/src/components/Message/MessageItemView/Headers/MessageReminderHeader.tsx +7 -1
  267. package/src/components/Message/MessageItemView/Headers/SentToChannelHeader.tsx +7 -1
  268. package/src/components/Message/MessageItemView/MessageContent.tsx +34 -4
  269. package/src/components/Message/MessageItemView/MessageFooter.tsx +6 -1
  270. package/src/components/Message/MessageItemView/MessageReplies.tsx +9 -7
  271. package/src/components/Message/MessageItemView/MessageRepliesAvatars.tsx +5 -1
  272. package/src/components/Message/MessageItemView/MessageStatus.tsx +36 -39
  273. package/src/components/Message/MessageItemView/MessageTextContainer.tsx +27 -4
  274. package/src/components/Message/MessageItemView/ReactionList/ReactionListClustered.tsx +24 -10
  275. package/src/components/Message/MessageItemView/ReactionList/ReactionListItem.tsx +7 -1
  276. package/src/components/Message/MessageItemView/__tests__/Message.test.tsx +10 -3
  277. package/src/components/Message/MessageItemView/__tests__/MessageReplies.test.tsx +10 -2
  278. package/src/components/Message/MessageItemView/__tests__/MessageStatus.test.tsx +10 -7
  279. package/src/components/Message/MessageItemView/__tests__/ReactionListBottom.test.tsx +1 -1
  280. package/src/components/Message/hooks/useCreateMessageContext.ts +12 -1
  281. package/src/components/Message/hooks/useMessageActionHandlers.ts +7 -0
  282. package/src/components/MessageInput/__tests__/__snapshots__/AttachButton.test.tsx.snap +3 -0
  283. package/src/components/MessageInput/__tests__/__snapshots__/SendButton.test.tsx.snap +2 -0
  284. package/src/components/MessageList/InlineDateSeparator.tsx +10 -3
  285. package/src/components/MessageMenu/ReactionButton.tsx +0 -7
  286. package/src/components/MessageMenu/__tests__/ReactionButton.test.tsx +1 -14
  287. package/src/components/Poll/Poll.tsx +2 -26
  288. package/src/components/Poll/components/PollOption.tsx +7 -1
  289. package/src/components/Reply/Reply.tsx +58 -7
  290. package/src/components/Thread/__tests__/__snapshots__/Thread.test.tsx.snap +9 -0
  291. package/src/components/ui/Avatar/ChannelAvatar.tsx +39 -29
  292. package/src/components/ui/Badge/BadgeNotification.tsx +11 -2
  293. package/src/contexts/accessibilityContext/AccessibilityContext.tsx +60 -36
  294. package/src/contexts/messageContext/MessageContext.tsx +8 -0
  295. package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx +5 -1
  296. package/src/contexts/overlayContext/OverlayProvider.tsx +2 -1
  297. package/src/i18n/ar.json +26 -8
  298. package/src/i18n/en.json +23 -5
  299. package/src/i18n/es.json +23 -5
  300. package/src/i18n/fr.json +23 -5
  301. package/src/i18n/he.json +23 -5
  302. package/src/i18n/hi.json +24 -6
  303. package/src/i18n/it.json +23 -5
  304. package/src/i18n/ja.json +23 -5
  305. package/src/i18n/ko.json +23 -5
  306. package/src/i18n/nl.json +24 -6
  307. package/src/i18n/pt-br.json +25 -7
  308. package/src/i18n/ru.json +23 -5
  309. package/src/i18n/tr.json +24 -6
  310. package/src/state-store/__tests__/image-gallery-state-store.test.ts +1 -0
  311. package/src/state-store/image-gallery-state-store.ts +13 -1
  312. package/src/utils/i18n/getDateString.ts +57 -0
  313. package/src/version.json +1 -1
  314. package/lib/commonjs/components/Poll/hooks/usePollAccessibilityActions.js +0 -150
  315. package/lib/commonjs/components/Poll/hooks/usePollAccessibilityActions.js.map +0 -1
  316. package/lib/commonjs/components/Poll/hooks/usePollAccessibilityLabel.js +0 -59
  317. package/lib/commonjs/components/Poll/hooks/usePollAccessibilityLabel.js.map +0 -1
  318. package/lib/module/components/Poll/hooks/usePollAccessibilityActions.js +0 -150
  319. package/lib/module/components/Poll/hooks/usePollAccessibilityActions.js.map +0 -1
  320. package/lib/module/components/Poll/hooks/usePollAccessibilityLabel.js +0 -59
  321. package/lib/module/components/Poll/hooks/usePollAccessibilityLabel.js.map +0 -1
  322. package/lib/typescript/components/Poll/hooks/usePollAccessibilityActions.d.ts +0 -25
  323. package/lib/typescript/components/Poll/hooks/usePollAccessibilityActions.d.ts.map +0 -1
  324. package/lib/typescript/components/Poll/hooks/usePollAccessibilityLabel.d.ts +0 -8
  325. package/lib/typescript/components/Poll/hooks/usePollAccessibilityLabel.d.ts.map +0 -1
  326. package/src/components/Poll/hooks/__tests__/usePollAccessibilityActions.test.tsx +0 -358
  327. package/src/components/Poll/hooks/__tests__/usePollAccessibilityLabel.test.tsx +0 -142
  328. package/src/components/Poll/hooks/usePollAccessibilityActions.ts +0 -191
  329. package/src/components/Poll/hooks/usePollAccessibilityLabel.ts +0 -75
@@ -171,10 +171,17 @@ describe('Message', () => {
171
171
 
172
172
  fireEvent(getByTestId('custom-overlay-trigger'), 'longPress');
173
173
 
174
+ // Once the overlay opens, the host layer's `accessibilityViewIsModal`
175
+ // marks the chat-side subtree as hidden to RNTL. The overlay target's
176
+ // children are Portal-teleported but their React parent stays in the
177
+ // chat, so default visibility filtering excludes them. Pass
178
+ // `includeHiddenElements` to look past the modal-sibling heuristic.
174
179
  await waitFor(() => {
175
- expect(getByText('outside:normal')).toBeTruthy();
176
- expect(getByText('inside:overlay')).toBeTruthy();
177
- expect(getByTestId('custom-overlay-target-placeholder')).toBeTruthy();
180
+ expect(getByText('outside:normal', { includeHiddenElements: true })).toBeTruthy();
181
+ expect(getByText('inside:overlay', { includeHiddenElements: true })).toBeTruthy();
182
+ expect(
183
+ getByTestId('custom-overlay-target-placeholder', { includeHiddenElements: true }),
184
+ ).toBeTruthy();
178
185
  });
179
186
  });
180
187
  });
@@ -33,7 +33,11 @@ describe('MessageReplies', () => {
33
33
  );
34
34
 
35
35
  await waitFor(() => {
36
- expect(screen.getByTestId('message-replies')).toBeTruthy();
36
+ const pluralPressable = screen.getByTestId('message-replies');
37
+ expect(pluralPressable).toBeTruthy();
38
+ expect(pluralPressable.props.accessibilityRole).toBe('button');
39
+ expect(pluralPressable.props.accessibilityLabel).toBe('{{ replyCount }} Replies');
40
+ expect(pluralPressable.props.accessibilityHint).toBe('a11y/Double tap to view thread');
37
41
  expect(t).toHaveBeenCalledWith('{{ replyCount }} Replies', {
38
42
  replyCount: message.reply_count,
39
43
  });
@@ -57,7 +61,11 @@ describe('MessageReplies', () => {
57
61
 
58
62
  await waitFor(() => {
59
63
  expect(onPressMock).toHaveBeenCalled();
60
- expect(screen.getByTestId('message-replies')).toBeTruthy();
64
+ const singularPressable = screen.getByTestId('message-replies');
65
+ expect(singularPressable).toBeTruthy();
66
+ expect(singularPressable.props.accessibilityRole).toBe('button');
67
+ expect(singularPressable.props.accessibilityLabel).toBe('1 Reply');
68
+ expect(singularPressable.props.accessibilityHint).toBe('a11y/Double tap to view thread');
61
69
  expect(t).toHaveBeenCalledWith('1 Reply');
62
70
  expect(screen.getByText('1 Reply')).toBeTruthy();
63
71
  });
@@ -5,6 +5,7 @@ import type { Channel as ChannelType, StreamChat } from 'stream-chat';
5
5
 
6
6
  import { Channel } from '../../..';
7
7
  import { ChannelsStateProvider } from '../../../../contexts/channelsStateContext/ChannelsStateContext';
8
+ import { OverlayProvider } from '../../../../contexts/overlayContext/OverlayProvider';
8
9
  import { getOrCreateChannelApi } from '../../../../mock-builders/api/getOrCreateChannel';
9
10
  import { useMockedApis } from '../../../../mock-builders/api/useMockedApis';
10
11
  import { generateChannelResponse } from '../../../../mock-builders/generator/channel';
@@ -54,13 +55,15 @@ describe('MessageStatus', () => {
54
55
  channelProps?: Partial<React.ComponentProps<typeof Channel>>,
55
56
  ) =>
56
57
  render(
57
- <ChannelsStateProvider>
58
- <Chat client={chatClient}>
59
- <Channel channel={channel} {...channelProps}>
60
- <MessageStatus {...options} />
61
- </Channel>
62
- </Chat>
63
- </ChannelsStateProvider>,
58
+ <OverlayProvider accessibility={{ enabled: true }}>
59
+ <ChannelsStateProvider>
60
+ <Chat client={chatClient}>
61
+ <Channel channel={channel} {...channelProps}>
62
+ <MessageStatus {...options} />
63
+ </Channel>
64
+ </Chat>
65
+ </ChannelsStateProvider>
66
+ </OverlayProvider>,
64
67
  );
65
68
 
66
69
  // NOTE: Original source had `it.each('string', async () => { ... })` which was a
@@ -168,7 +168,7 @@ describe('ReactionListBottom', () => {
168
168
  { reactionListPosition: 'bottom', reactionListType: 'segmented' },
169
169
  );
170
170
 
171
- const reactionListBottomItem = screen.getByLabelText('Reaction List Item');
171
+ const reactionListBottomItem = screen.getByTestId('reaction-list-item');
172
172
 
173
173
  fireEvent(reactionListBottomItem, 'onPress');
174
174
 
@@ -1,5 +1,6 @@
1
1
  import { useMemo, useRef } from 'react';
2
2
 
3
+ import { useAccessibilityContext } from '../../../contexts/accessibilityContext/AccessibilityContext';
3
4
  import type { MessageContextValue } from '../../../contexts/messageContext/MessageContext';
4
5
 
5
6
  import { stringifyMessage } from '../../../utils/utils';
@@ -58,7 +59,7 @@ export const useCreateMessageContext = ({
58
59
  threadList,
59
60
  videos,
60
61
  setQuotedMessage,
61
- }: MessageContextValue) => {
62
+ }: Omit<MessageContextValue, 'hasInteractiveAccessibilityContent'>) => {
62
63
  const stableGroupStyles = useStableRefValue(groupStyles);
63
64
  const reactionsValue = reactions.map(({ count, own, type }) => `${own}${type}${count}`).join();
64
65
  const stringifiedMessage = stringifyMessage({ message });
@@ -70,6 +71,14 @@ export const useCreateMessageContext = ({
70
71
  ? stringifyMessage({ includeReactions: false, message: message.quoted_message })
71
72
  : '';
72
73
 
74
+ // Resolved here (not at each consumer) so the boolean lives on MessageContext
75
+ // and downstream components (MessageContent, MessageTextContainer) read it
76
+ // directly. The predicate's identity is stable in the default case; an
77
+ // integrator override is expected to be stable too (documented on the config).
78
+ const { hasInteractiveAccessibilityContent: hasInteractiveAccessibilityContentPredicate } =
79
+ useAccessibilityContext();
80
+ const hasInteractiveAccessibilityContent = hasInteractiveAccessibilityContentPredicate(message);
81
+
73
82
  const messageContext: MessageContextValue = useMemo(
74
83
  () => ({
75
84
  actionsEnabled,
@@ -85,6 +94,7 @@ export const useCreateMessageContext = ({
85
94
  hasAttachmentActions,
86
95
  handleReaction,
87
96
  handleToggleReaction,
97
+ hasInteractiveAccessibilityContent,
88
98
  hasReactions,
89
99
  messageHasOnlySingleAttachment,
90
100
  images,
@@ -123,6 +133,7 @@ export const useCreateMessageContext = ({
123
133
  goToMessage,
124
134
  stableGroupStyles,
125
135
  hasAttachmentActions,
136
+ hasInteractiveAccessibilityContent,
126
137
  hasReactions,
127
138
  messageHasOnlySingleAttachment,
128
139
  lastGroupMessage,
@@ -5,10 +5,12 @@ import { UserResponse } from 'stream-chat';
5
5
 
6
6
  import { useUserMuteActive } from './useUserMuteActive';
7
7
 
8
+ import { useScreenReaderEnabled } from '../../../a11y/hooks/useScreenReaderEnabled';
8
9
  import type { ChannelContextValue } from '../../../contexts/channelContext/ChannelContext';
9
10
  import type { ChatContextValue } from '../../../contexts/chatContext/ChatContext';
10
11
  import { MessageComposerAPIContextValue } from '../../../contexts/messageComposerContext/MessageComposerAPIContext';
11
12
  import type { MessageContextValue } from '../../../contexts/messageContext/MessageContext';
13
+ import { useMessageInputContext } from '../../../contexts/messageInputContext/MessageInputContext';
12
14
  import type { MessagesContextValue } from '../../../contexts/messagesContext/MessagesContext';
13
15
 
14
16
  import { useTranslationContext } from '../../../contexts/translationContext/TranslationContext';
@@ -67,6 +69,8 @@ export const useMessageActionHandlers = ({
67
69
  Pick<MessageComposerAPIContextValue, 'setEditingState' | 'setQuotedMessage'>) => {
68
70
  const { t } = useTranslationContext();
69
71
  const { addNotification } = useNotificationApi();
72
+ const { inputBoxRef } = useMessageInputContext();
73
+ const screenReaderEnabled = useScreenReaderEnabled();
70
74
  const handleResendMessage = useStableCallback(() => retrySendMessage(message));
71
75
  const translatedMessage = useTranslatedMessage(message);
72
76
 
@@ -74,6 +78,9 @@ export const useMessageActionHandlers = ({
74
78
 
75
79
  const handleQuotedReplyMessage = useStableCallback(() => {
76
80
  setQuotedMessage(message);
81
+ if (screenReaderEnabled) {
82
+ inputBoxRef.current?.focus();
83
+ }
77
84
  });
78
85
 
79
86
  const handleCopyMessage = useStableCallback(() => {
@@ -818,6 +818,7 @@ exports[`AttachButton should call handleAttachButtonPress when the button is cli
818
818
  </View>
819
819
  </View>
820
820
  <View
821
+ accessibilityViewIsModal={false}
821
822
  collapsable={false}
822
823
  pointerEvents="box-none"
823
824
  style={
@@ -1733,6 +1734,7 @@ exports[`AttachButton should render a enabled AttachButton 1`] = `
1733
1734
  </View>
1734
1735
  </View>
1735
1736
  <View
1737
+ accessibilityViewIsModal={false}
1736
1738
  collapsable={false}
1737
1739
  pointerEvents="box-none"
1738
1740
  style={
@@ -2648,6 +2650,7 @@ exports[`AttachButton should render an disabled AttachButton 1`] = `
2648
2650
  </View>
2649
2651
  </View>
2650
2652
  <View
2653
+ accessibilityViewIsModal={false}
2651
2654
  collapsable={false}
2652
2655
  pointerEvents="box-none"
2653
2656
  style={
@@ -816,6 +816,7 @@ exports[`SendButton should render a SendButton 1`] = `
816
816
  </View>
817
817
  </View>
818
818
  <View
819
+ accessibilityViewIsModal={false}
819
820
  collapsable={false}
820
821
  pointerEvents="box-none"
821
822
  style={
@@ -1729,6 +1730,7 @@ exports[`SendButton should render a disabled SendButton 1`] = `
1729
1730
  </View>
1730
1731
  </View>
1731
1732
  <View
1733
+ accessibilityViewIsModal={false}
1732
1734
  collapsable={false}
1733
1735
  pointerEvents="box-none"
1734
1736
  style={
@@ -4,7 +4,7 @@ import { StyleSheet, Text, View } from 'react-native';
4
4
  import { useTheme } from '../../contexts/themeContext/ThemeContext';
5
5
  import { useTranslationContext } from '../../contexts/translationContext/TranslationContext';
6
6
  import { primitives } from '../../theme';
7
- import { getDateString } from '../../utils/i18n/getDateString';
7
+ import { getDateString, getDateStringForA11y } from '../../utils/i18n/getDateString';
8
8
 
9
9
  /**
10
10
  * Props for the `InlineDateSeparator` component.
@@ -17,7 +17,7 @@ export type InlineDateSeparatorProps = {
17
17
  };
18
18
 
19
19
  export const InlineDateSeparator = ({ date }: InlineDateSeparatorProps) => {
20
- const { t, tDateTimeParser } = useTranslationContext();
20
+ const { t, tDateTimeParser, userLanguage } = useTranslationContext();
21
21
  const styles = useStyles();
22
22
 
23
23
  const dateString = useMemo(
@@ -31,9 +31,16 @@ export const InlineDateSeparator = ({ date }: InlineDateSeparatorProps) => {
31
31
  [date, t, tDateTimeParser],
32
32
  );
33
33
 
34
+ const a11yDateString = useMemo(
35
+ () => getDateStringForA11y({ date, tDateTimeParser, userLanguage }),
36
+ [date, tDateTimeParser, userLanguage],
37
+ );
38
+
34
39
  return (
35
40
  <View style={styles.container} testID='date-separator'>
36
- <Text style={styles.text}>{dateString}</Text>
41
+ <Text accessibilityLabel={a11yDateString} style={styles.text}>
42
+ {dateString}
43
+ </Text>
37
44
  </View>
38
45
  );
39
46
  };
@@ -49,17 +49,10 @@ export const ReactionButton = (props: ReactionButtonProps) => {
49
49
  () => <Icon size={reactionIconSize ?? 24} />,
50
50
  [Icon, reactionIconSize],
51
51
  );
52
- const selectedLabelState = selected ? 'selected' : 'unselected';
53
- const accessibilityLabelParams = useMemo(
54
- () => ({ selected: selectedLabelState, type }),
55
- [selectedLabelState, type],
56
- );
57
52
 
58
53
  return (
59
54
  <View style={styles.reactionButton}>
60
55
  <Button
61
- accessibilityLabelKey='a11y/reaction-button-{{type}}-{{selected}}'
62
- accessibilityLabelParams={accessibilityLabelParams}
63
56
  variant={'secondary'}
64
57
  type={'outline'}
65
58
  iconOnly={!count}
@@ -2,9 +2,8 @@ import React from 'react';
2
2
 
3
3
  import { Text } from 'react-native';
4
4
 
5
- import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react-native';
5
+ import { cleanup, fireEvent, render } from '@testing-library/react-native';
6
6
 
7
- import { OverlayProvider } from '../../../contexts/overlayContext/OverlayProvider';
8
7
  import { ThemeProvider } from '../../../contexts/themeContext/ThemeContext';
9
8
  import { defaultTheme } from '../../../contexts/themeContext/utils/theme';
10
9
  import { IconProps } from '../../../icons';
@@ -38,18 +37,6 @@ describe('ReactionButton', () => {
38
37
  expect(getByText('24')).toBeTruthy();
39
38
  });
40
39
 
41
- it('uses the released reaction button label when accessibility labels are translated', async () => {
42
- render(
43
- <OverlayProvider accessibility={{ enabled: true }}>
44
- <ReactionButton {...defaultProps} />
45
- </OverlayProvider>,
46
- );
47
-
48
- await waitFor(() => {
49
- expect(screen.getByLabelText('reaction-button-like-unselected')).toBeTruthy();
50
- });
51
- });
52
-
53
40
  it('should call onPress function with the correct reaction type when pressed', () => {
54
41
  const { getByRole } = render(
55
42
  <ThemeProvider theme={defaultTheme}>
@@ -6,18 +6,14 @@ import { PollOption as PollOptionClass } from 'stream-chat';
6
6
  import { PollOption, ShowAllOptionsButton } from './components';
7
7
  import { PollUIStateProvider } from './contexts/PollUIStateContext';
8
8
 
9
- import { usePollAccessibilityActions } from './hooks/usePollAccessibilityActions';
10
- import { usePollAccessibilityLabel } from './hooks/usePollAccessibilityLabel';
11
9
  import { usePollState } from './hooks/usePollState';
12
10
 
13
- import { useA11yLabel } from '../../a11y/hooks/useA11yLabel';
14
11
  import {
15
12
  PollContextProvider,
16
13
  PollContextValue,
17
14
  useTheme,
18
15
  useTranslationContext,
19
16
  } from '../../contexts';
20
- import { useAccessibilityContext } from '../../contexts/accessibilityContext/AccessibilityContext';
21
17
  import { useComponentsContext } from '../../contexts/componentsContext/ComponentsContext';
22
18
 
23
19
  import { primitives } from '../../theme';
@@ -54,7 +50,7 @@ export const PollHeader = () => {
54
50
  } = useTheme();
55
51
 
56
52
  return (
57
- <View style={styles.headerContainer}>
53
+ <View accessible accessibilityRole='text' style={styles.headerContainer}>
58
54
  <Text style={[styles.headerTitle, header.title]}>{name}</Text>
59
55
  <Text style={[styles.headerSubtitle, header.subtitle]}>{subtitle}</Text>
60
56
  </View>
@@ -66,10 +62,6 @@ export const PollContent = () => {
66
62
  const styles = useStyles();
67
63
  const { PollButtons: PollButtonsComponent, PollHeader: PollHeaderComponent } =
68
64
  useComponentsContext();
69
- const { enabled: a11yEnabled } = useAccessibilityContext();
70
- const accessibilityHint = useA11yLabel('a11y/Double tap and hold to activate contextual menu');
71
- const accessibilityLabel = usePollAccessibilityLabel();
72
- const { accessibilityActions, onAccessibilityAction } = usePollAccessibilityActions();
73
65
 
74
66
  const {
75
67
  theme: {
@@ -79,24 +71,8 @@ export const PollContent = () => {
79
71
  },
80
72
  } = useTheme();
81
73
 
82
- // NOTE: Android custom accessibilityActions are broken in RN < 0.83.2 —
83
- // see facebook/react-native#47268, fixed by PR #52724. On affected versions
84
- // the actions menu surfaces only a subset of the list and dispatch
85
- // announces "Action not supported". iOS works correctly on all versions.
86
- // Once the SDK's minimum RN reaches 0.83.2, wrap the descendants below in
87
- // <View importantForAccessibility='no-hide-descendants'> so Android
88
- // TalkBack groups them under the composite rather than exposing each
89
- // interactive child as a separate focus stop.
90
74
  return (
91
- <View
92
- accessibilityActions={accessibilityActions}
93
- accessibilityHint={accessibilityHint}
94
- accessibilityLabel={accessibilityLabel}
95
- accessibilityRole={a11yEnabled ? 'button' : undefined}
96
- accessible={a11yEnabled || undefined}
97
- onAccessibilityAction={onAccessibilityAction}
98
- style={[styles.container, container]}
99
- >
75
+ <View style={[styles.container, container]}>
100
76
  <PollHeaderComponent />
101
77
  <View style={[styles.optionsWrapper, optionsWrapper]}>
102
78
  {options?.slice(0, defaultPollOptionCount)?.map((option: PollOptionClass) => (
@@ -92,6 +92,7 @@ export const PollAllOptions = ({
92
92
 
93
93
  export const PollOption = ({ option, showProgressBar = true, forceIncoming }: PollOptionProps) => {
94
94
  const { latestVotesByOption, voteCountsByOption, voteCount } = usePollState();
95
+ const { t } = useTranslationContext();
95
96
  const styles = useStyles();
96
97
 
97
98
  const relevantVotes = useMemo(
@@ -141,7 +142,12 @@ export const PollOption = ({ option, showProgressBar = true, forceIncoming }: Po
141
142
  />
142
143
  ) : null}
143
144
 
144
- <Text style={[styles.votesText, votesText]}>{voteCountsByOption[option.id] || 0}</Text>
145
+ <Text
146
+ accessibilityLabel={t('{{count}} votes', { count: votes })}
147
+ style={[styles.votesText, votesText]}
148
+ >
149
+ {votes}
150
+ </Text>
145
151
  </View>
146
152
  </View>
147
153
  {showProgressBar ? (
@@ -20,6 +20,7 @@ import {
20
20
 
21
21
  import { ReplyMessageView } from './ReplyMessageView';
22
22
 
23
+ import { useAnnounceOnShow } from '../../a11y/hooks/useAnnounceOnShow';
23
24
  import { useChatContext } from '../../contexts/chatContext/ChatContext';
24
25
  import { useComponentsContext } from '../../contexts/componentsContext/ComponentsContext';
25
26
  import {
@@ -42,6 +43,8 @@ const messageComposerStateStoreSelector = (state: MessageComposerState) => ({
42
43
  quotedMessage: state.quotedMessage,
43
44
  });
44
45
 
46
+ const ANNOUNCEMENT_TEXT_MAX_LENGTH = 120;
47
+
45
48
  const RightContent = React.memo(
46
49
  (props: Pick<ReplyPropsWithContext, 'ImageComponent' | 'message'>) => {
47
50
  const { ImageComponent, message } = props;
@@ -234,6 +237,43 @@ export const MemoizedReply = React.memo(ReplyWithContext, areEqual) as typeof Re
234
237
  export type ReplyProps = Partial<ReplyPropsWithContext> &
235
238
  Pick<ReplyPropsWithContext, 'mode' | 'onDismiss'>;
236
239
 
240
+ /**
241
+ * Mounted only when the Reply is rendered as the composer header preview
242
+ * (edit/reply). Keeps the translation + announcer subscriptions off the
243
+ * per-row in-message quoted-reply render path.
244
+ */
245
+ const ReplyComposerAnnouncer = ({
246
+ message,
247
+ mode,
248
+ }: {
249
+ message: ReplyPropsWithContext['quotedMessage'];
250
+ mode: ReplyPropsWithContext['mode'];
251
+ }) => {
252
+ const { t } = useTranslationContext();
253
+ const truncatedText = useMemo(() => {
254
+ const raw = message?.text?.trim();
255
+ if (!raw) return undefined;
256
+ return raw.length > ANNOUNCEMENT_TEXT_MAX_LENGTH
257
+ ? `${raw.slice(0, ANNOUNCEMENT_TEXT_MAX_LENGTH).trimEnd()}…`
258
+ : raw;
259
+ }, [message?.text]);
260
+ const announcement = useMemo(() => {
261
+ if (mode === 'edit') {
262
+ return truncatedText
263
+ ? t('a11y/Editing message: {{text}}', { text: truncatedText })
264
+ : t('a11y/Editing message');
265
+ }
266
+ const name = message?.user?.name;
267
+ if (!name) return undefined;
268
+ return truncatedText
269
+ ? t('a11y/Replying to {{user}}: {{text}}', { text: truncatedText, user: name })
270
+ : t('a11y/Replying to {{user}}', { user: name });
271
+ }, [mode, message?.user?.name, truncatedText, t]);
272
+
273
+ useAnnounceOnShow(true, announcement);
274
+ return null;
275
+ };
276
+
237
277
  export const Reply = (props: ReplyProps) => {
238
278
  const { message: messageFromContext } = useMessageContext();
239
279
  const { client } = useChatContext();
@@ -251,14 +291,25 @@ export const Reply = (props: ReplyProps) => {
251
291
 
252
292
  const isMyMessage = client.user?.id === quotedMessage?.user?.id;
253
293
 
294
+ // Composer header passes `onDismiss`; the in-message quoted-reply renderer
295
+ // does not. Only the composer-preview path pays for announcement work.
296
+ const isComposerPreview = !!props.onDismiss;
297
+
254
298
  return (
255
- <MemoizedReply
256
- ImageComponent={ImageComponent}
257
- isMyMessage={isMyMessage}
258
- message={messageFromContext}
259
- quotedMessage={quotedMessage}
260
- {...props}
261
- />
299
+ <>
300
+ {isComposerPreview ? (
301
+ // Edit passes the message via `quotedMessage` prop; reply uses the
302
+ // composer-state quoted message we computed locally.
303
+ <ReplyComposerAnnouncer message={props.quotedMessage ?? quotedMessage} mode={props.mode} />
304
+ ) : null}
305
+ <MemoizedReply
306
+ ImageComponent={ImageComponent}
307
+ isMyMessage={isMyMessage}
308
+ message={messageFromContext}
309
+ quotedMessage={quotedMessage}
310
+ {...props}
311
+ />
312
+ </>
262
313
  );
263
314
  };
264
315
 
@@ -588,6 +588,8 @@ exports[`Thread should match thread snapshot 1`] = `
588
588
  </View>
589
589
  </View>
590
590
  <View
591
+ accessibilityRole="text"
592
+ accessible={true}
591
593
  style={
592
594
  [
593
595
  {
@@ -920,6 +922,8 @@ exports[`Thread should match thread snapshot 1`] = `
920
922
  </View>
921
923
  </View>
922
924
  <View
925
+ accessibilityRole="text"
926
+ accessible={true}
923
927
  style={
924
928
  [
925
929
  {
@@ -1013,6 +1017,7 @@ exports[`Thread should match thread snapshot 1`] = `
1013
1017
  testID="date-separator"
1014
1018
  >
1015
1019
  <Text
1020
+ accessibilityLabel="May 5, 2020"
1016
1021
  style={
1017
1022
  {
1018
1023
  "color": "#414552",
@@ -1285,6 +1290,8 @@ exports[`Thread should match thread snapshot 1`] = `
1285
1290
  </View>
1286
1291
  </View>
1287
1292
  <View
1293
+ accessibilityRole="text"
1294
+ accessible={true}
1288
1295
  style={
1289
1296
  [
1290
1297
  {
@@ -1611,6 +1618,8 @@ exports[`Thread should match thread snapshot 1`] = `
1611
1618
  </View>
1612
1619
  </View>
1613
1620
  <View
1621
+ accessibilityRole="text"
1622
+ accessible={true}
1614
1623
  style={
1615
1624
  [
1616
1625
  {
@@ -8,10 +8,12 @@ import { UserAvatarGroup } from './AvatarGroup';
8
8
 
9
9
  import { UserAvatar } from './UserAvatar';
10
10
 
11
+ import { useA11yLabel } from '../../../a11y/hooks/useA11yLabel';
11
12
  import { useChannelPreviewDisplayPresence } from '../../../components/ChannelPreview/hooks/useChannelPreviewDisplayPresence';
12
13
  import { useChatContext } from '../../../contexts/chatContext/ChatContext';
13
14
  import { useTheme } from '../../../contexts/themeContext/ThemeContext';
14
15
  import { hashStringToNumber } from '../../../utils/utils';
16
+ import { CompositeAccessibilityProbe } from '../../Accessibility/CompositeAccessibilityProbe';
15
17
 
16
18
  export type ChannelAvatarProps = {
17
19
  channel: Channel;
@@ -47,34 +49,42 @@ export const ChannelAvatar = (props: ChannelAvatarProps) => {
47
49
 
48
50
  const channelName = (channel.data?.name as string | undefined) ?? channel.cid;
49
51
 
50
- if (channelImage) {
51
- return (
52
- <Avatar
53
- backgroundColor={avatarBackgroundColor}
54
- imageUrl={channelImage}
55
- name={channelName}
56
- showBorder={showBorder}
57
- size={size}
58
- />
59
- );
60
- }
52
+ const memberCount = Object.keys(channel.state.members).length;
53
+ const isGroup = !!channel.data?.name || memberCount > 2;
54
+ const otherUserName = usersWithoutSelf[0]?.name || usersWithoutSelf[0]?.id;
55
+ const labelParams = useMemo(
56
+ () => ({ count: memberCount, name: otherUserName ?? '' }),
57
+ [memberCount, otherUserName],
58
+ );
59
+ const accessibilityLabel = useA11yLabel(
60
+ isGroup ? 'a11y/Channel with {{count}} members' : 'a11y/Direct chat with {{name}}',
61
+ labelParams,
62
+ );
61
63
 
62
- if (usersWithoutSelf.length > 1) {
63
- return (
64
- <UserAvatarGroup
65
- size={size}
66
- users={usersForGroup}
67
- showOnlineIndicator={showOnlineIndicator}
68
- />
69
- );
70
- } else {
71
- return (
72
- <UserAvatar
73
- user={usersWithoutSelf[0]}
74
- size={size}
75
- showBorder={showBorder}
76
- showOnlineIndicator={showOnlineIndicator}
77
- />
78
- );
79
- }
64
+ return (
65
+ <CompositeAccessibilityProbe label={accessibilityLabel}>
66
+ {channelImage ? (
67
+ <Avatar
68
+ backgroundColor={avatarBackgroundColor}
69
+ imageUrl={channelImage}
70
+ name={channelName}
71
+ showBorder={showBorder}
72
+ size={size}
73
+ />
74
+ ) : usersWithoutSelf.length > 1 ? (
75
+ <UserAvatarGroup
76
+ size={size}
77
+ users={usersForGroup}
78
+ showOnlineIndicator={showOnlineIndicator}
79
+ />
80
+ ) : (
81
+ <UserAvatar
82
+ user={usersWithoutSelf[0]}
83
+ size={size}
84
+ showBorder={showBorder}
85
+ showOnlineIndicator={showOnlineIndicator}
86
+ />
87
+ )}
88
+ </CompositeAccessibilityProbe>
89
+ );
80
90
  };
@@ -9,6 +9,11 @@ export type BadgeNotificationProps = {
9
9
  count: number;
10
10
  size: 'sm' | 'xs';
11
11
  testID?: string;
12
+ /**
13
+ * Optional accessibility label override. When provided, screen readers
14
+ * announce this string instead of the bare count.
15
+ */
16
+ accessibilityLabel?: string;
12
17
  };
13
18
 
14
19
  const sizes = {
@@ -34,7 +39,7 @@ const textStyles = {
34
39
  };
35
40
 
36
41
  export const BadgeNotification = (props: BadgeNotificationProps) => {
37
- const { type = 'primary', count, size = 'sm', testID } = props;
42
+ const { accessibilityLabel, type = 'primary', count, size = 'sm', testID } = props;
38
43
  const styles = useStyles();
39
44
  const {
40
45
  theme: { semantics },
@@ -49,7 +54,11 @@ export const BadgeNotification = (props: BadgeNotificationProps) => {
49
54
  return (
50
55
  <View style={styles.border}>
51
56
  <View style={[styles.container, { backgroundColor: colors[type] }, sizes[size]]}>
52
- <Text style={[styles.text, textStyles[size]]} testID={testID}>
57
+ <Text
58
+ accessibilityLabel={accessibilityLabel}
59
+ style={[styles.text, textStyles[size]]}
60
+ testID={testID}
61
+ >
53
62
  {count}
54
63
  </Text>
55
64
  </View>