@planningcenter/chat-react-native 4.4.0-rc.7 → 4.4.0

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 (339) hide show
  1. package/build/components/conversation/jump_to_bottom_button.d.ts.map +1 -1
  2. package/build/components/conversation/jump_to_bottom_button.js +42 -40
  3. package/build/components/conversation/jump_to_bottom_button.js.map +1 -1
  4. package/build/components/conversation/message.d.ts +2 -1
  5. package/build/components/conversation/message.d.ts.map +1 -1
  6. package/build/components/conversation/message.js +3 -1
  7. package/build/components/conversation/message.js.map +1 -1
  8. package/build/components/conversation/message_form.d.ts.map +1 -1
  9. package/build/components/conversation/message_form.js +3 -1
  10. package/build/components/conversation/message_form.js.map +1 -1
  11. package/build/components/conversation/message_list.d.ts.map +1 -1
  12. package/build/components/conversation/message_list.js +35 -31
  13. package/build/components/conversation/message_list.js.map +1 -1
  14. package/build/components/conversation/message_reaction.d.ts +2 -1
  15. package/build/components/conversation/message_reaction.d.ts.map +1 -1
  16. package/build/components/conversation/message_reaction.js +67 -60
  17. package/build/components/conversation/message_reaction.js.map +1 -1
  18. package/build/components/conversation/messages_disabled_banners.d.ts +7 -5
  19. package/build/components/conversation/messages_disabled_banners.d.ts.map +1 -1
  20. package/build/components/conversation/messages_disabled_banners.js +15 -14
  21. package/build/components/conversation/messages_disabled_banners.js.map +1 -1
  22. package/build/components/conversation/system_message.d.ts +2 -1
  23. package/build/components/conversation/system_message.d.ts.map +1 -1
  24. package/build/components/conversation/system_message.js +133 -100
  25. package/build/components/conversation/system_message.js.map +1 -1
  26. package/build/components/conversations/archived_results_footer.d.ts +6 -0
  27. package/build/components/conversations/archived_results_footer.d.ts.map +1 -0
  28. package/build/components/conversations/archived_results_footer.js +51 -0
  29. package/build/components/conversations/archived_results_footer.js.map +1 -0
  30. package/build/components/conversations/conversation_actions.d.ts.map +1 -1
  31. package/build/components/conversations/conversation_actions.js +241 -206
  32. package/build/components/conversations/conversation_actions.js.map +1 -1
  33. package/build/components/conversations/conversation_preview.d.ts.map +1 -1
  34. package/build/components/conversations/conversation_preview.js +102 -93
  35. package/build/components/conversations/conversation_preview.js.map +1 -1
  36. package/build/components/conversations/conversations.d.ts.map +1 -1
  37. package/build/components/conversations/conversations.js +66 -41
  38. package/build/components/conversations/conversations.js.map +1 -1
  39. package/build/components/conversations/conversations_blank_state.d.ts.map +1 -1
  40. package/build/components/conversations/conversations_blank_state.js +55 -12
  41. package/build/components/conversations/conversations_blank_state.js.map +1 -1
  42. package/build/components/conversations/filtered_results_footer.d.ts +2 -0
  43. package/build/components/conversations/filtered_results_footer.d.ts.map +1 -0
  44. package/build/components/conversations/filtered_results_footer.js +77 -0
  45. package/build/components/conversations/filtered_results_footer.js.map +1 -0
  46. package/build/components/display/action_button.d.ts.map +1 -1
  47. package/build/components/display/action_button.js +18 -16
  48. package/build/components/display/action_button.js.map +1 -1
  49. package/build/components/display/filter_dropdown.d.ts +14 -0
  50. package/build/components/display/filter_dropdown.d.ts.map +1 -0
  51. package/build/components/display/filter_dropdown.js +193 -0
  52. package/build/components/display/filter_dropdown.js.map +1 -0
  53. package/build/components/display/icon_tile.d.ts +13 -0
  54. package/build/components/display/icon_tile.d.ts.map +1 -0
  55. package/build/components/display/icon_tile.js +149 -0
  56. package/build/components/display/icon_tile.js.map +1 -0
  57. package/build/components/display/index.d.ts +2 -0
  58. package/build/components/display/index.d.ts.map +1 -1
  59. package/build/components/display/index.js +2 -0
  60. package/build/components/display/index.js.map +1 -1
  61. package/build/contexts/api_provider.d.ts.map +1 -1
  62. package/build/contexts/api_provider.js +21 -9
  63. package/build/contexts/api_provider.js.map +1 -1
  64. package/build/contexts/conversations_context.d.ts.map +1 -1
  65. package/build/contexts/conversations_context.js +21 -18
  66. package/build/contexts/conversations_context.js.map +1 -1
  67. package/build/hooks/groups/use_group_members_for_new_conversation.d.ts +196 -16
  68. package/build/hooks/groups/use_group_members_for_new_conversation.d.ts.map +1 -1
  69. package/build/hooks/groups/use_group_members_for_new_conversation.js +72 -58
  70. package/build/hooks/groups/use_group_members_for_new_conversation.js.map +1 -1
  71. package/build/hooks/index.d.ts +3 -0
  72. package/build/hooks/index.d.ts.map +1 -1
  73. package/build/hooks/index.js +3 -0
  74. package/build/hooks/index.js.map +1 -1
  75. package/build/hooks/use_animated_message_background_color.d.ts.map +1 -1
  76. package/build/hooks/use_animated_message_background_color.js +5 -3
  77. package/build/hooks/use_animated_message_background_color.js.map +1 -1
  78. package/build/hooks/use_api.d.ts +3 -2
  79. package/build/hooks/use_api.d.ts.map +1 -1
  80. package/build/hooks/use_api.js.map +1 -1
  81. package/build/hooks/use_can_browse_archived_conversations.d.ts +2 -0
  82. package/build/hooks/use_can_browse_archived_conversations.d.ts.map +1 -0
  83. package/build/hooks/use_can_browse_archived_conversations.js +29 -0
  84. package/build/hooks/use_can_browse_archived_conversations.js.map +1 -0
  85. package/build/hooks/use_clear_conversation_filters.d.ts +2 -0
  86. package/build/hooks/use_clear_conversation_filters.d.ts.map +1 -0
  87. package/build/hooks/use_clear_conversation_filters.js +25 -0
  88. package/build/hooks/use_clear_conversation_filters.js.map +1 -0
  89. package/build/hooks/use_conversation.d.ts +73 -1
  90. package/build/hooks/use_conversation.d.ts.map +1 -1
  91. package/build/hooks/use_conversation.js +130 -3
  92. package/build/hooks/use_conversation.js.map +1 -1
  93. package/build/hooks/use_conversation_messages.d.ts.map +1 -1
  94. package/build/hooks/use_conversation_messages.js +29 -26
  95. package/build/hooks/use_conversation_messages.js.map +1 -1
  96. package/build/hooks/use_current_person.d.ts.map +1 -1
  97. package/build/hooks/use_current_person.js +1 -1
  98. package/build/hooks/use_current_person.js.map +1 -1
  99. package/build/hooks/use_jolt.d.ts +1 -0
  100. package/build/hooks/use_jolt.d.ts.map +1 -1
  101. package/build/hooks/use_jolt.js +59 -0
  102. package/build/hooks/use_jolt.js.map +1 -1
  103. package/build/hooks/use_manual_archiving_enabled.d.ts +2 -0
  104. package/build/hooks/use_manual_archiving_enabled.d.ts.map +1 -0
  105. package/build/hooks/use_manual_archiving_enabled.js +18 -0
  106. package/build/hooks/use_manual_archiving_enabled.js.map +1 -0
  107. package/build/hooks/use_mark_latest_message_read.d.ts.map +1 -1
  108. package/build/hooks/use_mark_latest_message_read.js +3 -3
  109. package/build/hooks/use_mark_latest_message_read.js.map +1 -1
  110. package/build/hooks/use_reduce_motion.d.ts +2 -0
  111. package/build/hooks/use_reduce_motion.d.ts.map +1 -0
  112. package/build/hooks/use_reduce_motion.js +28 -0
  113. package/build/hooks/use_reduce_motion.js.map +1 -0
  114. package/build/hooks/use_unarchive_conversation_action.d.ts +7 -0
  115. package/build/hooks/use_unarchive_conversation_action.d.ts.map +1 -0
  116. package/build/hooks/use_unarchive_conversation_action.js +52 -0
  117. package/build/hooks/use_unarchive_conversation_action.js.map +1 -0
  118. package/build/screens/attachment_actions/attachment_actions_screen.d.ts.map +1 -1
  119. package/build/screens/attachment_actions/attachment_actions_screen.js +84 -61
  120. package/build/screens/attachment_actions/attachment_actions_screen.js.map +1 -1
  121. package/build/screens/conversation_details_screen.d.ts.map +1 -1
  122. package/build/screens/conversation_details_screen.js +490 -395
  123. package/build/screens/conversation_details_screen.js.map +1 -1
  124. package/build/screens/conversation_filters/components/conversation_filters.d.ts.map +1 -1
  125. package/build/screens/conversation_filters/components/conversation_filters.js +88 -18
  126. package/build/screens/conversation_filters/components/conversation_filters.js.map +1 -1
  127. package/build/screens/conversation_filters/components/rows.d.ts +9 -0
  128. package/build/screens/conversation_filters/components/rows.d.ts.map +1 -1
  129. package/build/screens/conversation_filters/components/rows.js +93 -1
  130. package/build/screens/conversation_filters/components/rows.js.map +1 -1
  131. package/build/screens/conversation_filters/context/conversation_filter_context.d.ts.map +1 -1
  132. package/build/screens/conversation_filters/context/conversation_filter_context.js +8 -4
  133. package/build/screens/conversation_filters/context/conversation_filter_context.js.map +1 -1
  134. package/build/screens/conversation_filters/filter_types.d.ts +1 -0
  135. package/build/screens/conversation_filters/filter_types.d.ts.map +1 -1
  136. package/build/screens/conversation_filters/filter_types.js +1 -0
  137. package/build/screens/conversation_filters/filter_types.js.map +1 -1
  138. package/build/screens/conversation_filters/screen_props.d.ts +1 -0
  139. package/build/screens/conversation_filters/screen_props.d.ts.map +1 -1
  140. package/build/screens/conversation_filters/screen_props.js.map +1 -1
  141. package/build/screens/conversation_filters_screen.d.ts +1 -0
  142. package/build/screens/conversation_filters_screen.d.ts.map +1 -1
  143. package/build/screens/conversation_filters_screen.js +79 -48
  144. package/build/screens/conversation_filters_screen.js.map +1 -1
  145. package/build/screens/conversation_new/components/groups_form.d.ts.map +1 -1
  146. package/build/screens/conversation_new/components/groups_form.js +37 -34
  147. package/build/screens/conversation_new/components/groups_form.js.map +1 -1
  148. package/build/screens/conversation_screen.d.ts.map +1 -1
  149. package/build/screens/conversation_screen.js +13 -10
  150. package/build/screens/conversation_screen.js.map +1 -1
  151. package/build/screens/conversations/components/filter_dropdown_list_header_component.d.ts +2 -0
  152. package/build/screens/conversations/components/filter_dropdown_list_header_component.d.ts.map +1 -0
  153. package/build/screens/conversations/components/filter_dropdown_list_header_component.js +580 -0
  154. package/build/screens/conversations/components/filter_dropdown_list_header_component.js.map +1 -0
  155. package/build/screens/conversations/components/list_header_component.d.ts.map +1 -1
  156. package/build/screens/conversations/components/list_header_component.js +95 -90
  157. package/build/screens/conversations/components/list_header_component.js.map +1 -1
  158. package/build/screens/conversations/components/search_list_header_component.d.ts.map +1 -1
  159. package/build/screens/conversations/components/search_list_header_component.js +100 -95
  160. package/build/screens/conversations/components/search_list_header_component.js.map +1 -1
  161. package/build/screens/conversations/conversations_screen.d.ts +1 -0
  162. package/build/screens/conversations/conversations_screen.d.ts.map +1 -1
  163. package/build/screens/conversations/conversations_screen.js +15 -2
  164. package/build/screens/conversations/conversations_screen.js.map +1 -1
  165. package/build/screens/design_system_screen.d.ts.map +1 -1
  166. package/build/screens/design_system_screen.js +275 -215
  167. package/build/screens/design_system_screen.js.map +1 -1
  168. package/build/screens/message_actions_screen.d.ts.map +1 -1
  169. package/build/screens/message_actions_screen.js +112 -92
  170. package/build/screens/message_actions_screen.js.map +1 -1
  171. package/build/screens/search/search_screen.d.ts.map +1 -1
  172. package/build/screens/search/search_screen.js +10 -2
  173. package/build/screens/search/search_screen.js.map +1 -1
  174. package/build/screens/send_giphy_screen.d.ts +1 -0
  175. package/build/screens/send_giphy_screen.d.ts.map +1 -1
  176. package/build/screens/send_giphy_screen.js +108 -106
  177. package/build/screens/send_giphy_screen.js.map +1 -1
  178. package/build/screens/team_conversation_screen.js +1 -1
  179. package/build/screens/team_conversation_screen.js.map +1 -1
  180. package/build/types/jolt_events/message_events.d.ts +2 -0
  181. package/build/types/jolt_events/message_events.d.ts.map +1 -1
  182. package/build/types/jolt_events/message_events.js.map +1 -1
  183. package/build/types/resources/conversation.d.ts +2 -2
  184. package/build/types/resources/conversation.d.ts.map +1 -1
  185. package/build/types/resources/conversation.js.map +1 -1
  186. package/build/types/resources/member_ability.d.ts +2 -0
  187. package/build/types/resources/member_ability.d.ts.map +1 -1
  188. package/build/types/resources/member_ability.js.map +1 -1
  189. package/build/types/resources/message.d.ts +2 -0
  190. package/build/types/resources/message.d.ts.map +1 -1
  191. package/build/types/resources/message.js.map +1 -1
  192. package/build/types/resources/person.d.ts +1 -0
  193. package/build/types/resources/person.d.ts.map +1 -1
  194. package/build/types/resources/person.js.map +1 -1
  195. package/build/utils/archived_conversation.d.ts +5 -0
  196. package/build/utils/archived_conversation.d.ts.map +1 -0
  197. package/build/utils/archived_conversation.js +5 -0
  198. package/build/utils/archived_conversation.js.map +1 -0
  199. package/build/utils/date.d.ts +1 -0
  200. package/build/utils/date.d.ts.map +1 -1
  201. package/build/utils/date.js +9 -0
  202. package/build/utils/date.js.map +1 -1
  203. package/build/utils/index.d.ts +1 -0
  204. package/build/utils/index.d.ts.map +1 -1
  205. package/build/utils/index.js +1 -0
  206. package/build/utils/index.js.map +1 -1
  207. package/build/utils/jolt/transform_message_event_data_to_message_resource.d.ts.map +1 -1
  208. package/build/utils/jolt/transform_message_event_data_to_message_resource.js +2 -1
  209. package/build/utils/jolt/transform_message_event_data_to_message_resource.js.map +1 -1
  210. package/build/utils/native_adapters/configuration.d.ts +3 -0
  211. package/build/utils/native_adapters/configuration.d.ts.map +1 -1
  212. package/build/utils/native_adapters/configuration.js +3 -0
  213. package/build/utils/native_adapters/configuration.js.map +1 -1
  214. package/build/utils/native_adapters/index.d.ts +1 -0
  215. package/build/utils/native_adapters/index.d.ts.map +1 -1
  216. package/build/utils/native_adapters/index.js +1 -0
  217. package/build/utils/native_adapters/index.js.map +1 -1
  218. package/build/utils/native_adapters/menu.d.ts +41 -0
  219. package/build/utils/native_adapters/menu.d.ts.map +1 -0
  220. package/build/utils/native_adapters/menu.js +56 -0
  221. package/build/utils/native_adapters/menu.js.map +1 -0
  222. package/build/utils/request/conversation.d.ts +2 -1
  223. package/build/utils/request/conversation.d.ts.map +1 -1
  224. package/build/utils/request/conversation.js +4 -1
  225. package/build/utils/request/conversation.js.map +1 -1
  226. package/build/utils/request/messages_data_options.d.ts.map +1 -1
  227. package/build/utils/request/messages_data_options.js +1 -1
  228. package/build/utils/request/messages_data_options.js.map +1 -1
  229. package/build/utils/system_messages.d.ts +7 -5
  230. package/build/utils/system_messages.d.ts.map +1 -1
  231. package/build/utils/system_messages.js +10 -2
  232. package/build/utils/system_messages.js.map +1 -1
  233. package/build/vendor/font_awesome_pro_solid_icons.d.ts +1 -0
  234. package/build/vendor/font_awesome_pro_solid_icons.d.ts.map +1 -1
  235. package/build/vendor/font_awesome_pro_solid_icons.js +5 -0
  236. package/build/vendor/font_awesome_pro_solid_icons.js.map +1 -1
  237. package/package.json +3 -3
  238. package/src/__tests__/hooks/use_can_browse_archived_conversations.test.tsx +98 -0
  239. package/src/__tests__/screens/conversation_filters/components/conversation_filters.test.tsx +132 -0
  240. package/src/__tests__/screens/conversation_filters_screen.test.tsx +76 -0
  241. package/src/__tests__/screens/conversation_new/components/services_form.test.tsx +1 -0
  242. package/src/__tests__/screens/message_actions_screen.test.tsx +65 -2
  243. package/src/__tests__/screens/send_giphy_screen.test.tsx +102 -0
  244. package/src/__tests__/utils/system_messages.ts +19 -32
  245. package/src/components/conversation/__tests__/message_list.test.tsx +68 -2
  246. package/src/components/conversation/__tests__/system_message.test.tsx +106 -0
  247. package/src/components/conversation/jump_to_bottom_button.tsx +4 -3
  248. package/src/components/conversation/message.tsx +3 -0
  249. package/src/components/conversation/message_form.tsx +3 -1
  250. package/src/components/conversation/message_list.tsx +9 -1
  251. package/src/components/conversation/message_reaction.tsx +7 -2
  252. package/src/components/conversation/messages_disabled_banners.tsx +17 -18
  253. package/src/components/conversation/system_message.tsx +44 -16
  254. package/src/components/conversations/__tests__/conversation_actions.test.tsx +67 -0
  255. package/src/components/conversations/__tests__/conversation_preview.test.tsx +105 -0
  256. package/src/components/conversations/__tests__/conversations.test.tsx +93 -0
  257. package/src/components/conversations/__tests__/conversations_blank_state.test.tsx +73 -0
  258. package/src/components/conversations/archived_results_footer.tsx +36 -0
  259. package/src/components/conversations/conversation_actions.tsx +27 -19
  260. package/src/components/conversations/conversation_preview.tsx +39 -10
  261. package/src/components/conversations/conversations.tsx +26 -3
  262. package/src/components/conversations/conversations_blank_state.tsx +21 -0
  263. package/src/components/conversations/filtered_results_footer.tsx +38 -0
  264. package/src/components/display/__tests__/filter_dropdown.test.tsx +114 -0
  265. package/src/components/display/__tests__/icon_tile.test.tsx +16 -0
  266. package/src/components/display/action_button.tsx +4 -3
  267. package/src/components/display/filter_dropdown.tsx +124 -0
  268. package/src/components/display/icon_tile.tsx +67 -0
  269. package/src/components/display/index.ts +2 -0
  270. package/src/contexts/api_provider.tsx +8 -0
  271. package/src/contexts/conversations_context.tsx +2 -1
  272. package/src/hooks/__tests__/use_conversation_archive.test.tsx +152 -0
  273. package/src/hooks/__tests__/use_conversation_jolt_events.test.tsx +74 -0
  274. package/src/hooks/__tests__/use_conversation_messages.test.tsx +10 -2
  275. package/src/hooks/__tests__/use_jolt.test.tsx +100 -1
  276. package/src/hooks/__tests__/use_mark_latest_message_read.test.tsx +21 -7
  277. package/src/hooks/__tests__/use_reduce_motion.test.tsx +61 -0
  278. package/src/hooks/__tests__/use_unarchive_conversation_action.test.tsx +67 -0
  279. package/src/hooks/groups/use_group_members_for_new_conversation.ts +21 -16
  280. package/src/hooks/index.ts +3 -0
  281. package/src/hooks/use_animated_message_background_color.ts +6 -3
  282. package/src/hooks/use_api.ts +9 -2
  283. package/src/hooks/use_can_browse_archived_conversations.ts +17 -0
  284. package/src/hooks/use_clear_conversation_filters.ts +16 -0
  285. package/src/hooks/use_conversation.ts +44 -3
  286. package/src/hooks/use_conversation_messages.ts +7 -5
  287. package/src/hooks/use_current_person.ts +1 -0
  288. package/src/hooks/use_jolt.ts +57 -1
  289. package/src/hooks/use_manual_archiving_enabled.ts +10 -0
  290. package/src/hooks/use_mark_latest_message_read.ts +4 -3
  291. package/src/hooks/use_reduce_motion.ts +19 -0
  292. package/src/hooks/use_unarchive_conversation_action.ts +24 -0
  293. package/src/screens/__tests__/conversation_details_screen.test.tsx +305 -0
  294. package/src/screens/attachment_actions/__tests__/attachment_actions_screen.test.tsx +72 -0
  295. package/src/screens/attachment_actions/attachment_actions_screen.tsx +3 -1
  296. package/src/screens/conversation_details_screen.tsx +80 -16
  297. package/src/screens/conversation_filters/__tests__/conversation_filters.test.tsx +101 -0
  298. package/src/screens/conversation_filters/components/conversation_filters.tsx +40 -7
  299. package/src/screens/conversation_filters/components/rows.tsx +29 -1
  300. package/src/screens/conversation_filters/context/conversation_filter_context.tsx +4 -0
  301. package/src/screens/conversation_filters/filter_types.ts +1 -0
  302. package/src/screens/conversation_filters/screen_props.ts +1 -0
  303. package/src/screens/conversation_filters_screen.tsx +25 -5
  304. package/src/screens/conversation_new/components/groups_form.tsx +1 -0
  305. package/src/screens/conversation_screen.tsx +4 -2
  306. package/src/screens/conversations/__tests__/conversations_screen.test.tsx +52 -0
  307. package/src/screens/conversations/components/__tests__/filter_dropdown_list_header_component.test.tsx +259 -0
  308. package/src/screens/conversations/components/__tests__/list_header_component.test.tsx +21 -4
  309. package/src/screens/conversations/components/filter_dropdown_list_header_component.tsx +286 -0
  310. package/src/screens/conversations/components/list_header_component.tsx +17 -12
  311. package/src/screens/conversations/components/search_list_header_component.tsx +17 -12
  312. package/src/screens/conversations/conversations_screen.tsx +15 -2
  313. package/src/screens/design_system_screen.tsx +26 -0
  314. package/src/screens/message_actions_screen.tsx +29 -20
  315. package/src/screens/search/__tests__/search_screen.test.tsx +13 -1
  316. package/src/screens/search/search_screen.tsx +8 -2
  317. package/src/screens/send_giphy_screen.tsx +6 -3
  318. package/src/screens/team_conversation_screen.tsx +1 -1
  319. package/src/types/jolt_events/message_events.ts +2 -0
  320. package/src/types/resources/conversation.ts +2 -2
  321. package/src/types/resources/member_ability.ts +2 -0
  322. package/src/types/resources/message.ts +3 -0
  323. package/src/types/resources/person.ts +1 -0
  324. package/src/utils/__tests__/conversation.test.ts +1 -0
  325. package/src/utils/__tests__/date.test.ts +23 -0
  326. package/src/utils/__tests__/group_messages.test.ts +1 -0
  327. package/src/utils/archived_conversation.ts +13 -0
  328. package/src/utils/date.ts +8 -0
  329. package/src/utils/index.ts +1 -0
  330. package/src/utils/jolt/__tests__/transform_message_event_data_to_message_resource.test.ts +35 -0
  331. package/src/utils/jolt/transform_message_event_data_to_message_resource.ts +1 -0
  332. package/src/utils/native_adapters/__tests__/menu.test.tsx +46 -0
  333. package/src/utils/native_adapters/configuration.ts +5 -0
  334. package/src/utils/native_adapters/index.ts +1 -0
  335. package/src/utils/native_adapters/menu.tsx +70 -0
  336. package/src/utils/request/conversation.ts +4 -0
  337. package/src/utils/request/messages_data_options.ts +1 -0
  338. package/src/utils/system_messages.ts +19 -10
  339. package/src/vendor/font_awesome_pro_solid_icons.ts +12 -0
@@ -0,0 +1,305 @@
1
+ import { DefaultTheme, ThemeProvider } from '@react-navigation/native'
2
+ import { QueryClientProvider } from '@tanstack/react-query'
3
+ import { fireEvent, render } from '@testing-library/react-native'
4
+ import React from 'react'
5
+ import { Alert } from 'react-native'
6
+ import { SafeAreaProvider } from 'react-native-safe-area-context'
7
+ import { buildTestQueryClient } from '../../__utils__/query_client'
8
+ import type { ConversationResource } from '../../types'
9
+ import { ConversationDetailsScreen } from '../conversation_details_screen'
10
+
11
+ const baseConversation = {
12
+ type: 'Conversation',
13
+ id: 7,
14
+ title: 'Youth Group',
15
+ muted: false,
16
+ repliesDisabled: false,
17
+ unreadCount: 0,
18
+ unreadReaction: false,
19
+ conversationPreview: null,
20
+ createdAt: '2026-01-01T00:00:00Z',
21
+ updatedAt: '2026-01-01T00:00:00Z',
22
+ badges: [],
23
+ groups: [],
24
+ disabled: false,
25
+ disabledReason: null,
26
+ disabledMessage: null,
27
+ memberAbility: {
28
+ type: 'MemberAbility',
29
+ canUpdate: true,
30
+ canDelete: true,
31
+ canDeleteNonAuthoredMessages: true,
32
+ leader: true,
33
+ canReply: true,
34
+ canMessageAvailableMembers: false,
35
+ },
36
+ conversationMembership: { notificationLevelDescription: 'All messages' },
37
+ } as unknown as ConversationResource
38
+
39
+ const archivedConversation = {
40
+ ...baseConversation,
41
+ disabled: true,
42
+ disabledReason: 'archived',
43
+ disabledMessage: 'This conversation is archived. Only you and other leaders can unarchive.',
44
+ memberAbility: { ...baseConversation.memberAbility, canUpdate: false },
45
+ } as ConversationResource
46
+
47
+ const mockNavigation = {
48
+ navigate: jest.fn(),
49
+ dispatch: jest.fn(),
50
+ goBack: jest.fn(),
51
+ setOptions: jest.fn(),
52
+ }
53
+ jest.mock('@react-navigation/native', () => ({
54
+ ...jest.requireActual('@react-navigation/native'),
55
+ useNavigation: () => mockNavigation,
56
+ }))
57
+ jest.mock('../../hooks/use_api_client', () => ({ useApiClient: () => ({}) }))
58
+
59
+ let mockConversation: ConversationResource
60
+ const mockArchive = jest.fn()
61
+ const mockSaveTitle = jest.fn()
62
+ jest.mock('../../hooks/use_conversation', () => ({
63
+ useConversation: () => ({ data: mockConversation }),
64
+ useConversationArchive: () => ({ setArchived: mockArchive, isPending: false }),
65
+ useConversationDelete: () => ({ mutate: jest.fn() }),
66
+ useConversationDisableReplies: () => ({ repliesDisabled: false, setRepliesDisabled: jest.fn() }),
67
+ useConversationMute: () => ({ muted: false, setMuted: jest.fn() }),
68
+ useConversationUpdate: () => ({ mutate: mockSaveTitle }),
69
+ }))
70
+
71
+ let mockEnabledFeatures: string[] = []
72
+ jest.mock('../../hooks/use_features', () => ({
73
+ ...jest.requireActual('../../hooks/use_features'),
74
+ useFeatures: () => ({
75
+ features: [],
76
+ featureEnabled: (name: string) => mockEnabledFeatures.includes(name),
77
+ }),
78
+ }))
79
+
80
+ jest.mock('../../hooks/use_suspense_api', () => ({
81
+ ...jest.requireActual('../../hooks/use_suspense_api'),
82
+ useSuspensePaginator: () => ({ data: [], fetchNextPage: jest.fn(), isFetchingNextPage: false }),
83
+ }))
84
+
85
+ let mockPressDone = () => {}
86
+ jest.mock('../../components/display/platform_modal_header_buttons', () => ({
87
+ ...jest.requireActual('../../components/display/platform_modal_header_buttons'),
88
+ useHeaderRight: ({ onPress }: { onPress: () => void }) => {
89
+ mockPressDone = onPress
90
+ },
91
+ }))
92
+ jest.mock('../../components/display/conversation_avatar', () => ({
93
+ ConversationAvatar: () => null,
94
+ }))
95
+
96
+ const buildConversation = (canArchive: boolean) =>
97
+ ({
98
+ type: 'Conversation',
99
+ id: 7,
100
+ title: 'Youth Group',
101
+ memberAbility: { type: 'MemberAbility', canArchive, canDelete: false },
102
+ groups: [],
103
+ badges: [],
104
+ }) as unknown as ConversationResource
105
+
106
+ const safeAreaMetrics = {
107
+ frame: { x: 0, y: 0, width: 390, height: 844 },
108
+ insets: { top: 0, right: 0, bottom: 0, left: 0 },
109
+ }
110
+
111
+ const renderScreen = () =>
112
+ render(
113
+ <QueryClientProvider client={buildTestQueryClient()}>
114
+ <SafeAreaProvider initialMetrics={safeAreaMetrics}>
115
+ <ThemeProvider value={DefaultTheme}>
116
+ <ConversationDetailsScreen route={{ params: { conversation_id: 7 } }} />
117
+ </ThemeProvider>
118
+ </SafeAreaProvider>
119
+ </QueryClientProvider>
120
+ )
121
+
122
+ const alertButton = (text: string) => {
123
+ const buttons = jest.mocked(Alert.alert).mock.calls[0][2] ?? []
124
+ return buttons.find(button => button.text === text)
125
+ }
126
+
127
+ describe('ConversationDetailsScreen', () => {
128
+ beforeEach(() => {
129
+ mockConversation = baseConversation
130
+ mockEnabledFeatures = []
131
+ jest.clearAllMocks()
132
+ })
133
+
134
+ describe('for an active conversation', () => {
135
+ it('offers mute and the leader settings', () => {
136
+ mockEnabledFeatures = ['ROLLOUT_custom_conversation_avatars']
137
+ const { getByText } = renderScreen()
138
+
139
+ expect(getByText('Mute')).toBeTruthy()
140
+ expect(getByText('Only leaders can message')).toBeTruthy()
141
+ expect(getByText('Update avatar')).toBeTruthy()
142
+ })
143
+
144
+ it('offers notification levels when granular notifications are on', () => {
145
+ mockEnabledFeatures = ['ROLLOUT_granular_notification_preferences_ui']
146
+ const { getByText } = renderScreen()
147
+
148
+ expect(getByText('Notifications')).toBeTruthy()
149
+ })
150
+
151
+ it('saves the title on done', () => {
152
+ renderScreen()
153
+ mockPressDone()
154
+
155
+ expect(mockSaveTitle).toHaveBeenCalledWith({ title: 'Youth Group' })
156
+ })
157
+
158
+ it('renders a conversation with no title and asks for one', () => {
159
+ mockConversation = { ...baseConversation, title: null }
160
+ const { getByText, getByDisplayValue } = renderScreen()
161
+
162
+ expect(getByDisplayValue('')).toBeTruthy()
163
+ expect(getByText('A title is required for your conversation.')).toBeTruthy()
164
+ })
165
+
166
+ it('does not save a cleared title on done', () => {
167
+ const { getByDisplayValue } = renderScreen()
168
+ fireEvent.changeText(getByDisplayValue('Youth Group'), ' ')
169
+ mockPressDone()
170
+
171
+ expect(mockSaveTitle).not.toHaveBeenCalled()
172
+ })
173
+ })
174
+
175
+ describe('for an archived conversation', () => {
176
+ beforeEach(() => {
177
+ mockConversation = archivedConversation
178
+ })
179
+
180
+ it('replaces mute and the leader settings with the archived notice', () => {
181
+ mockEnabledFeatures = ['ROLLOUT_custom_conversation_avatars']
182
+ const { getByText, queryByText } = renderScreen()
183
+
184
+ expect(
185
+ getByText('This conversation is archived. Only you and other leaders can unarchive.')
186
+ ).toBeTruthy()
187
+ expect(queryByText('Mute')).toBeNull()
188
+ expect(queryByText('Only leaders can message')).toBeNull()
189
+ expect(queryByText('Update avatar')).toBeNull()
190
+ expect(getByText('Delete conversation…')).toBeTruthy()
191
+ })
192
+
193
+ it('falls back to a default notice when the server sends no message', () => {
194
+ mockConversation = { ...archivedConversation, disabledMessage: null }
195
+ const { getByText } = renderScreen()
196
+
197
+ expect(getByText('This conversation is archived.')).toBeTruthy()
198
+ })
199
+
200
+ it('hides notification levels even when granular notifications are on', () => {
201
+ mockEnabledFeatures = ['ROLLOUT_granular_notification_preferences_ui']
202
+ const { queryByText } = renderScreen()
203
+
204
+ expect(queryByText('Notifications')).toBeNull()
205
+ })
206
+
207
+ it('locks the title', () => {
208
+ const { getByDisplayValue } = renderScreen()
209
+
210
+ expect(getByDisplayValue('Youth Group').props.editable).toBe(false)
211
+ })
212
+
213
+ it('does not save the title on done', () => {
214
+ renderScreen()
215
+ mockPressDone()
216
+
217
+ expect(mockSaveTitle).not.toHaveBeenCalled()
218
+ })
219
+
220
+ it('unarchives from the notice for someone who can unarchive', () => {
221
+ mockEnabledFeatures = ['ROLLOUT_MOBILE_manual_archiving']
222
+ mockConversation = {
223
+ ...archivedConversation,
224
+ memberAbility: { ...archivedConversation.memberAbility!, canUnarchive: true },
225
+ }
226
+ const { getByText } = renderScreen()
227
+
228
+ fireEvent.press(getByText('Unarchive'))
229
+
230
+ expect(mockArchive).toHaveBeenCalledWith(false)
231
+ })
232
+ })
233
+ })
234
+
235
+ describe('ConversationDetailsScreen archive', () => {
236
+ beforeEach(() => {
237
+ jest.clearAllMocks()
238
+ jest.spyOn(Alert, 'alert').mockImplementation(() => {})
239
+ mockConversation = buildConversation(true)
240
+ mockEnabledFeatures = ['ROLLOUT_MOBILE_manual_archiving']
241
+ })
242
+
243
+ afterEach(() => {
244
+ jest.restoreAllMocks()
245
+ })
246
+
247
+ it('hides archiving when the member cannot archive', () => {
248
+ mockConversation = buildConversation(false)
249
+ const { queryByText } = renderScreen()
250
+
251
+ expect(queryByText('Archive conversation…')).toBeNull()
252
+ })
253
+
254
+ it.each(['QA_MOBILE_manual_archiving', 'ROLLOUT_MOBILE_manual_archiving'])(
255
+ 'offers archiving with only %s on',
256
+ flag => {
257
+ mockEnabledFeatures = [flag]
258
+ const { getByText } = renderScreen()
259
+
260
+ expect(getByText('Archive conversation…')).toBeTruthy()
261
+ }
262
+ )
263
+
264
+ it('hides archiving when neither mobile flag is on', () => {
265
+ mockEnabledFeatures = ['ROLLOUT_WEB_manual_archiving']
266
+ const { queryByText } = renderScreen()
267
+
268
+ expect(queryByText('Archive conversation…')).toBeNull()
269
+ })
270
+
271
+ it('asks for confirmation before archiving', () => {
272
+ const { getByText } = renderScreen()
273
+
274
+ fireEvent.press(getByText('Archive conversation…'))
275
+
276
+ expect(Alert.alert).toHaveBeenCalledWith(
277
+ 'Archive conversation',
278
+ 'This conversation will be archived for everyone. Only its leaders and creator can open and unarchive it.',
279
+ expect.any(Array)
280
+ )
281
+ expect(mockArchive).not.toHaveBeenCalled()
282
+ })
283
+
284
+ it('archives once confirmed and stays on the details', () => {
285
+ const { getByText } = renderScreen()
286
+ fireEvent.press(getByText('Archive conversation…'))
287
+
288
+ alertButton('Archive')?.onPress?.()
289
+
290
+ expect(mockArchive).toHaveBeenCalledTimes(1)
291
+ expect(mockArchive).toHaveBeenCalledWith(true)
292
+ expect(mockNavigation.dispatch).not.toHaveBeenCalled()
293
+ expect(mockNavigation.goBack).not.toHaveBeenCalled()
294
+ expect(mockNavigation.navigate).not.toHaveBeenCalled()
295
+ })
296
+
297
+ it('does nothing when the confirmation is cancelled', () => {
298
+ const { getByText } = renderScreen()
299
+ fireEvent.press(getByText('Archive conversation…'))
300
+
301
+ alertButton('Cancel')?.onPress?.()
302
+
303
+ expect(mockArchive).not.toHaveBeenCalled()
304
+ })
305
+ })
@@ -0,0 +1,72 @@
1
+ import { HeaderHeightContext } from '@react-navigation/elements'
2
+ import { DefaultTheme, ThemeProvider } from '@react-navigation/native'
3
+ import { render } from '@testing-library/react-native'
4
+ import React from 'react'
5
+ import { SafeAreaProvider } from 'react-native-safe-area-context'
6
+ import type { ConversationResource } from '../../../types'
7
+ import { AttachmentActionsScreen, AttachmentActionsScreenProps } from '../attachment_actions_screen'
8
+
9
+ let mockConversation = { id: 1, disabled: false } as ConversationResource
10
+
11
+ jest.mock('@react-navigation/native', () => ({
12
+ ...jest.requireActual('@react-navigation/native'),
13
+ useNavigation: () => ({ goBack: jest.fn() }),
14
+ }))
15
+ jest.mock('../../../hooks/use_conversation', () => ({
16
+ useConversation: () => ({ data: mockConversation }),
17
+ }))
18
+ jest.mock('../hooks/useDeleteAttachment', () => ({
19
+ useDeleteAttachment: () => ({ handleDeleteAttachment: jest.fn() }),
20
+ }))
21
+
22
+ const safeAreaMetrics = {
23
+ frame: { x: 0, y: 0, width: 390, height: 844 },
24
+ insets: { top: 0, right: 0, bottom: 0, left: 0 },
25
+ }
26
+
27
+ const renderScreen = (params: Partial<AttachmentActionsScreenProps['route']['params']> = {}) => {
28
+ const route = {
29
+ key: 'AttachmentActions',
30
+ name: 'AttachmentActions',
31
+ params: {
32
+ attachmentId: 'a1',
33
+ attachmentContentType: 'image/png',
34
+ attachmentUrl: 'https://example.com/a1.png',
35
+ conversation_id: 1,
36
+ canDeleteNonAuthoredMessages: false,
37
+ myMessage: true,
38
+ ...params,
39
+ },
40
+ } as AttachmentActionsScreenProps['route']
41
+
42
+ return render(
43
+ <SafeAreaProvider initialMetrics={safeAreaMetrics}>
44
+ <ThemeProvider value={DefaultTheme}>
45
+ <HeaderHeightContext.Provider value={0}>
46
+ <AttachmentActionsScreen route={route} />
47
+ </HeaderHeightContext.Provider>
48
+ </ThemeProvider>
49
+ </SafeAreaProvider>
50
+ )
51
+ }
52
+
53
+ describe('AttachmentActionsScreen', () => {
54
+ beforeEach(() => {
55
+ mockConversation = { id: 1, disabled: false } as ConversationResource
56
+ })
57
+
58
+ it('offers Delete on my own attachment', () => {
59
+ const { getByText } = renderScreen()
60
+
61
+ expect(getByText('Delete image')).toBeTruthy()
62
+ })
63
+
64
+ it('hides Delete when the conversation is disabled', () => {
65
+ mockConversation = { id: 1, disabled: true, disabledReason: 'locked' } as ConversationResource
66
+
67
+ const { queryByText, getByText } = renderScreen({ canDeleteNonAuthoredMessages: true })
68
+
69
+ expect(queryByText('Delete image')).toBeNull()
70
+ expect(getByText('Open in browser')).toBeTruthy()
71
+ })
72
+ })
@@ -2,6 +2,7 @@ import { StaticScreenProps, useNavigation } from '@react-navigation/native'
2
2
  import React, { useCallback } from 'react'
3
3
  import { Alert, Linking, StyleSheet } from 'react-native'
4
4
  import FormSheet, { getFormSheetScreenOptions } from '../../components/primitive/form_sheet'
5
+ import { useConversation } from '../../hooks/use_conversation'
5
6
  import { useDeleteAttachment } from './hooks/useDeleteAttachment'
6
7
 
7
8
  export const AttachmentActionsScreenOptions = getFormSheetScreenOptions({
@@ -28,8 +29,9 @@ export function AttachmentActionsScreen({ route }: AttachmentActionsScreenProps)
28
29
  myMessage,
29
30
  } = route.params
30
31
 
32
+ const { data: conversation } = useConversation({ conversation_id }, { refetchOnMount: false })
31
33
  const attachmentName = getAttachmentLabelName(attachmentContentType)
32
- const canDelete = myMessage || canDeleteNonAuthoredMessages
34
+ const canDelete = !conversation.disabled && (myMessage || canDeleteNonAuthoredMessages)
33
35
  const styles = useStyles()
34
36
 
35
37
  const { handleDeleteAttachment } = useDeleteAttachment({
@@ -43,16 +43,20 @@ import { ButtonAppearanceUnion } from '../components/display/utils/button_colors
43
43
  import { useSuspensePaginator, useTheme } from '../hooks'
44
44
  import {
45
45
  useConversation,
46
+ useConversationArchive,
46
47
  useConversationDelete,
47
48
  useConversationDisableReplies,
48
49
  useConversationMute,
49
50
  useConversationUpdate,
50
51
  } from '../hooks/use_conversation'
51
52
  import { availableFeatures, useFeatures } from '../hooks/use_features'
53
+ import { useManualArchivingEnabled } from '../hooks/use_manual_archiving_enabled'
52
54
  import { usePreviewAvatarDiameter } from '../hooks/use_preview_avatar_diameter'
55
+ import { useUnarchiveConversationAction } from '../hooks/use_unarchive_conversation_action'
53
56
  import { type ConversationResource, MemberResource, isDefined } from '../types'
54
57
  import { GroupResource } from '../types/resources/group_resource'
55
58
  import { MAX_FONT_SIZE_MULTIPLIER_LANDMARK } from '../utils'
59
+ import { archivedConversationMessage, isArchivedConversation } from '../utils/archived_conversation'
56
60
  import { genderDisplayLabel } from '../utils/gender_display_label'
57
61
  import { tokens } from '../vendor/tapestry/tokens'
58
62
  // =========================================
@@ -100,26 +104,32 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
100
104
  const styles = useStyles()
101
105
 
102
106
  const { data: conversation } = useConversation(route.params)
103
- const [title, setTitle] = useState(conversation.title)
107
+ const [title, setTitle] = useState(conversation.title ?? '')
104
108
  const { conversation_id } = route.params
105
109
  const { repliesDisabled, setRepliesDisabled } = useConversationDisableReplies(route.params)
106
110
  const { mutate: saveTitle } = useConversationUpdate(route.params)
107
111
  const { mutate: deleteConversation } = useConversationDelete(route.params)
112
+ const { setArchived, isPending: isArchiving } = useConversationArchive(route.params)
113
+ const unarchiveAction = useUnarchiveConversationAction(conversation)
108
114
  const { featureEnabled } = useFeatures()
109
115
  const granularNotificationsEnabled = featureEnabled(availableFeatures.granular_notifications_ui)
110
116
  const customAvatarsEnabled = featureEnabled(availableFeatures.custom_conversation_avatars)
117
+ const manualArchivingEnabled = useManualArchivingEnabled()
111
118
  const showGenderFilter =
112
119
  featureEnabled(availableFeatures.gender_specific_conversations) && !!conversation.genderOption
113
120
  const genderLabel = conversation.genderOption ? genderDisplayLabel(conversation.genderOption) : ''
114
121
  const { muted, setMuted } = useConversationMute({ conversation_id })
115
122
 
116
123
  const trimmedTitle = title.trim()
117
- const emptyTitle = trimmedTitle === '' || title === null
124
+ const emptyTitle = trimmedTitle === ''
118
125
 
119
126
  const notificationLevelDescription =
120
127
  conversation?.conversationMembership?.notificationLevelDescription
128
+ const archived = isArchivedConversation(conversation)
121
129
  const canUpdate = conversation.memberAbility?.canUpdate || false
130
+ const canSaveTitle = canUpdate && !emptyTitle
122
131
  const canDelete = conversation.memberAbility?.canDelete || false
132
+ const canArchive = manualArchivingEnabled && (conversation.memberAbility?.canArchive || false)
123
133
  const isLeader = conversation.memberAbility?.leader || false
124
134
  const isDisabled = conversation.disabled ?? false
125
135
  const showAdultRequirementNotice = isDisabled && conversation.disabledReason === 'safety_check'
@@ -203,6 +213,23 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
203
213
  )
204
214
  }, [deleteConversation, navigation, title])
205
215
 
216
+ const handleArchive = useCallback(() => {
217
+ Alert.alert(
218
+ 'Archive conversation',
219
+ 'This conversation will be archived for everyone. Only its leaders and creator can open and unarchive it.',
220
+ [
221
+ {
222
+ text: 'Cancel',
223
+ style: 'cancel',
224
+ },
225
+ {
226
+ text: 'Archive',
227
+ onPress: () => setArchived(true),
228
+ },
229
+ ]
230
+ )
231
+ }, [setArchived])
232
+
206
233
  const HeaderTitle = useCallback(
207
234
  (props: HeaderTitleProps) => {
208
235
  return (
@@ -226,7 +253,7 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
226
253
  icon: 'general.check',
227
254
  accessibilityLabel: 'Done',
228
255
  onPress: () => {
229
- saveTitle({ title: trimmedTitle || conversation.title })
256
+ if (canSaveTitle) saveTitle({ title: trimmedTitle })
230
257
  navigation.goBack()
231
258
  },
232
259
  })
@@ -275,25 +302,47 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
275
302
  showBottomBorder: true,
276
303
  sectionInnerStyle: styles.sectionInnerHeaderWithBottomBorder,
277
304
  },
278
- granularNotificationsEnabled
305
+ archived
279
306
  ? {
280
- type: SectionTypes.navigableSetting,
307
+ type: SectionTypes.view,
281
308
  data: {
282
- title: 'Notifications',
283
- subtitle: notificationLevelDescription,
284
- onPress: () =>
285
- navigation.navigate('ConversationNotificationLevelSelect', { conversation_id }),
309
+ children: (
310
+ <>
311
+ <Text variant="tertiary">{archivedConversationMessage(conversation)}</Text>
312
+ {unarchiveAction && (
313
+ <Button
314
+ variant="fill"
315
+ size="lg"
316
+ title={unarchiveAction.label}
317
+ onPress={unarchiveAction.onPress}
318
+ loading={unarchiveAction.loading}
319
+ style={styles.unarchiveButton}
320
+ />
321
+ )}
322
+ </>
323
+ ),
286
324
  },
287
325
  showBottomBorder: true,
288
326
  }
289
- : {
290
- type: SectionTypes.setting,
291
- data: {
292
- title: 'Mute',
293
- rightItem: <Switch value={muted} onValueChange={value => setMuted(value)} />,
327
+ : granularNotificationsEnabled
328
+ ? {
329
+ type: SectionTypes.navigableSetting,
330
+ data: {
331
+ title: 'Notifications',
332
+ subtitle: notificationLevelDescription,
333
+ onPress: () =>
334
+ navigation.navigate('ConversationNotificationLevelSelect', { conversation_id }),
335
+ },
336
+ showBottomBorder: true,
337
+ }
338
+ : {
339
+ type: SectionTypes.setting,
340
+ data: {
341
+ title: 'Mute',
342
+ rightItem: <Switch value={muted} onValueChange={value => setMuted(value)} />,
343
+ },
344
+ showBottomBorder: true,
294
345
  },
295
- showBottomBorder: true,
296
- },
297
346
  {
298
347
  type: canUpdate ? SectionTypes.setting : SectionTypes.hidden,
299
348
  data: {
@@ -304,6 +353,15 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
304
353
  },
305
354
  showBottomBorder: true,
306
355
  },
356
+ {
357
+ type: canArchive ? SectionTypes.setting : SectionTypes.hidden,
358
+ data: {
359
+ title: 'Archive conversation…',
360
+ disabled: isArchiving,
361
+ onPress: handleArchive,
362
+ },
363
+ showBottomBorder: canDelete,
364
+ },
307
365
  {
308
366
  type: canDelete ? SectionTypes.setting : SectionTypes.hidden,
309
367
  data: {
@@ -557,6 +615,7 @@ interface SettingRowProps {
557
615
  subtitle?: string
558
616
  rightItemStyle?: ViewStyle
559
617
  appearance?: ButtonAppearanceUnion
618
+ disabled?: boolean
560
619
  onPress?: () => void
561
620
  }
562
621
 
@@ -569,6 +628,7 @@ function SettingRow({
569
628
  rightItem,
570
629
  rightItemStyle = {},
571
630
  appearance,
631
+ disabled,
572
632
  onPress,
573
633
  }: SettingRowProps) {
574
634
  const styles = useStyles()
@@ -579,6 +639,7 @@ function SettingRow({
579
639
  style={[styles.settingRow, style]}
580
640
  textStyle={buttonTextStyle}
581
641
  appearance={appearance}
642
+ disabled={disabled}
582
643
  onPress={onPress}
583
644
  >
584
645
  {title}
@@ -778,6 +839,9 @@ const useStyles = ({ isStart, isEnd }: { isStart?: boolean; isEnd?: boolean } =
778
839
  deleteButtonText: {
779
840
  fontWeight: 'normal',
780
841
  },
842
+ unarchiveButton: {
843
+ marginTop: 12,
844
+ },
781
845
  settingRowText: {
782
846
  lineHeight: 20,
783
847
  },
@@ -0,0 +1,101 @@
1
+ import { DefaultTheme, ThemeProvider } from '@react-navigation/native'
2
+ import { fireEvent, render, within } from '@testing-library/react-native'
3
+ import React from 'react'
4
+ import { ConversationFilters } from '../components/conversation_filters'
5
+ import { FilterContext } from '../context/conversation_filter_context'
6
+ import { ConversationFiltersParams } from '../screen_props'
7
+
8
+ jest.mock('react-native-gesture-handler', () => {
9
+ const { createElement } = jest.requireActual('react')
10
+ const { FlatList } = jest.requireActual('react-native')
11
+
12
+ return {
13
+ FlatList: (props: object) => createElement(FlatList, { ...props, initialNumToRender: 50 }),
14
+ }
15
+ })
16
+
17
+ let mockCanBrowseArchived = false
18
+ jest.mock('../../../hooks/use_can_browse_archived_conversations', () => ({
19
+ useCanBrowseArchivedConversations: () => mockCanBrowseArchived,
20
+ }))
21
+ jest.mock('../../../hooks/use_direct_messages_eligible', () => ({
22
+ useDirectMessagesEligible: () => false,
23
+ }))
24
+ jest.mock('../../../hooks/use_filter_dropdown_enabled', () => ({
25
+ useFilterDropdownEnabled: () => false,
26
+ }))
27
+ jest.mock('../../../hooks/use_has_direct_messages', () => ({
28
+ useHasDirectMessages: () => false,
29
+ }))
30
+ jest.mock('../hooks/filters', () => ({
31
+ useGroupsToFilter: () => ({ groups: [] }),
32
+ useTeamsToFilter: () => ({ teams: [] }),
33
+ }))
34
+
35
+ const mockSetAppFilter = jest.fn()
36
+
37
+ const renderFilters = (params: ConversationFiltersParams = {}) =>
38
+ render(
39
+ <ThemeProvider value={DefaultTheme}>
40
+ <FilterContext.Provider
41
+ value={{
42
+ params,
43
+ scrollOffset: 0,
44
+ setScrollOffset: jest.fn(),
45
+ setFormSheetHeaderHeight: jest.fn(),
46
+ resetFilter: jest.fn(),
47
+ setGroupFilter: jest.fn(),
48
+ setAppFilter: mockSetAppFilter,
49
+ applyFilters: jest.fn(),
50
+ platformListPaddingBottom: 0,
51
+ }}
52
+ >
53
+ <ConversationFilters />
54
+ </FilterContext.Provider>
55
+ </ThemeProvider>
56
+ )
57
+
58
+ describe('ConversationFilters archived option', () => {
59
+ beforeEach(() => {
60
+ mockSetAppFilter.mockClear()
61
+ mockCanBrowseArchived = false
62
+ })
63
+
64
+ it('hides the Other section when the person cannot browse archived conversations', () => {
65
+ const view = renderFilters()
66
+
67
+ expect(view.queryByText('Other')).toBeNull()
68
+ expect(view.queryByText('Archived conversations')).toBeNull()
69
+ })
70
+
71
+ it('offers Archived under Other when the person can browse archived conversations', () => {
72
+ mockCanBrowseArchived = true
73
+
74
+ const view = renderFilters()
75
+
76
+ expect(view.getByText('Other')).toBeTruthy()
77
+ expect(view.getByText('Archived conversations')).toBeTruthy()
78
+ expect(view.getByText('Archived for everybody')).toBeTruthy()
79
+ expect(view.queryByRole('button', { selected: true })).toBeNull()
80
+ })
81
+
82
+ it('selecting Archived sets the archived filter param', () => {
83
+ mockCanBrowseArchived = true
84
+
85
+ const view = renderFilters()
86
+
87
+ fireEvent.press(view.getByText('Archived conversations'))
88
+
89
+ expect(mockSetAppFilter).toHaveBeenCalledTimes(1)
90
+ expect(mockSetAppFilter).toHaveBeenCalledWith({ show_archived: true })
91
+ })
92
+
93
+ it('preselects Archived when it is the active filter', () => {
94
+ mockCanBrowseArchived = true
95
+
96
+ const view = renderFilters({ show_archived: true })
97
+
98
+ const selected = view.getByRole('button', { selected: true })
99
+ expect(within(selected).getByText('Archived conversations')).toBeTruthy()
100
+ })
101
+ })