@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,67 @@
|
|
|
1
|
+
import type { IconDefinition } from '@fortawesome/fontawesome-svg-core'
|
|
2
|
+
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { StyleSheet, View } from 'react-native'
|
|
5
|
+
import { useFontScale, useTheme } from '../../hooks'
|
|
6
|
+
import { MAX_FONT_SIZE_MULTIPLIER } from '../../utils'
|
|
7
|
+
import AvatarPrimitive, { type AvatarRootProps } from '../primitive/avatar_primitive'
|
|
8
|
+
|
|
9
|
+
const ICON_SIZE_PX = 16
|
|
10
|
+
const SQUARE_BORDER_RADIUS_PX = 4
|
|
11
|
+
|
|
12
|
+
interface IconTileProps {
|
|
13
|
+
icon: IconDefinition
|
|
14
|
+
shape: 'circle' | 'square'
|
|
15
|
+
size?: AvatarRootProps['size']
|
|
16
|
+
maxFontSizeMultiplier?: AvatarRootProps['maxFontSizeMultiplier']
|
|
17
|
+
style?: AvatarRootProps['style']
|
|
18
|
+
testID?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function IconTile({
|
|
22
|
+
icon,
|
|
23
|
+
shape,
|
|
24
|
+
size = 'lg',
|
|
25
|
+
maxFontSizeMultiplier = MAX_FONT_SIZE_MULTIPLIER,
|
|
26
|
+
style,
|
|
27
|
+
testID,
|
|
28
|
+
}: IconTileProps) {
|
|
29
|
+
const fontScale = useFontScale({ maxFontSizeMultiplier })
|
|
30
|
+
const styles = useStyles({ fontScale })
|
|
31
|
+
|
|
32
|
+
const tile = (
|
|
33
|
+
<View
|
|
34
|
+
style={[styles.tile, shape === 'square' && styles.square]}
|
|
35
|
+
testID={testID}
|
|
36
|
+
accessibilityElementsHidden
|
|
37
|
+
>
|
|
38
|
+
<FontAwesomeIcon icon={icon} size={ICON_SIZE_PX * fontScale} color={styles.icon.color} />
|
|
39
|
+
</View>
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<AvatarPrimitive.Root size={size} maxFontSizeMultiplier={maxFontSizeMultiplier} style={style}>
|
|
44
|
+
{shape === 'circle' ? <AvatarPrimitive.Mask>{tile}</AvatarPrimitive.Mask> : tile}
|
|
45
|
+
</AvatarPrimitive.Root>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const useStyles = ({ fontScale }: { fontScale: number }) => {
|
|
50
|
+
const { colors } = useTheme()
|
|
51
|
+
|
|
52
|
+
return StyleSheet.create({
|
|
53
|
+
tile: {
|
|
54
|
+
flex: 1,
|
|
55
|
+
alignItems: 'center',
|
|
56
|
+
justifyContent: 'center',
|
|
57
|
+
backgroundColor: colors.fillColorNeutral070,
|
|
58
|
+
},
|
|
59
|
+
square: {
|
|
60
|
+
borderRadius: SQUARE_BORDER_RADIUS_PX * fontScale,
|
|
61
|
+
overflow: 'hidden',
|
|
62
|
+
},
|
|
63
|
+
icon: {
|
|
64
|
+
color: colors.iconColorDefaultPrimary,
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
}
|
|
@@ -4,6 +4,7 @@ export * from './badge'
|
|
|
4
4
|
export * from './conversation_avatar'
|
|
5
5
|
export * from './emoji_avatar'
|
|
6
6
|
export * from './icon_avatar'
|
|
7
|
+
export * from './icon_tile'
|
|
7
8
|
export * from './banner_collapsible'
|
|
8
9
|
export * from './banner'
|
|
9
10
|
export * from './button'
|
|
@@ -23,6 +24,7 @@ export * from './text_button'
|
|
|
23
24
|
export * from './text_inline_button'
|
|
24
25
|
export * from './text'
|
|
25
26
|
export * from './toggle_button'
|
|
27
|
+
export * from './filter_dropdown'
|
|
26
28
|
export * from './keyboard_scroll_view'
|
|
27
29
|
export * from './keyboard_view'
|
|
28
30
|
export * from './platform_modal_header_buttons'
|
|
@@ -11,6 +11,7 @@ import { ApiClient, useApiClient } from '../hooks/use_api_client'
|
|
|
11
11
|
import { useAppState } from '../hooks/use_app_state'
|
|
12
12
|
import { appGrantsQueryOptions } from '../hooks/use_chat_permissions'
|
|
13
13
|
import { featuresQueryOptions } from '../hooks/use_features'
|
|
14
|
+
import { useJoltForegroundPing } from '../hooks/use_jolt'
|
|
14
15
|
import { RequestQueryKey } from '../hooks/use_suspense_api'
|
|
15
16
|
import { emitAuthRefresh } from '../utils/auth_events'
|
|
16
17
|
import { ResponseError } from '../utils/response_error'
|
|
@@ -73,6 +74,7 @@ export function ApiProvider({ children }: ViewProps) {
|
|
|
73
74
|
return (
|
|
74
75
|
<QueryClientProvider client={chatQueryClient}>
|
|
75
76
|
{session.isAuthenticated && <PrefetchQueries />}
|
|
77
|
+
<JoltForegroundPing />
|
|
76
78
|
{children}
|
|
77
79
|
</QueryClientProvider>
|
|
78
80
|
)
|
|
@@ -90,6 +92,12 @@ const PrefetchQueries = () => {
|
|
|
90
92
|
return null
|
|
91
93
|
}
|
|
92
94
|
|
|
95
|
+
const JoltForegroundPing = () => {
|
|
96
|
+
useJoltForegroundPing()
|
|
97
|
+
|
|
98
|
+
return null
|
|
99
|
+
}
|
|
100
|
+
|
|
93
101
|
export function useFocusManager() {
|
|
94
102
|
const appState = useAppState()
|
|
95
103
|
|
|
@@ -45,13 +45,14 @@ export const ConversationsContextProvider = ({
|
|
|
45
45
|
opts?: ApiGetQueryOptions
|
|
46
46
|
}>) => {
|
|
47
47
|
const [activeConversationId, setActiveConversationId] = useState<number | undefined>()
|
|
48
|
-
const { chat_group_graph_id, group_source_app_name, show_direct_messages } = args
|
|
48
|
+
const { chat_group_graph_id, group_source_app_name, show_direct_messages, show_archived } = args
|
|
49
49
|
|
|
50
50
|
const query = useConversations(
|
|
51
51
|
{
|
|
52
52
|
chat_group_graph_id,
|
|
53
53
|
group_source_app_name,
|
|
54
54
|
show_direct_messages,
|
|
55
|
+
show_archived,
|
|
55
56
|
},
|
|
56
57
|
paginatorOptions
|
|
57
58
|
)
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { QueryClientProvider, useQuery } from '@tanstack/react-query'
|
|
2
|
+
import { act, renderHook, waitFor } from '@testing-library/react-native'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { Alert } from 'react-native'
|
|
5
|
+
import { buildTestQueryClient } from '../../__utils__/query_client'
|
|
6
|
+
import { getConversationsRequestArgs } from '../../utils/request/conversation'
|
|
7
|
+
import { getConversationRequestArgs, useConversationArchive } from '../use_conversation'
|
|
8
|
+
import { currentPersonQueryKey } from '../use_current_person'
|
|
9
|
+
import { getRequestQueryKey } from '../use_suspense_api'
|
|
10
|
+
|
|
11
|
+
const mockPost = jest.fn()
|
|
12
|
+
jest.mock('../use_api_client', () => ({
|
|
13
|
+
useApiClient: () => ({ chat: { post: mockPost } }),
|
|
14
|
+
}))
|
|
15
|
+
|
|
16
|
+
const conversation_id = 7
|
|
17
|
+
const conversationKey = getRequestQueryKey(getConversationRequestArgs({ conversation_id }))
|
|
18
|
+
const conversationsKey = getRequestQueryKey(getConversationsRequestArgs({ gids: 'all' }))
|
|
19
|
+
|
|
20
|
+
const buildQueryClient = () =>
|
|
21
|
+
buildTestQueryClient({ defaultOptions: { queries: { gcTime: Infinity } } })
|
|
22
|
+
|
|
23
|
+
let queryClient = buildQueryClient()
|
|
24
|
+
|
|
25
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
26
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
const isInvalidated = (queryKey: unknown[]) =>
|
|
30
|
+
queryClient.getQueryCache().find({ queryKey, exact: true })?.state.isInvalidated
|
|
31
|
+
|
|
32
|
+
describe('useConversationArchive', () => {
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
queryClient = buildQueryClient()
|
|
35
|
+
queryClient.setQueryData(conversationKey, { data: { id: conversation_id } })
|
|
36
|
+
queryClient.setQueryData(conversationsKey, { data: [] })
|
|
37
|
+
queryClient.setQueryData(currentPersonQueryKey, {
|
|
38
|
+
data: { id: 1, canBrowseArchivedConversations: false },
|
|
39
|
+
})
|
|
40
|
+
mockPost.mockReset()
|
|
41
|
+
jest.spyOn(Alert, 'alert').mockImplementation(() => {})
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
jest.restoreAllMocks()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it.each([
|
|
49
|
+
[true, 'archive'],
|
|
50
|
+
[false, 'unarchive'],
|
|
51
|
+
])(
|
|
52
|
+
'posts archived=%s to the %s action, then invalidates the conversation and list',
|
|
53
|
+
async (archived, action) => {
|
|
54
|
+
mockPost.mockResolvedValue({ data: { id: conversation_id } })
|
|
55
|
+
const { result } = renderHook(() => useConversationArchive({ conversation_id }), { wrapper })
|
|
56
|
+
|
|
57
|
+
await act(() => result.current.mutateAsync(archived))
|
|
58
|
+
|
|
59
|
+
expect(mockPost).toHaveBeenCalledWith({
|
|
60
|
+
url: `/me/conversations/7/${action}`,
|
|
61
|
+
data: { data: { type: 'Conversation', attributes: {} } },
|
|
62
|
+
})
|
|
63
|
+
expect(isInvalidated(conversationKey)).toBe(true)
|
|
64
|
+
expect(isInvalidated(conversationsKey)).toBe(true)
|
|
65
|
+
expect(isInvalidated(currentPersonQueryKey)).toBe(false)
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
it.each([
|
|
70
|
+
[true, true],
|
|
71
|
+
[false, false],
|
|
72
|
+
])(
|
|
73
|
+
'sets archive access in the person cache to %s after archived=%s',
|
|
74
|
+
async (archived, canBrowse) => {
|
|
75
|
+
mockPost.mockResolvedValue({ data: { id: conversation_id } })
|
|
76
|
+
const { result } = renderHook(() => useConversationArchive({ conversation_id }), { wrapper })
|
|
77
|
+
|
|
78
|
+
await act(() => result.current.mutateAsync(archived))
|
|
79
|
+
|
|
80
|
+
expect(queryClient.getQueryData(currentPersonQueryKey)).toEqual({
|
|
81
|
+
data: { id: 1, canBrowseArchivedConversations: canBrowse },
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
it('stays pending until the conversation refetch finishes', async () => {
|
|
87
|
+
mockPost.mockResolvedValue({ data: { id: conversation_id } })
|
|
88
|
+
let finishRefetch = () => {}
|
|
89
|
+
const { result } = renderHook(
|
|
90
|
+
() => {
|
|
91
|
+
useQuery({
|
|
92
|
+
queryKey: conversationKey,
|
|
93
|
+
queryFn: () =>
|
|
94
|
+
new Promise(resolve => {
|
|
95
|
+
finishRefetch = () => resolve({ data: { id: conversation_id } })
|
|
96
|
+
}),
|
|
97
|
+
staleTime: Infinity,
|
|
98
|
+
})
|
|
99
|
+
return useConversationArchive({ conversation_id })
|
|
100
|
+
},
|
|
101
|
+
{ wrapper }
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
let archiving = Promise.resolve()
|
|
105
|
+
await act(async () => {
|
|
106
|
+
archiving = result.current.mutateAsync(true).then(() => {})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
await waitFor(() => expect(result.current.isPending).toBe(true))
|
|
110
|
+
|
|
111
|
+
await act(async () => {
|
|
112
|
+
finishRefetch()
|
|
113
|
+
await archiving
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
await waitFor(() => expect(result.current.isPending).toBe(false))
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it("doesn't wait on the list refetch", async () => {
|
|
120
|
+
mockPost.mockResolvedValue({ data: { id: conversation_id } })
|
|
121
|
+
const { result } = renderHook(
|
|
122
|
+
() => {
|
|
123
|
+
useQuery({
|
|
124
|
+
queryKey: conversationsKey,
|
|
125
|
+
queryFn: () => new Promise(() => {}),
|
|
126
|
+
staleTime: Infinity,
|
|
127
|
+
})
|
|
128
|
+
return useConversationArchive({ conversation_id })
|
|
129
|
+
},
|
|
130
|
+
{ wrapper }
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
await act(() => result.current.mutateAsync(true))
|
|
134
|
+
|
|
135
|
+
expect(result.current.isPending).toBe(false)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it.each([
|
|
139
|
+
[true, 'archive'],
|
|
140
|
+
[false, 'unarchive'],
|
|
141
|
+
])('alerts when archived=%s fails', async (archived, action) => {
|
|
142
|
+
mockPost.mockRejectedValue(new Error('nope'))
|
|
143
|
+
const { result } = renderHook(() => useConversationArchive({ conversation_id }), { wrapper })
|
|
144
|
+
|
|
145
|
+
await act(() => result.current.mutateAsync(archived).catch(() => {}))
|
|
146
|
+
|
|
147
|
+
expect(Alert.alert).toHaveBeenCalledWith(
|
|
148
|
+
'Oops',
|
|
149
|
+
`Couldn't ${action} this conversation. Please try again.`
|
|
150
|
+
)
|
|
151
|
+
})
|
|
152
|
+
})
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
2
|
+
import { renderHook } from '@testing-library/react-native'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { buildTestQueryClient } from '../../__utils__/query_client'
|
|
5
|
+
import type { ApiResource, ConversationResource } from '../../types'
|
|
6
|
+
import { getConversationRequestArgs } from '../use_conversation'
|
|
7
|
+
import { useConversationJoltEvents } from '../use_conversation_jolt_events'
|
|
8
|
+
import { getRequestQueryKey } from '../use_suspense_api'
|
|
9
|
+
|
|
10
|
+
const handlers: Record<string, (event: unknown) => void> = {}
|
|
11
|
+
jest.mock('../use_jolt', () => ({
|
|
12
|
+
useJoltChannel: () => 'channel',
|
|
13
|
+
useJoltEvent: (_channel: unknown, name: string, handler: (event: unknown) => void) => {
|
|
14
|
+
handlers[name] = handler
|
|
15
|
+
},
|
|
16
|
+
}))
|
|
17
|
+
jest.mock('../use_current_person', () => ({ useCurrentPerson: () => ({ id: 1 }) }))
|
|
18
|
+
jest.mock('../use_api_client', () => ({ useApiClient: () => ({ chat: {} }) }))
|
|
19
|
+
|
|
20
|
+
const conversationId = 7
|
|
21
|
+
const queryKey = getRequestQueryKey(getConversationRequestArgs({ conversation_id: conversationId }))
|
|
22
|
+
let queryClient: QueryClient
|
|
23
|
+
|
|
24
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
25
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
const archivedAttributes = {
|
|
29
|
+
disabled: true,
|
|
30
|
+
disabled_reason: 'archived',
|
|
31
|
+
disabled_message: 'This conversation is archived.',
|
|
32
|
+
}
|
|
33
|
+
const activeAttributes = { disabled: false, disabled_reason: null, disabled_message: null }
|
|
34
|
+
|
|
35
|
+
const seed = (conversation: Partial<ConversationResource>) =>
|
|
36
|
+
queryClient.setQueryData<ApiResource<ConversationResource>>(queryKey, {
|
|
37
|
+
data: { type: 'Conversation', id: conversationId, title: 'Youth', ...conversation },
|
|
38
|
+
} as ApiResource<ConversationResource>)
|
|
39
|
+
|
|
40
|
+
const cached = () => queryClient.getQueryData<ApiResource<ConversationResource>>(queryKey)!.data
|
|
41
|
+
|
|
42
|
+
const conversationUpdated = (attributes: Record<string, unknown>) =>
|
|
43
|
+
handlers['conversation.updated']({
|
|
44
|
+
event: 'conversation.updated',
|
|
45
|
+
data: { data: { id: conversationId, ...attributes } },
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
queryClient = buildTestQueryClient()
|
|
50
|
+
renderHook(() => useConversationJoltEvents({ conversationId }), { wrapper })
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
describe('useConversationJoltEvents on conversation.updated', () => {
|
|
54
|
+
it('switches the cached conversation into the archived state', () => {
|
|
55
|
+
seed({ disabled: false, disabledReason: null, disabledMessage: null })
|
|
56
|
+
|
|
57
|
+
conversationUpdated(archivedAttributes)
|
|
58
|
+
|
|
59
|
+
expect(cached()).toMatchObject({
|
|
60
|
+
title: 'Youth',
|
|
61
|
+
disabled: true,
|
|
62
|
+
disabledReason: 'archived',
|
|
63
|
+
disabledMessage: 'This conversation is archived.',
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('switches the cached conversation back out of the archived state', () => {
|
|
68
|
+
seed({ disabled: true, disabledReason: 'archived', disabledMessage: 'Archived' })
|
|
69
|
+
|
|
70
|
+
conversationUpdated(activeAttributes)
|
|
71
|
+
|
|
72
|
+
expect(cached()).toMatchObject({ disabled: false, disabledReason: null, disabledMessage: null })
|
|
73
|
+
})
|
|
74
|
+
})
|
|
@@ -101,8 +101,8 @@ describe('useConversationMessages', () => {
|
|
|
101
101
|
await waitFor(() => expect(result.current.messages.map(m => m.id)).toContain(anchor))
|
|
102
102
|
})
|
|
103
103
|
|
|
104
|
-
it('reuses a cached window that
|
|
105
|
-
seed(
|
|
104
|
+
it('reuses a cached window that was opened at the anchor', async () => {
|
|
105
|
+
seed(anchoredPageParam(anchor), ['Z', anchor], behindMeta)
|
|
106
106
|
|
|
107
107
|
const { result } = render({ conversation_id, anchor })
|
|
108
108
|
|
|
@@ -110,6 +110,14 @@ describe('useConversationMessages', () => {
|
|
|
110
110
|
expect(mockGet).not.toHaveBeenCalled()
|
|
111
111
|
})
|
|
112
112
|
|
|
113
|
+
it('refetches around the anchor when a live window still holds a stale copy of it', async () => {
|
|
114
|
+
seed(livePageParam, ['Z', anchor])
|
|
115
|
+
|
|
116
|
+
render({ conversation_id, anchor })
|
|
117
|
+
|
|
118
|
+
await waitFor(() => expect(requestedWhere()).toEqual([{ id_around: anchor }]))
|
|
119
|
+
})
|
|
120
|
+
|
|
113
121
|
it('keeps a deleted anchor so the list can mark where it was', async () => {
|
|
114
122
|
const deleted = {
|
|
115
123
|
...message(anchor),
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import JoltClient from '@planningcenter/jolt-client'
|
|
2
|
+
import { JoltSubscription } from '@planningcenter/jolt-client/dist/types/JoltSubscription'
|
|
1
3
|
import { QueryClientProvider } from '@tanstack/react-query'
|
|
2
4
|
import { render, waitFor } from '@testing-library/react-native'
|
|
3
5
|
import React, { PropsWithChildren, Suspense } from 'react'
|
|
6
|
+
import { AppState, AppStateStatus } from 'react-native'
|
|
4
7
|
import { buildTestQueryClient } from '../../__utils__/query_client'
|
|
5
8
|
import { ChatContext, ChatContextValue } from '../../contexts/chat_context'
|
|
6
9
|
import { Session } from '../../utils'
|
|
7
10
|
import { Client } from '../../utils/client'
|
|
8
11
|
import { ResponseError } from '../../utils/response_error'
|
|
9
|
-
import { useJoltClient } from '../use_jolt'
|
|
12
|
+
import { useJoltClient, useJoltForegroundPing } from '../use_jolt'
|
|
10
13
|
|
|
11
14
|
let caught: unknown
|
|
12
15
|
|
|
@@ -68,3 +71,99 @@ describe('useJoltClient', () => {
|
|
|
68
71
|
expect((caught as ResponseError).status).toBe(401)
|
|
69
72
|
})
|
|
70
73
|
})
|
|
74
|
+
|
|
75
|
+
describe('useJoltForegroundPing', () => {
|
|
76
|
+
const addEventListener = AppState.addEventListener as jest.Mock
|
|
77
|
+
const confirmedSubscription = {} as JoltSubscription
|
|
78
|
+
|
|
79
|
+
const PingProbe = () => {
|
|
80
|
+
useJoltForegroundPing()
|
|
81
|
+
return null
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const renderForegroundPing = (joltClient: Partial<JoltClient>, { mounts = 1 } = {}) => {
|
|
85
|
+
const queryClient = buildTestQueryClient({ defaultOptions: { queries: { gcTime: Infinity } } })
|
|
86
|
+
queryClient.setQueryData(['jolt-client', 'wss://jolt.test', 'a-real-token'], joltClient)
|
|
87
|
+
|
|
88
|
+
const result = render(
|
|
89
|
+
<QueryClientProvider client={queryClient}>
|
|
90
|
+
{Array.from({ length: mounts }, (_, index) => (
|
|
91
|
+
<PingProbe key={index} />
|
|
92
|
+
))}
|
|
93
|
+
</QueryClientProvider>
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
const [, onChange] = addEventListener.mock.calls.at(-1) as [
|
|
97
|
+
string,
|
|
98
|
+
(_state: AppStateStatus) => void,
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
return { onChange, unmount: result.unmount }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
beforeEach(() => {
|
|
105
|
+
addEventListener.mockClear()
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('pings the jolt client once when the app returns to the foreground', () => {
|
|
109
|
+
const ping = jest.fn()
|
|
110
|
+
const { onChange } = renderForegroundPing({ ping, subscriptions: [confirmedSubscription] })
|
|
111
|
+
|
|
112
|
+
onChange('background')
|
|
113
|
+
onChange('active')
|
|
114
|
+
|
|
115
|
+
expect(ping).toHaveBeenCalledTimes(1)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('registers one listener and pings once no matter how many providers mount it', () => {
|
|
119
|
+
const ping = jest.fn()
|
|
120
|
+
const remove = jest.fn()
|
|
121
|
+
addEventListener.mockReturnValueOnce({ remove })
|
|
122
|
+
const { onChange, unmount } = renderForegroundPing(
|
|
123
|
+
{ ping, subscriptions: [confirmedSubscription] },
|
|
124
|
+
{ mounts: 3 }
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
onChange('background')
|
|
128
|
+
onChange('active')
|
|
129
|
+
|
|
130
|
+
expect(addEventListener).toHaveBeenCalledTimes(1)
|
|
131
|
+
expect(ping).toHaveBeenCalledTimes(1)
|
|
132
|
+
|
|
133
|
+
unmount()
|
|
134
|
+
|
|
135
|
+
expect(remove).toHaveBeenCalledTimes(1)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('does not ping when there are no confirmed subscriptions', () => {
|
|
139
|
+
const ping = jest.fn()
|
|
140
|
+
const { onChange } = renderForegroundPing({ ping, subscriptions: [] })
|
|
141
|
+
|
|
142
|
+
onChange('background')
|
|
143
|
+
onChange('active')
|
|
144
|
+
|
|
145
|
+
expect(ping).not.toHaveBeenCalled()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('does not ping when the app leaves the foreground', () => {
|
|
149
|
+
const ping = jest.fn()
|
|
150
|
+
const { onChange } = renderForegroundPing({ ping, subscriptions: [confirmedSubscription] })
|
|
151
|
+
|
|
152
|
+
onChange('inactive')
|
|
153
|
+
onChange('background')
|
|
154
|
+
|
|
155
|
+
expect(ping).not.toHaveBeenCalled()
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('swallows errors from pinging a closed socket', () => {
|
|
159
|
+
const ping = jest.fn(() => {
|
|
160
|
+
throw new Error('No WebSocket')
|
|
161
|
+
})
|
|
162
|
+
const { onChange } = renderForegroundPing({ ping, subscriptions: [confirmedSubscription] })
|
|
163
|
+
|
|
164
|
+
onChange('background')
|
|
165
|
+
|
|
166
|
+
expect(() => onChange('active')).not.toThrow()
|
|
167
|
+
expect(ping).toHaveBeenCalledTimes(1)
|
|
168
|
+
})
|
|
169
|
+
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { renderHook } from '@testing-library/react-native'
|
|
1
|
+
import { act, renderHook } from '@testing-library/react-native'
|
|
2
2
|
import type { ConversationResource } from '../../types'
|
|
3
3
|
import { useMarkLatestMessageRead } from '../use_mark_latest_message_read'
|
|
4
4
|
|
|
@@ -19,15 +19,29 @@ describe('useMarkLatestMessageRead', () => {
|
|
|
19
19
|
expect(mockMarkRead).toHaveBeenCalledTimes(1)
|
|
20
20
|
})
|
|
21
21
|
|
|
22
|
-
it('
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
it('marks read once on mount even while disabled', () => {
|
|
23
|
+
renderHook(() => useMarkLatestMessageRead({ conversation, messages: [], enabled: false }))
|
|
24
|
+
|
|
25
|
+
expect(mockMarkRead).toHaveBeenCalledTimes(1)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('ignores unread bumps while disabled and marks read once enabled', () => {
|
|
29
|
+
jest.useFakeTimers()
|
|
30
|
+
const { rerender } = renderHook<void, { conversation: ConversationResource; enabled: boolean }>(
|
|
31
|
+
props => useMarkLatestMessageRead({ ...props, messages: [] }),
|
|
32
|
+
{ initialProps: { conversation: { ...conversation, unreadCount: 0 }, enabled: false } }
|
|
26
33
|
)
|
|
27
|
-
expect(mockMarkRead).
|
|
34
|
+
expect(mockMarkRead).toHaveBeenCalledTimes(1)
|
|
28
35
|
|
|
29
|
-
rerender({ enabled:
|
|
36
|
+
rerender({ conversation: { ...conversation, unreadCount: 2 }, enabled: false })
|
|
37
|
+
act(() => jest.advanceTimersByTime(1000))
|
|
30
38
|
|
|
31
39
|
expect(mockMarkRead).toHaveBeenCalledTimes(1)
|
|
40
|
+
|
|
41
|
+
rerender({ conversation: { ...conversation, unreadCount: 2 }, enabled: true })
|
|
42
|
+
act(() => jest.advanceTimersByTime(1000))
|
|
43
|
+
|
|
44
|
+
expect(mockMarkRead).toHaveBeenCalledTimes(2)
|
|
45
|
+
jest.useRealTimers()
|
|
32
46
|
})
|
|
33
47
|
})
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { act, renderHook, waitFor } from '@testing-library/react-native'
|
|
2
|
+
import { AccessibilityInfo, EmitterSubscription } from 'react-native'
|
|
3
|
+
import * as Reanimated from 'react-native-reanimated'
|
|
4
|
+
import { useReduceMotion } from '../use_reduce_motion'
|
|
5
|
+
|
|
6
|
+
type ReduceMotionListener = (enabled: boolean) => void
|
|
7
|
+
|
|
8
|
+
describe('useReduceMotion', () => {
|
|
9
|
+
let listener: ReduceMotionListener | undefined
|
|
10
|
+
const remove = jest.fn()
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
listener = undefined
|
|
14
|
+
remove.mockClear()
|
|
15
|
+
jest.spyOn(Reanimated, 'useReducedMotion').mockReturnValue(false)
|
|
16
|
+
jest.spyOn(AccessibilityInfo, 'isReduceMotionEnabled').mockResolvedValue(true)
|
|
17
|
+
const addEventListener: typeof AccessibilityInfo.addEventListener = (_event, handler) => {
|
|
18
|
+
listener = handler as ReduceMotionListener
|
|
19
|
+
return { remove } as unknown as EmitterSubscription
|
|
20
|
+
}
|
|
21
|
+
jest.spyOn(AccessibilityInfo, 'addEventListener').mockImplementation(addEventListener)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
afterEach(() => jest.restoreAllMocks())
|
|
25
|
+
|
|
26
|
+
it('seeds synchronously from the launch-time setting', async () => {
|
|
27
|
+
jest.spyOn(Reanimated, 'useReducedMotion').mockReturnValue(true)
|
|
28
|
+
jest.mocked(AccessibilityInfo.isReduceMotionEnabled).mockResolvedValue(false)
|
|
29
|
+
|
|
30
|
+
const { result } = renderHook(() => useReduceMotion())
|
|
31
|
+
|
|
32
|
+
expect(result.current).toBe(true)
|
|
33
|
+
await waitFor(() => expect(result.current).toBe(false))
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('updates to the live setting once it resolves', async () => {
|
|
37
|
+
const { result } = renderHook(() => useReduceMotion())
|
|
38
|
+
|
|
39
|
+
expect(result.current).toBe(false)
|
|
40
|
+
await waitFor(() => expect(result.current).toBe(true))
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('follows reduceMotionChanged while mounted', async () => {
|
|
44
|
+
const { result } = renderHook(() => useReduceMotion())
|
|
45
|
+
await waitFor(() => expect(result.current).toBe(true))
|
|
46
|
+
|
|
47
|
+
act(() => listener?.(false))
|
|
48
|
+
expect(result.current).toBe(false)
|
|
49
|
+
|
|
50
|
+
act(() => listener?.(true))
|
|
51
|
+
expect(result.current).toBe(true)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('removes the listener on unmount', () => {
|
|
55
|
+
const { unmount } = renderHook(() => useReduceMotion())
|
|
56
|
+
|
|
57
|
+
expect(remove).not.toHaveBeenCalled()
|
|
58
|
+
unmount()
|
|
59
|
+
expect(remove).toHaveBeenCalledTimes(1)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { act, renderHook } from '@testing-library/react-native'
|
|
2
|
+
import type { ConversationResource } from '../../types'
|
|
3
|
+
import { availableFeatures } from '../use_features'
|
|
4
|
+
import { useUnarchiveConversationAction } from '../use_unarchive_conversation_action'
|
|
5
|
+
|
|
6
|
+
const mockSetArchived = jest.fn()
|
|
7
|
+
jest.mock('../use_conversation', () => ({
|
|
8
|
+
useConversationArchive: () => ({ setArchived: mockSetArchived, isPending: false }),
|
|
9
|
+
}))
|
|
10
|
+
|
|
11
|
+
let mockEnabledFeatures: string[] = []
|
|
12
|
+
jest.mock('../use_features', () => ({
|
|
13
|
+
...jest.requireActual('../use_features'),
|
|
14
|
+
useFeatures: () => ({
|
|
15
|
+
features: [],
|
|
16
|
+
featureEnabled: (name: string) => mockEnabledFeatures.includes(name),
|
|
17
|
+
}),
|
|
18
|
+
}))
|
|
19
|
+
|
|
20
|
+
const conversation = ({
|
|
21
|
+
disabledReason = 'archived',
|
|
22
|
+
canUnarchive = true,
|
|
23
|
+
}: {
|
|
24
|
+
disabledReason?: string | null
|
|
25
|
+
canUnarchive?: boolean
|
|
26
|
+
} = {}) =>
|
|
27
|
+
({
|
|
28
|
+
type: 'Conversation',
|
|
29
|
+
id: 7,
|
|
30
|
+
disabled: !!disabledReason,
|
|
31
|
+
disabledReason,
|
|
32
|
+
memberAbility: { type: 'MemberAbility', canUnarchive },
|
|
33
|
+
}) as unknown as ConversationResource
|
|
34
|
+
|
|
35
|
+
const renderAction = (resource: ConversationResource) =>
|
|
36
|
+
renderHook(() => useUnarchiveConversationAction(resource)).result.current
|
|
37
|
+
|
|
38
|
+
describe('useUnarchiveConversationAction', () => {
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
mockEnabledFeatures = [availableFeatures.manual_archiving_rollout]
|
|
41
|
+
jest.clearAllMocks()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('unarchives an archived conversation the person can unarchive', () => {
|
|
45
|
+
const action = renderAction(conversation())
|
|
46
|
+
|
|
47
|
+
expect(action?.label).toBe('Unarchive')
|
|
48
|
+
|
|
49
|
+
act(() => action?.onPress())
|
|
50
|
+
|
|
51
|
+
expect(mockSetArchived).toHaveBeenCalledWith(false)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it.each([
|
|
55
|
+
['neither mobile flag is on', () => (mockEnabledFeatures = []), conversation()],
|
|
56
|
+
['the person cannot unarchive', () => {}, conversation({ canUnarchive: false })],
|
|
57
|
+
[
|
|
58
|
+
'the conversation is disabled for another reason',
|
|
59
|
+
() => {},
|
|
60
|
+
conversation({ disabledReason: 'safety_check' }),
|
|
61
|
+
],
|
|
62
|
+
])('offers nothing when %s', (_case, setup, resource) => {
|
|
63
|
+
setup()
|
|
64
|
+
|
|
65
|
+
expect(renderAction(resource)).toBeUndefined()
|
|
66
|
+
})
|
|
67
|
+
})
|