@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.
- package/build/components/conversation/jump_to_bottom_button.d.ts.map +1 -1
- package/build/components/conversation/jump_to_bottom_button.js +42 -40
- package/build/components/conversation/jump_to_bottom_button.js.map +1 -1
- package/build/components/conversation/message.d.ts +2 -1
- package/build/components/conversation/message.d.ts.map +1 -1
- package/build/components/conversation/message.js +3 -1
- package/build/components/conversation/message.js.map +1 -1
- package/build/components/conversation/message_form.d.ts.map +1 -1
- package/build/components/conversation/message_form.js +3 -1
- package/build/components/conversation/message_form.js.map +1 -1
- package/build/components/conversation/message_list.d.ts.map +1 -1
- package/build/components/conversation/message_list.js +35 -31
- package/build/components/conversation/message_list.js.map +1 -1
- package/build/components/conversation/message_reaction.d.ts +2 -1
- package/build/components/conversation/message_reaction.d.ts.map +1 -1
- package/build/components/conversation/message_reaction.js +67 -60
- package/build/components/conversation/message_reaction.js.map +1 -1
- package/build/components/conversation/messages_disabled_banners.d.ts +7 -5
- package/build/components/conversation/messages_disabled_banners.d.ts.map +1 -1
- package/build/components/conversation/messages_disabled_banners.js +15 -14
- package/build/components/conversation/messages_disabled_banners.js.map +1 -1
- package/build/components/conversation/system_message.d.ts +2 -1
- package/build/components/conversation/system_message.d.ts.map +1 -1
- package/build/components/conversation/system_message.js +133 -100
- package/build/components/conversation/system_message.js.map +1 -1
- package/build/components/conversations/archived_results_footer.d.ts +6 -0
- package/build/components/conversations/archived_results_footer.d.ts.map +1 -0
- package/build/components/conversations/archived_results_footer.js +51 -0
- package/build/components/conversations/archived_results_footer.js.map +1 -0
- package/build/components/conversations/conversation_actions.d.ts.map +1 -1
- package/build/components/conversations/conversation_actions.js +241 -206
- package/build/components/conversations/conversation_actions.js.map +1 -1
- package/build/components/conversations/conversation_preview.d.ts.map +1 -1
- package/build/components/conversations/conversation_preview.js +102 -93
- package/build/components/conversations/conversation_preview.js.map +1 -1
- package/build/components/conversations/conversations.d.ts.map +1 -1
- package/build/components/conversations/conversations.js +66 -41
- package/build/components/conversations/conversations.js.map +1 -1
- package/build/components/conversations/conversations_blank_state.d.ts.map +1 -1
- package/build/components/conversations/conversations_blank_state.js +55 -12
- package/build/components/conversations/conversations_blank_state.js.map +1 -1
- package/build/components/conversations/filtered_results_footer.d.ts +2 -0
- package/build/components/conversations/filtered_results_footer.d.ts.map +1 -0
- package/build/components/conversations/filtered_results_footer.js +77 -0
- package/build/components/conversations/filtered_results_footer.js.map +1 -0
- package/build/components/display/action_button.d.ts.map +1 -1
- package/build/components/display/action_button.js +18 -16
- package/build/components/display/action_button.js.map +1 -1
- package/build/components/display/filter_dropdown.d.ts +14 -0
- package/build/components/display/filter_dropdown.d.ts.map +1 -0
- package/build/components/display/filter_dropdown.js +193 -0
- package/build/components/display/filter_dropdown.js.map +1 -0
- package/build/components/display/icon_tile.d.ts +13 -0
- package/build/components/display/icon_tile.d.ts.map +1 -0
- package/build/components/display/icon_tile.js +149 -0
- package/build/components/display/icon_tile.js.map +1 -0
- package/build/components/display/index.d.ts +2 -0
- package/build/components/display/index.d.ts.map +1 -1
- package/build/components/display/index.js +2 -0
- package/build/components/display/index.js.map +1 -1
- package/build/contexts/api_provider.d.ts.map +1 -1
- package/build/contexts/api_provider.js +21 -9
- package/build/contexts/api_provider.js.map +1 -1
- package/build/contexts/conversations_context.d.ts.map +1 -1
- package/build/contexts/conversations_context.js +21 -18
- package/build/contexts/conversations_context.js.map +1 -1
- package/build/hooks/groups/use_group_members_for_new_conversation.d.ts +196 -16
- package/build/hooks/groups/use_group_members_for_new_conversation.d.ts.map +1 -1
- package/build/hooks/groups/use_group_members_for_new_conversation.js +72 -58
- package/build/hooks/groups/use_group_members_for_new_conversation.js.map +1 -1
- package/build/hooks/index.d.ts +3 -0
- package/build/hooks/index.d.ts.map +1 -1
- package/build/hooks/index.js +3 -0
- package/build/hooks/index.js.map +1 -1
- package/build/hooks/use_animated_message_background_color.d.ts.map +1 -1
- package/build/hooks/use_animated_message_background_color.js +5 -3
- package/build/hooks/use_animated_message_background_color.js.map +1 -1
- package/build/hooks/use_api.d.ts +3 -2
- package/build/hooks/use_api.d.ts.map +1 -1
- package/build/hooks/use_api.js.map +1 -1
- package/build/hooks/use_can_browse_archived_conversations.d.ts +2 -0
- package/build/hooks/use_can_browse_archived_conversations.d.ts.map +1 -0
- package/build/hooks/use_can_browse_archived_conversations.js +29 -0
- package/build/hooks/use_can_browse_archived_conversations.js.map +1 -0
- package/build/hooks/use_clear_conversation_filters.d.ts +2 -0
- package/build/hooks/use_clear_conversation_filters.d.ts.map +1 -0
- package/build/hooks/use_clear_conversation_filters.js +25 -0
- package/build/hooks/use_clear_conversation_filters.js.map +1 -0
- package/build/hooks/use_conversation.d.ts +73 -1
- package/build/hooks/use_conversation.d.ts.map +1 -1
- package/build/hooks/use_conversation.js +130 -3
- package/build/hooks/use_conversation.js.map +1 -1
- package/build/hooks/use_conversation_messages.d.ts.map +1 -1
- package/build/hooks/use_conversation_messages.js +29 -26
- package/build/hooks/use_conversation_messages.js.map +1 -1
- package/build/hooks/use_current_person.d.ts.map +1 -1
- package/build/hooks/use_current_person.js +1 -1
- package/build/hooks/use_current_person.js.map +1 -1
- package/build/hooks/use_jolt.d.ts +1 -0
- package/build/hooks/use_jolt.d.ts.map +1 -1
- package/build/hooks/use_jolt.js +59 -0
- package/build/hooks/use_jolt.js.map +1 -1
- package/build/hooks/use_manual_archiving_enabled.d.ts +2 -0
- package/build/hooks/use_manual_archiving_enabled.d.ts.map +1 -0
- package/build/hooks/use_manual_archiving_enabled.js +18 -0
- package/build/hooks/use_manual_archiving_enabled.js.map +1 -0
- package/build/hooks/use_mark_latest_message_read.d.ts.map +1 -1
- package/build/hooks/use_mark_latest_message_read.js +3 -3
- package/build/hooks/use_mark_latest_message_read.js.map +1 -1
- package/build/hooks/use_reduce_motion.d.ts +2 -0
- package/build/hooks/use_reduce_motion.d.ts.map +1 -0
- package/build/hooks/use_reduce_motion.js +28 -0
- package/build/hooks/use_reduce_motion.js.map +1 -0
- package/build/hooks/use_unarchive_conversation_action.d.ts +7 -0
- package/build/hooks/use_unarchive_conversation_action.d.ts.map +1 -0
- package/build/hooks/use_unarchive_conversation_action.js +52 -0
- package/build/hooks/use_unarchive_conversation_action.js.map +1 -0
- package/build/screens/attachment_actions/attachment_actions_screen.d.ts.map +1 -1
- package/build/screens/attachment_actions/attachment_actions_screen.js +84 -61
- package/build/screens/attachment_actions/attachment_actions_screen.js.map +1 -1
- package/build/screens/conversation_details_screen.d.ts.map +1 -1
- package/build/screens/conversation_details_screen.js +490 -395
- package/build/screens/conversation_details_screen.js.map +1 -1
- package/build/screens/conversation_filters/components/conversation_filters.d.ts.map +1 -1
- package/build/screens/conversation_filters/components/conversation_filters.js +88 -18
- package/build/screens/conversation_filters/components/conversation_filters.js.map +1 -1
- package/build/screens/conversation_filters/components/rows.d.ts +9 -0
- package/build/screens/conversation_filters/components/rows.d.ts.map +1 -1
- package/build/screens/conversation_filters/components/rows.js +93 -1
- package/build/screens/conversation_filters/components/rows.js.map +1 -1
- package/build/screens/conversation_filters/context/conversation_filter_context.d.ts.map +1 -1
- package/build/screens/conversation_filters/context/conversation_filter_context.js +8 -4
- package/build/screens/conversation_filters/context/conversation_filter_context.js.map +1 -1
- package/build/screens/conversation_filters/filter_types.d.ts +1 -0
- package/build/screens/conversation_filters/filter_types.d.ts.map +1 -1
- package/build/screens/conversation_filters/filter_types.js +1 -0
- package/build/screens/conversation_filters/filter_types.js.map +1 -1
- package/build/screens/conversation_filters/screen_props.d.ts +1 -0
- package/build/screens/conversation_filters/screen_props.d.ts.map +1 -1
- package/build/screens/conversation_filters/screen_props.js.map +1 -1
- package/build/screens/conversation_filters_screen.d.ts +1 -0
- package/build/screens/conversation_filters_screen.d.ts.map +1 -1
- package/build/screens/conversation_filters_screen.js +79 -48
- package/build/screens/conversation_filters_screen.js.map +1 -1
- package/build/screens/conversation_new/components/groups_form.d.ts.map +1 -1
- package/build/screens/conversation_new/components/groups_form.js +37 -34
- package/build/screens/conversation_new/components/groups_form.js.map +1 -1
- package/build/screens/conversation_screen.d.ts.map +1 -1
- package/build/screens/conversation_screen.js +13 -10
- package/build/screens/conversation_screen.js.map +1 -1
- package/build/screens/conversations/components/filter_dropdown_list_header_component.d.ts +2 -0
- package/build/screens/conversations/components/filter_dropdown_list_header_component.d.ts.map +1 -0
- package/build/screens/conversations/components/filter_dropdown_list_header_component.js +580 -0
- package/build/screens/conversations/components/filter_dropdown_list_header_component.js.map +1 -0
- package/build/screens/conversations/components/list_header_component.d.ts.map +1 -1
- package/build/screens/conversations/components/list_header_component.js +95 -90
- package/build/screens/conversations/components/list_header_component.js.map +1 -1
- package/build/screens/conversations/components/search_list_header_component.d.ts.map +1 -1
- package/build/screens/conversations/components/search_list_header_component.js +100 -95
- package/build/screens/conversations/components/search_list_header_component.js.map +1 -1
- package/build/screens/conversations/conversations_screen.d.ts +1 -0
- package/build/screens/conversations/conversations_screen.d.ts.map +1 -1
- package/build/screens/conversations/conversations_screen.js +15 -2
- package/build/screens/conversations/conversations_screen.js.map +1 -1
- package/build/screens/design_system_screen.d.ts.map +1 -1
- package/build/screens/design_system_screen.js +275 -215
- package/build/screens/design_system_screen.js.map +1 -1
- package/build/screens/message_actions_screen.d.ts.map +1 -1
- package/build/screens/message_actions_screen.js +112 -92
- package/build/screens/message_actions_screen.js.map +1 -1
- package/build/screens/search/search_screen.d.ts.map +1 -1
- package/build/screens/search/search_screen.js +10 -2
- package/build/screens/search/search_screen.js.map +1 -1
- package/build/screens/send_giphy_screen.d.ts +1 -0
- package/build/screens/send_giphy_screen.d.ts.map +1 -1
- package/build/screens/send_giphy_screen.js +108 -106
- package/build/screens/send_giphy_screen.js.map +1 -1
- package/build/screens/team_conversation_screen.js +1 -1
- package/build/screens/team_conversation_screen.js.map +1 -1
- package/build/types/jolt_events/message_events.d.ts +2 -0
- package/build/types/jolt_events/message_events.d.ts.map +1 -1
- package/build/types/jolt_events/message_events.js.map +1 -1
- package/build/types/resources/conversation.d.ts +2 -2
- package/build/types/resources/conversation.d.ts.map +1 -1
- package/build/types/resources/conversation.js.map +1 -1
- package/build/types/resources/member_ability.d.ts +2 -0
- package/build/types/resources/member_ability.d.ts.map +1 -1
- package/build/types/resources/member_ability.js.map +1 -1
- package/build/types/resources/message.d.ts +2 -0
- package/build/types/resources/message.d.ts.map +1 -1
- package/build/types/resources/message.js.map +1 -1
- package/build/types/resources/person.d.ts +1 -0
- package/build/types/resources/person.d.ts.map +1 -1
- package/build/types/resources/person.js.map +1 -1
- package/build/utils/archived_conversation.d.ts +5 -0
- package/build/utils/archived_conversation.d.ts.map +1 -0
- package/build/utils/archived_conversation.js +5 -0
- package/build/utils/archived_conversation.js.map +1 -0
- package/build/utils/date.d.ts +1 -0
- package/build/utils/date.d.ts.map +1 -1
- package/build/utils/date.js +9 -0
- package/build/utils/date.js.map +1 -1
- package/build/utils/index.d.ts +1 -0
- package/build/utils/index.d.ts.map +1 -1
- package/build/utils/index.js +1 -0
- package/build/utils/index.js.map +1 -1
- package/build/utils/jolt/transform_message_event_data_to_message_resource.d.ts.map +1 -1
- package/build/utils/jolt/transform_message_event_data_to_message_resource.js +2 -1
- package/build/utils/jolt/transform_message_event_data_to_message_resource.js.map +1 -1
- package/build/utils/native_adapters/configuration.d.ts +3 -0
- package/build/utils/native_adapters/configuration.d.ts.map +1 -1
- package/build/utils/native_adapters/configuration.js +3 -0
- package/build/utils/native_adapters/configuration.js.map +1 -1
- package/build/utils/native_adapters/index.d.ts +1 -0
- package/build/utils/native_adapters/index.d.ts.map +1 -1
- package/build/utils/native_adapters/index.js +1 -0
- package/build/utils/native_adapters/index.js.map +1 -1
- package/build/utils/native_adapters/menu.d.ts +41 -0
- package/build/utils/native_adapters/menu.d.ts.map +1 -0
- package/build/utils/native_adapters/menu.js +56 -0
- package/build/utils/native_adapters/menu.js.map +1 -0
- package/build/utils/request/conversation.d.ts +2 -1
- package/build/utils/request/conversation.d.ts.map +1 -1
- package/build/utils/request/conversation.js +4 -1
- package/build/utils/request/conversation.js.map +1 -1
- package/build/utils/request/messages_data_options.d.ts.map +1 -1
- package/build/utils/request/messages_data_options.js +1 -1
- package/build/utils/request/messages_data_options.js.map +1 -1
- package/build/utils/system_messages.d.ts +7 -5
- package/build/utils/system_messages.d.ts.map +1 -1
- package/build/utils/system_messages.js +10 -2
- package/build/utils/system_messages.js.map +1 -1
- package/build/vendor/font_awesome_pro_solid_icons.d.ts +1 -0
- package/build/vendor/font_awesome_pro_solid_icons.d.ts.map +1 -1
- package/build/vendor/font_awesome_pro_solid_icons.js +5 -0
- package/build/vendor/font_awesome_pro_solid_icons.js.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/hooks/use_can_browse_archived_conversations.test.tsx +98 -0
- package/src/__tests__/screens/conversation_filters/components/conversation_filters.test.tsx +132 -0
- package/src/__tests__/screens/conversation_filters_screen.test.tsx +76 -0
- package/src/__tests__/screens/conversation_new/components/services_form.test.tsx +1 -0
- package/src/__tests__/screens/message_actions_screen.test.tsx +65 -2
- package/src/__tests__/screens/send_giphy_screen.test.tsx +102 -0
- package/src/__tests__/utils/system_messages.ts +19 -32
- package/src/components/conversation/__tests__/message_list.test.tsx +68 -2
- package/src/components/conversation/__tests__/system_message.test.tsx +106 -0
- package/src/components/conversation/jump_to_bottom_button.tsx +4 -3
- package/src/components/conversation/message.tsx +3 -0
- package/src/components/conversation/message_form.tsx +3 -1
- package/src/components/conversation/message_list.tsx +9 -1
- package/src/components/conversation/message_reaction.tsx +7 -2
- package/src/components/conversation/messages_disabled_banners.tsx +17 -18
- package/src/components/conversation/system_message.tsx +44 -16
- package/src/components/conversations/__tests__/conversation_actions.test.tsx +67 -0
- package/src/components/conversations/__tests__/conversation_preview.test.tsx +105 -0
- package/src/components/conversations/__tests__/conversations.test.tsx +93 -0
- package/src/components/conversations/__tests__/conversations_blank_state.test.tsx +73 -0
- package/src/components/conversations/archived_results_footer.tsx +36 -0
- package/src/components/conversations/conversation_actions.tsx +27 -19
- package/src/components/conversations/conversation_preview.tsx +39 -10
- package/src/components/conversations/conversations.tsx +26 -3
- package/src/components/conversations/conversations_blank_state.tsx +21 -0
- package/src/components/conversations/filtered_results_footer.tsx +38 -0
- package/src/components/display/__tests__/filter_dropdown.test.tsx +114 -0
- package/src/components/display/__tests__/icon_tile.test.tsx +16 -0
- package/src/components/display/action_button.tsx +4 -3
- package/src/components/display/filter_dropdown.tsx +124 -0
- package/src/components/display/icon_tile.tsx +67 -0
- package/src/components/display/index.ts +2 -0
- package/src/contexts/api_provider.tsx +8 -0
- package/src/contexts/conversations_context.tsx +2 -1
- package/src/hooks/__tests__/use_conversation_archive.test.tsx +152 -0
- package/src/hooks/__tests__/use_conversation_jolt_events.test.tsx +74 -0
- package/src/hooks/__tests__/use_conversation_messages.test.tsx +10 -2
- package/src/hooks/__tests__/use_jolt.test.tsx +100 -1
- package/src/hooks/__tests__/use_mark_latest_message_read.test.tsx +21 -7
- package/src/hooks/__tests__/use_reduce_motion.test.tsx +61 -0
- package/src/hooks/__tests__/use_unarchive_conversation_action.test.tsx +67 -0
- package/src/hooks/groups/use_group_members_for_new_conversation.ts +21 -16
- package/src/hooks/index.ts +3 -0
- package/src/hooks/use_animated_message_background_color.ts +6 -3
- package/src/hooks/use_api.ts +9 -2
- package/src/hooks/use_can_browse_archived_conversations.ts +17 -0
- package/src/hooks/use_clear_conversation_filters.ts +16 -0
- package/src/hooks/use_conversation.ts +44 -3
- package/src/hooks/use_conversation_messages.ts +7 -5
- package/src/hooks/use_current_person.ts +1 -0
- package/src/hooks/use_jolt.ts +57 -1
- package/src/hooks/use_manual_archiving_enabled.ts +10 -0
- package/src/hooks/use_mark_latest_message_read.ts +4 -3
- package/src/hooks/use_reduce_motion.ts +19 -0
- package/src/hooks/use_unarchive_conversation_action.ts +24 -0
- package/src/screens/__tests__/conversation_details_screen.test.tsx +305 -0
- package/src/screens/attachment_actions/__tests__/attachment_actions_screen.test.tsx +72 -0
- package/src/screens/attachment_actions/attachment_actions_screen.tsx +3 -1
- package/src/screens/conversation_details_screen.tsx +80 -16
- package/src/screens/conversation_filters/__tests__/conversation_filters.test.tsx +101 -0
- package/src/screens/conversation_filters/components/conversation_filters.tsx +40 -7
- package/src/screens/conversation_filters/components/rows.tsx +29 -1
- package/src/screens/conversation_filters/context/conversation_filter_context.tsx +4 -0
- package/src/screens/conversation_filters/filter_types.ts +1 -0
- package/src/screens/conversation_filters/screen_props.ts +1 -0
- package/src/screens/conversation_filters_screen.tsx +25 -5
- package/src/screens/conversation_new/components/groups_form.tsx +1 -0
- package/src/screens/conversation_screen.tsx +4 -2
- package/src/screens/conversations/__tests__/conversations_screen.test.tsx +52 -0
- package/src/screens/conversations/components/__tests__/filter_dropdown_list_header_component.test.tsx +259 -0
- package/src/screens/conversations/components/__tests__/list_header_component.test.tsx +21 -4
- package/src/screens/conversations/components/filter_dropdown_list_header_component.tsx +286 -0
- package/src/screens/conversations/components/list_header_component.tsx +17 -12
- package/src/screens/conversations/components/search_list_header_component.tsx +17 -12
- package/src/screens/conversations/conversations_screen.tsx +15 -2
- package/src/screens/design_system_screen.tsx +26 -0
- package/src/screens/message_actions_screen.tsx +29 -20
- package/src/screens/search/__tests__/search_screen.test.tsx +13 -1
- package/src/screens/search/search_screen.tsx +8 -2
- package/src/screens/send_giphy_screen.tsx +6 -3
- package/src/screens/team_conversation_screen.tsx +1 -1
- package/src/types/jolt_events/message_events.ts +2 -0
- package/src/types/resources/conversation.ts +2 -2
- package/src/types/resources/member_ability.ts +2 -0
- package/src/types/resources/message.ts +3 -0
- package/src/types/resources/person.ts +1 -0
- package/src/utils/__tests__/conversation.test.ts +1 -0
- package/src/utils/__tests__/date.test.ts +23 -0
- package/src/utils/__tests__/group_messages.test.ts +1 -0
- package/src/utils/archived_conversation.ts +13 -0
- package/src/utils/date.ts +8 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/jolt/__tests__/transform_message_event_data_to_message_resource.test.ts +35 -0
- package/src/utils/jolt/transform_message_event_data_to_message_resource.ts +1 -0
- package/src/utils/native_adapters/__tests__/menu.test.tsx +46 -0
- package/src/utils/native_adapters/configuration.ts +5 -0
- package/src/utils/native_adapters/index.ts +1 -0
- package/src/utils/native_adapters/menu.tsx +70 -0
- package/src/utils/request/conversation.ts +4 -0
- package/src/utils/request/messages_data_options.ts +1 -0
- package/src/utils/system_messages.ts +19 -10
- 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 === ''
|
|
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
|
|
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
|
-
|
|
305
|
+
archived
|
|
279
306
|
? {
|
|
280
|
-
type: SectionTypes.
|
|
307
|
+
type: SectionTypes.view,
|
|
281
308
|
data: {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
+
})
|