@planningcenter/chat-react-native 3.38.0-rc.1 → 3.38.0-rc.11
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 +1 -2
- package/build/components/conversation/jump_to_bottom_button.d.ts.map +1 -1
- package/build/components/conversation/jump_to_bottom_button.js +7 -39
- package/build/components/conversation/jump_to_bottom_button.js.map +1 -1
- package/build/components/conversation/message_list.d.ts +10 -0
- package/build/components/conversation/message_list.d.ts.map +1 -0
- package/build/components/conversation/message_list.js +13 -0
- package/build/components/conversation/message_list.js.map +1 -0
- package/build/components/conversation/reply_shadow_message.d.ts +2 -1
- package/build/components/conversation/reply_shadow_message.d.ts.map +1 -1
- package/build/components/conversation/reply_shadow_message.js.map +1 -1
- package/build/components/display/conversation_avatar.d.ts +2 -1
- package/build/components/display/conversation_avatar.d.ts.map +1 -1
- package/build/components/display/conversation_avatar.js +6 -5
- package/build/components/display/conversation_avatar.js.map +1 -1
- package/build/components/display/emoji_avatar.d.ts +3 -1
- package/build/components/display/emoji_avatar.d.ts.map +1 -1
- package/build/components/display/emoji_avatar.js +2 -2
- package/build/components/display/emoji_avatar.js.map +1 -1
- package/build/components/display/icon_avatar.d.ts +3 -1
- package/build/components/display/icon_avatar.d.ts.map +1 -1
- package/build/components/display/icon_avatar.js +2 -2
- package/build/components/display/icon_avatar.js.map +1 -1
- package/build/contexts/conversation_context.d.ts +1 -8
- package/build/contexts/conversation_context.d.ts.map +1 -1
- package/build/contexts/conversation_context.js +3 -21
- package/build/contexts/conversation_context.js.map +1 -1
- package/build/hooks/groups/use_group_chat_conversation_payload.d.ts.map +1 -1
- package/build/hooks/groups/use_group_chat_conversation_payload.js +1 -0
- package/build/hooks/groups/use_group_chat_conversation_payload.js.map +1 -1
- package/build/hooks/index.d.ts +1 -0
- package/build/hooks/index.d.ts.map +1 -1
- package/build/hooks/index.js +1 -0
- package/build/hooks/index.js.map +1 -1
- package/build/hooks/use_conversation_messages.d.ts +6 -15
- package/build/hooks/use_conversation_messages.d.ts.map +1 -1
- package/build/hooks/use_conversation_messages.js +9 -62
- package/build/hooks/use_conversation_messages.js.map +1 -1
- package/build/hooks/use_conversation_messages_jolt_events.d.ts.map +1 -1
- package/build/hooks/use_conversation_messages_jolt_events.js +4 -4
- package/build/hooks/use_conversation_messages_jolt_events.js.map +1 -1
- package/build/hooks/use_conversations_actions.d.ts +0 -5
- package/build/hooks/use_conversations_actions.d.ts.map +1 -1
- package/build/hooks/use_conversations_actions.js +0 -12
- package/build/hooks/use_conversations_actions.js.map +1 -1
- package/build/hooks/use_features.d.ts +0 -1
- package/build/hooks/use_features.d.ts.map +1 -1
- package/build/hooks/use_features.js +0 -1
- package/build/hooks/use_features.js.map +1 -1
- package/build/hooks/use_mark_latest_message_read.d.ts +1 -1
- package/build/hooks/use_mark_latest_message_read.d.ts.map +1 -1
- package/build/hooks/use_mark_latest_message_read.js +1 -17
- package/build/hooks/use_mark_latest_message_read.js.map +1 -1
- package/build/hooks/use_preview_avatar_diameter.d.ts +2 -0
- package/build/hooks/use_preview_avatar_diameter.d.ts.map +1 -0
- package/build/hooks/use_preview_avatar_diameter.js +11 -0
- package/build/hooks/use_preview_avatar_diameter.js.map +1 -0
- package/build/hooks/use_suspense_api.d.ts +0 -1
- package/build/hooks/use_suspense_api.d.ts.map +1 -1
- package/build/hooks/use_suspense_api.js +1 -1
- package/build/hooks/use_suspense_api.js.map +1 -1
- package/build/jest.js +1 -1
- package/build/jest.js.map +1 -1
- package/build/screens/avatar_picker/avatar_picker_screen.d.ts.map +1 -1
- package/build/screens/avatar_picker/avatar_picker_screen.js +11 -9
- package/build/screens/avatar_picker/avatar_picker_screen.js.map +1 -1
- package/build/screens/avatar_picker/avatar_preview.d.ts.map +1 -1
- package/build/screens/avatar_picker/avatar_preview.js +13 -5
- package/build/screens/avatar_picker/avatar_preview.js.map +1 -1
- package/build/screens/avatar_picker/emoji_tab.d.ts.map +1 -1
- package/build/screens/avatar_picker/emoji_tab.js +3 -7
- package/build/screens/avatar_picker/emoji_tab.js.map +1 -1
- package/build/screens/avatar_picker/upload_tab.d.ts.map +1 -1
- package/build/screens/avatar_picker/upload_tab.js +2 -1
- package/build/screens/avatar_picker/upload_tab.js.map +1 -1
- package/build/screens/conversation_details_screen.d.ts.map +1 -1
- package/build/screens/conversation_details_screen.js +5 -2
- package/build/screens/conversation_details_screen.js.map +1 -1
- package/build/screens/conversation_filter_recipients/components/header_row.d.ts.map +1 -1
- package/build/screens/conversation_filter_recipients/components/header_row.js +3 -2
- package/build/screens/conversation_filter_recipients/components/header_row.js.map +1 -1
- package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_with_teams.d.ts.map +1 -1
- package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_with_teams.js +47 -18
- package/build/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_with_teams.js.map +1 -1
- package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.d.ts +2 -1
- package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.d.ts.map +1 -1
- package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.js +23 -26
- package/build/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.js.map +1 -1
- package/build/screens/conversation_filter_recipients/types.d.ts +1 -1
- package/build/screens/conversation_filter_recipients/types.d.ts.map +1 -1
- package/build/screens/conversation_filter_recipients/types.js.map +1 -1
- package/build/screens/conversation_screen.d.ts +0 -1
- package/build/screens/conversation_screen.d.ts.map +1 -1
- package/build/screens/conversation_screen.js +45 -96
- package/build/screens/conversation_screen.js.map +1 -1
- package/build/screens/conversation_select_recipients/components/recipient_link_row.d.ts +1 -1
- package/build/screens/conversation_select_recipients/components/recipient_link_row.d.ts.map +1 -1
- package/build/screens/conversation_select_recipients/components/recipient_link_row.js +3 -3
- package/build/screens/conversation_select_recipients/components/recipient_link_row.js.map +1 -1
- package/build/screens/conversation_select_recipients/components/team_recipient_row.d.ts.map +1 -1
- package/build/screens/conversation_select_recipients/components/team_recipient_row.js +1 -1
- package/build/screens/conversation_select_recipients/components/team_recipient_row.js.map +1 -1
- package/build/screens/team_conversation_screen.d.ts.map +1 -1
- package/build/screens/team_conversation_screen.js +24 -1
- package/build/screens/team_conversation_screen.js.map +1 -1
- package/build/utils/cache/messages_cache.d.ts +0 -1
- package/build/utils/cache/messages_cache.d.ts.map +1 -1
- package/build/utils/cache/messages_cache.js +0 -4
- package/build/utils/cache/messages_cache.js.map +1 -1
- package/build/utils/client/client.d.ts +1 -1
- package/build/utils/client/client.d.ts.map +1 -1
- package/build/utils/client/client.js +7 -6
- package/build/utils/client/client.js.map +1 -1
- package/build/utils/client/instrumented_fetch.js +3 -5
- package/build/utils/client/instrumented_fetch.js.map +1 -1
- package/build/utils/group_messages.d.ts +2 -9
- package/build/utils/group_messages.d.ts.map +1 -1
- package/build/utils/group_messages.js +1 -20
- package/build/utils/group_messages.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/hooks/use_group_chat_conversation_payload.test.tsx +50 -0
- package/src/__tests__/jest.ts +1 -1
- package/src/__tests__/utils/client.ts +32 -0
- package/src/components/conversation/__tests__/message_list.test.tsx +14 -0
- package/src/components/conversation/jump_to_bottom_button.tsx +8 -57
- package/src/components/conversation/message_list.tsx +42 -0
- package/src/components/conversation/reply_shadow_message.tsx +1 -1
- package/src/components/display/conversation_avatar.tsx +7 -5
- package/src/components/display/emoji_avatar.tsx +10 -2
- package/src/components/display/icon_avatar.tsx +10 -2
- package/src/contexts/conversation_context.tsx +2 -30
- package/src/hooks/groups/use_group_chat_conversation_payload.ts +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use_conversation_messages.ts +20 -120
- package/src/hooks/use_conversation_messages_jolt_events.ts +3 -4
- package/src/hooks/use_conversations_actions.ts +0 -15
- package/src/hooks/use_features.ts +0 -1
- package/src/hooks/use_mark_latest_message_read.ts +2 -16
- package/src/hooks/use_preview_avatar_diameter.ts +12 -0
- package/src/hooks/use_suspense_api.ts +1 -1
- package/src/jest.ts +1 -1
- package/src/screens/avatar_picker/avatar_picker_screen.tsx +25 -9
- package/src/screens/avatar_picker/avatar_preview.tsx +14 -5
- package/src/screens/avatar_picker/emoji_tab.tsx +3 -6
- package/src/screens/avatar_picker/upload_tab.tsx +2 -0
- package/src/screens/conversation_details_screen.tsx +10 -1
- package/src/screens/conversation_filter_recipients/components/header_row.tsx +3 -2
- package/src/screens/conversation_filter_recipients/hooks/__tests__/use_service_types_with_teams.test.ts +108 -0
- package/src/screens/conversation_filter_recipients/hooks/use_flattened_array_of_service_types_with_teams.tsx +46 -19
- package/src/screens/conversation_filter_recipients/hooks/use_service_types_with_teams.ts +31 -29
- package/src/screens/conversation_filter_recipients/types.tsx +1 -1
- package/src/screens/conversation_screen.tsx +69 -186
- package/src/screens/conversation_select_recipients/components/recipient_link_row.tsx +6 -4
- package/src/screens/conversation_select_recipients/components/team_recipient_row.tsx +2 -1
- package/src/screens/team_conversation_screen.tsx +33 -1
- package/src/utils/__tests__/group_messages.test.ts +0 -71
- package/src/utils/cache/messages_cache.ts +0 -5
- package/src/utils/client/__tests__/instrumented_fetch.test.ts +9 -5
- package/src/utils/client/client.ts +9 -7
- package/src/utils/client/instrumented_fetch.ts +3 -6
- package/src/utils/group_messages.ts +2 -42
- package/build/components/conversation/unread_divider.d.ts +0 -6
- package/build/components/conversation/unread_divider.d.ts.map +0 -1
- package/build/components/conversation/unread_divider.js +0 -59
- package/build/components/conversation/unread_divider.js.map +0 -1
- package/build/hooks/use_flat_list_viewability.d.ts +0 -20
- package/build/hooks/use_flat_list_viewability.d.ts.map +0 -1
- package/build/hooks/use_flat_list_viewability.js +0 -30
- package/build/hooks/use_flat_list_viewability.js.map +0 -1
- package/build/hooks/use_jump_to_bottom_action.d.ts +0 -9
- package/build/hooks/use_jump_to_bottom_action.d.ts.map +0 -1
- package/build/hooks/use_jump_to_bottom_action.js +0 -62
- package/build/hooks/use_jump_to_bottom_action.js.map +0 -1
- package/build/hooks/use_jump_to_unread_anchor.d.ts +0 -20
- package/build/hooks/use_jump_to_unread_anchor.d.ts.map +0 -1
- package/build/hooks/use_jump_to_unread_anchor.js +0 -53
- package/build/hooks/use_jump_to_unread_anchor.js.map +0 -1
- package/build/hooks/use_jump_to_unread_gates.d.ts +0 -5
- package/build/hooks/use_jump_to_unread_gates.d.ts.map +0 -1
- package/build/hooks/use_jump_to_unread_gates.js +0 -10
- package/build/hooks/use_jump_to_unread_gates.js.map +0 -1
- package/build/hooks/use_scroll_tracking.d.ts +0 -13
- package/build/hooks/use_scroll_tracking.d.ts.map +0 -1
- package/build/hooks/use_scroll_tracking.js +0 -45
- package/build/hooks/use_scroll_tracking.js.map +0 -1
- package/build/hooks/use_track_highest_seen_message.d.ts +0 -4
- package/build/hooks/use_track_highest_seen_message.d.ts.map +0 -1
- package/build/hooks/use_track_highest_seen_message.js +0 -35
- package/build/hooks/use_track_highest_seen_message.js.map +0 -1
- package/build/utils/conversation_messages.d.ts +0 -10
- package/build/utils/conversation_messages.d.ts.map +0 -1
- package/build/utils/conversation_messages.js +0 -22
- package/build/utils/conversation_messages.js.map +0 -1
- package/build/utils/highest_seen_tracker.d.ts +0 -12
- package/build/utils/highest_seen_tracker.d.ts.map +0 -1
- package/build/utils/highest_seen_tracker.js +0 -37
- package/build/utils/highest_seen_tracker.js.map +0 -1
- package/build/utils/message_viewability.d.ts +0 -24
- package/build/utils/message_viewability.d.ts.map +0 -1
- package/build/utils/message_viewability.js +0 -29
- package/build/utils/message_viewability.js.map +0 -1
- package/build/utils/unread_divider_helpers.d.ts +0 -18
- package/build/utils/unread_divider_helpers.d.ts.map +0 -1
- package/build/utils/unread_divider_helpers.js +0 -13
- package/build/utils/unread_divider_helpers.js.map +0 -1
- package/src/__tests__/hooks/use_conversation_messages.test.tsx +0 -109
- package/src/__tests__/hooks/use_mark_latest_message_read.test.tsx +0 -154
- package/src/__tests__/utils/cache/messages_cache.test.ts +0 -54
- package/src/components/conversation/unread_divider.tsx +0 -90
- package/src/hooks/use_flat_list_viewability.ts +0 -50
- package/src/hooks/use_jump_to_bottom_action.ts +0 -75
- package/src/hooks/use_jump_to_unread_anchor.ts +0 -68
- package/src/hooks/use_jump_to_unread_gates.ts +0 -10
- package/src/hooks/use_scroll_tracking.ts +0 -64
- package/src/hooks/use_track_highest_seen_message.ts +0 -43
- package/src/utils/__tests__/conversation_messages.test.ts +0 -105
- package/src/utils/__tests__/highest_seen_tracker.test.ts +0 -82
- package/src/utils/__tests__/message_viewability.test.ts +0 -168
- package/src/utils/__tests__/unread_divider_helpers.test.ts +0 -85
- package/src/utils/conversation_messages.ts +0 -37
- package/src/utils/highest_seen_tracker.ts +0 -42
- package/src/utils/message_viewability.ts +0 -49
- package/src/utils/unread_divider_helpers.ts +0 -25
|
@@ -13,9 +13,10 @@ export function useFlattenedArrayOfServiceTypesWithTeams({
|
|
|
13
13
|
firstRowStyle,
|
|
14
14
|
lastRowStyle,
|
|
15
15
|
}: Props) {
|
|
16
|
-
const flattenedData: SectionListData = useMemo(
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
const flattenedData: SectionListData = useMemo(() => {
|
|
17
|
+
const serviceTypeRows = data
|
|
18
|
+
.filter(serviceType => serviceType.id > 0)
|
|
19
|
+
.flatMap(serviceType => {
|
|
19
20
|
const teamIdsForServiceType = serviceType.teams.map(team => team.id)
|
|
20
21
|
|
|
21
22
|
return [
|
|
@@ -28,23 +29,49 @@ export function useFlattenedArrayOfServiceTypesWithTeams({
|
|
|
28
29
|
},
|
|
29
30
|
sectionStyle: firstRowStyle,
|
|
30
31
|
},
|
|
31
|
-
...serviceType.teams.map((team, teamIdx) => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
},
|
|
41
|
-
sectionStyle: isLastTeamInServiceType ? lastRowStyle : undefined,
|
|
42
|
-
}
|
|
43
|
-
}),
|
|
32
|
+
...serviceType.teams.map((team, teamIdx) => ({
|
|
33
|
+
type: SectionTypes.team as const,
|
|
34
|
+
data: {
|
|
35
|
+
teamName: team.name,
|
|
36
|
+
teamId: team.id,
|
|
37
|
+
serviceTypeId: serviceType.id,
|
|
38
|
+
},
|
|
39
|
+
sectionStyle: teamIdx === serviceType.teams.length - 1 ? lastRowStyle : undefined,
|
|
40
|
+
})),
|
|
44
41
|
]
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
// Teams without a service type (id < 0) are merged under a single "No service type" section.
|
|
45
|
+
// Service type ID 0 is a safe sentinel — real IDs are always positive.
|
|
46
|
+
const serviceTypelessTeams = data
|
|
47
|
+
.filter(serviceType => serviceType.id < 0)
|
|
48
|
+
.flatMap(serviceType => serviceType.teams)
|
|
49
|
+
|
|
50
|
+
if (serviceTypelessTeams.length === 0) return serviceTypeRows
|
|
51
|
+
|
|
52
|
+
const serviceTypelessRows: SectionListData = [
|
|
53
|
+
{
|
|
54
|
+
type: SectionTypes.header as const,
|
|
55
|
+
data: {
|
|
56
|
+
serviceTypeName: null,
|
|
57
|
+
serviceTypeId: 0,
|
|
58
|
+
teamIdsForServiceType: serviceTypelessTeams.map(t => t.id),
|
|
59
|
+
},
|
|
60
|
+
sectionStyle: firstRowStyle,
|
|
61
|
+
},
|
|
62
|
+
...serviceTypelessTeams.map((team, teamIdx) => ({
|
|
63
|
+
type: SectionTypes.team as const,
|
|
64
|
+
data: {
|
|
65
|
+
teamName: team.name,
|
|
66
|
+
teamId: team.id,
|
|
67
|
+
serviceTypeId: 0,
|
|
68
|
+
},
|
|
69
|
+
sectionStyle: teamIdx === serviceTypelessTeams.length - 1 ? lastRowStyle : undefined,
|
|
70
|
+
})),
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
return [...serviceTypelessRows, ...serviceTypeRows]
|
|
74
|
+
}, [data, firstRowStyle, lastRowStyle])
|
|
48
75
|
|
|
49
76
|
return flattenedData
|
|
50
77
|
}
|
|
@@ -59,45 +59,47 @@ const useTeams = ({ filterType }: { filterType: TeamFilterTypes }) => {
|
|
|
59
59
|
return { data: result || [], ...rest }
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
function decorateTeamResponseItems(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
export function decorateTeamResponseItems(
|
|
63
|
+
teamResponseItems: TeamResponseItem[],
|
|
64
|
+
searchQuery?: string
|
|
65
|
+
) {
|
|
66
|
+
const filtered = teamResponseItems.filter(item => {
|
|
67
|
+
if (!searchQuery) return true
|
|
68
|
+
const evalMatch = (str: string) => str.toLowerCase().includes(searchQuery.toLowerCase())
|
|
69
|
+
return evalMatch(item.name) || evalMatch(item.serviceTypeNames?.join(',') || '')
|
|
70
|
+
})
|
|
66
71
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const serviceTypeNamesMatch = evalMatch(item.serviceTypeNames?.join(',') || '')
|
|
72
|
+
const withServiceTypes = filtered.filter(item => item.value.serviceTypeIds.length > 0)
|
|
73
|
+
const withoutServiceTypes = filtered.filter(item => item.value.serviceTypeIds.length === 0)
|
|
70
74
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
name: serviceTypeNames[i],
|
|
78
|
-
})),
|
|
79
|
-
team: {
|
|
80
|
-
id: value.teamId,
|
|
81
|
-
name: teamName,
|
|
82
|
-
},
|
|
83
|
-
}
|
|
75
|
+
// Negative team ID is used as a unique sentinel — real service type IDs are always positive.
|
|
76
|
+
const typelessEntries: ServiceTypeWithTeams[] = withoutServiceTypes.map(
|
|
77
|
+
({ value, teamName }) => ({
|
|
78
|
+
id: -value.teamId,
|
|
79
|
+
name: teamName,
|
|
80
|
+
teams: [{ id: value.teamId, name: teamName }],
|
|
84
81
|
})
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
const typedEntries = withServiceTypes
|
|
85
|
+
.map(({ value, serviceTypeNames, teamName }) => ({
|
|
86
|
+
service_types: value.serviceTypeIds.map((serviceTypeId, i) => ({
|
|
87
|
+
id: serviceTypeId,
|
|
88
|
+
name: serviceTypeNames[i],
|
|
89
|
+
})),
|
|
90
|
+
team: { id: value.teamId, name: teamName },
|
|
91
|
+
}))
|
|
85
92
|
.reduce((acc: ServiceTypeWithTeams[], { service_types, team }) => {
|
|
86
93
|
service_types.forEach(serviceType => {
|
|
87
94
|
let serviceTypeEntry = acc.find(entry => entry.id === serviceType.id)
|
|
88
|
-
|
|
89
95
|
if (!serviceTypeEntry) {
|
|
90
|
-
serviceTypeEntry = {
|
|
91
|
-
id: serviceType.id,
|
|
92
|
-
name: serviceType.name,
|
|
93
|
-
teams: [],
|
|
94
|
-
}
|
|
96
|
+
serviceTypeEntry = { id: serviceType.id, name: serviceType.name, teams: [] }
|
|
95
97
|
acc.push(serviceTypeEntry)
|
|
96
98
|
}
|
|
97
|
-
|
|
98
|
-
const initialTeams = serviceTypeEntry.teams
|
|
99
|
-
serviceTypeEntry.teams = uniqBy([...initialTeams, team], 'id')
|
|
99
|
+
serviceTypeEntry.teams = uniqBy([...serviceTypeEntry.teams, team], 'id')
|
|
100
100
|
})
|
|
101
101
|
return acc
|
|
102
102
|
}, [])
|
|
103
|
+
|
|
104
|
+
return [...typelessEntries, ...typedEntries]
|
|
103
105
|
}
|
|
@@ -8,9 +8,8 @@ import {
|
|
|
8
8
|
useTheme as useNavigationTheme,
|
|
9
9
|
useRoute,
|
|
10
10
|
} from '@react-navigation/native'
|
|
11
|
-
import React, { useCallback, useEffect,
|
|
12
|
-
import {
|
|
13
|
-
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'
|
|
11
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
|
12
|
+
import { FlatList, Platform, StyleSheet, View } from 'react-native'
|
|
14
13
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
15
14
|
import { Badge, Icon, Text } from '../components'
|
|
16
15
|
import { EmptyConversationBlankState } from '../components/conversation/empty_conversation_blank_state'
|
|
@@ -25,46 +24,25 @@ import {
|
|
|
25
24
|
import { ReplyShadowMessage } from '../components/conversation/reply_shadow_message'
|
|
26
25
|
import { SystemMessage } from '../components/conversation/system_message'
|
|
27
26
|
import { TypingIndicator } from '../components/conversation/typing_indicator'
|
|
28
|
-
import { UnreadDivider } from '../components/conversation/unread_divider'
|
|
29
27
|
import { KeyboardView } from '../components/display/keyboard_view'
|
|
30
28
|
import BlankState from '../components/primitive/blank_state_primitive'
|
|
31
|
-
import {
|
|
32
|
-
ConversationContextProvider,
|
|
33
|
-
useConversationContext,
|
|
34
|
-
} from '../contexts/conversation_context'
|
|
29
|
+
import { ConversationContextProvider } from '../contexts/conversation_context'
|
|
35
30
|
import { useTheme } from '../hooks'
|
|
36
31
|
import { useConversation } from '../hooks/use_conversation'
|
|
37
32
|
import { useConversationJoltEvents } from '../hooks/use_conversation_jolt_events'
|
|
38
33
|
import { useConversationMessages } from '../hooks/use_conversation_messages'
|
|
39
34
|
import { useConversationMessagesJoltEvents } from '../hooks/use_conversation_messages_jolt_events'
|
|
40
|
-
import { availableFeatures, useFeatures } from '../hooks/use_features'
|
|
41
|
-
import { useFlatListViewability } from '../hooks/use_flat_list_viewability'
|
|
42
|
-
import { useJumpToBottomAction } from '../hooks/use_jump_to_bottom_action'
|
|
43
|
-
import { useJumpToUnreadAnchor } from '../hooks/use_jump_to_unread_anchor'
|
|
44
|
-
import { useJumpToUnreadGates } from '../hooks/use_jump_to_unread_gates'
|
|
45
35
|
import { useMarkLatestMessageRead } from '../hooks/use_mark_latest_message_read'
|
|
46
36
|
import {
|
|
47
37
|
analyticsEvents,
|
|
48
38
|
normalizeAnalyticsMetadata,
|
|
49
39
|
usePublishProductAnalyticsEvent,
|
|
50
40
|
} from '../hooks/use_product_analytics'
|
|
51
|
-
import { useScrollTracking } from '../hooks/use_scroll_tracking'
|
|
52
|
-
import { useTrackHighestSeenMessage } from '../hooks/use_track_highest_seen_message'
|
|
53
41
|
import { ConversationResource } from '../types/resources/conversation'
|
|
54
42
|
import { ConversationBadgeResource } from '../types/resources/conversation_badge'
|
|
55
43
|
import { MessageResource } from '../types/resources/message'
|
|
56
44
|
import { getRelativeDateStatus } from '../utils/date'
|
|
57
|
-
import {
|
|
58
|
-
groupMessages,
|
|
59
|
-
UNREAD_DIVIDER_KEY,
|
|
60
|
-
type DateSeparator,
|
|
61
|
-
type EnrichedMessage,
|
|
62
|
-
} from '../utils/group_messages'
|
|
63
|
-
import {
|
|
64
|
-
detectDividerExitTowardNewer,
|
|
65
|
-
reportViewableMessages,
|
|
66
|
-
type ViewabilityObserver,
|
|
67
|
-
} from '../utils/message_viewability'
|
|
45
|
+
import { groupMessages, type DateSeparator } from '../utils/group_messages'
|
|
68
46
|
import { CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL } from '../utils/styles'
|
|
69
47
|
import { isSystemMessage } from '../utils/system_messages'
|
|
70
48
|
|
|
@@ -75,7 +53,6 @@ export type ConversationRouteProps = {
|
|
|
75
53
|
chat_group_graph_id?: string
|
|
76
54
|
clear_input?: boolean
|
|
77
55
|
editing_message_id?: number | null
|
|
78
|
-
message_id?: string
|
|
79
56
|
title?: string
|
|
80
57
|
subtitle?: string
|
|
81
58
|
badge?: ConversationBadgeResource
|
|
@@ -85,34 +62,20 @@ export type ConversationRouteProps = {
|
|
|
85
62
|
|
|
86
63
|
export type ConversationScreenProps = StaticScreenProps<ConversationRouteProps>
|
|
87
64
|
|
|
88
|
-
const extractItemKey = (item: EnrichedMessage) => String(item.id)
|
|
89
|
-
const maintainVisibleContentPosition = { minIndexForVisible: 0 }
|
|
90
|
-
|
|
91
65
|
export function ConversationScreen({ route }: ConversationScreenProps) {
|
|
92
|
-
const { conversation_id,
|
|
66
|
+
const { conversation_id, reply_root_id } = route.params
|
|
93
67
|
|
|
94
68
|
const { data: conversation } = useConversation({ conversation_id })
|
|
95
|
-
const { featureEnabled } = useFeatures()
|
|
96
69
|
|
|
97
70
|
usePublishProductAnalyticsEvent(analyticsEvents.conversation_show_opened, {
|
|
98
71
|
reply_root_id,
|
|
99
72
|
...normalizeAnalyticsMetadata(conversation),
|
|
100
73
|
})
|
|
101
74
|
|
|
102
|
-
const lastReadMessageSortKey = conversation.conversationMembership?.lastReadMessageSortKey ?? null
|
|
103
|
-
const jumpToUnreadAnchor =
|
|
104
|
-
featureEnabled(availableFeatures.jump_to_unread) && !reply_root_id
|
|
105
|
-
? lastReadMessageSortKey
|
|
106
|
-
: null
|
|
107
|
-
const initialMessageId = message_id ?? jumpToUnreadAnchor
|
|
108
|
-
const initialMessageIdIsAnchor = !!initialMessageId && !message_id
|
|
109
|
-
|
|
110
75
|
return (
|
|
111
76
|
<ConversationContextProvider
|
|
112
77
|
conversationId={conversation_id}
|
|
113
78
|
currentPageReplyRootId={reply_root_id ?? null}
|
|
114
|
-
initialMessageId={initialMessageId}
|
|
115
|
-
initialMessageIdIsAnchor={initialMessageIdIsAnchor}
|
|
116
79
|
>
|
|
117
80
|
<ConversationScreenContent route={route} />
|
|
118
81
|
</ConversationContextProvider>
|
|
@@ -122,49 +85,29 @@ export function ConversationScreen({ route }: ConversationScreenProps) {
|
|
|
122
85
|
function ConversationScreenContent({ route }: ConversationScreenProps) {
|
|
123
86
|
const styles = useStyles()
|
|
124
87
|
const navigation = useNavigation()
|
|
125
|
-
const {
|
|
126
|
-
|
|
127
|
-
editing_message_id: editingMessageId,
|
|
128
|
-
reply_root_id: replyRootId,
|
|
129
|
-
reply_root_author_name: replyRootAuthorName,
|
|
130
|
-
} = route.params
|
|
88
|
+
const { conversation_id, editing_message_id, reply_root_id, reply_root_author_name } =
|
|
89
|
+
route.params
|
|
131
90
|
const { data: conversation } = useConversation(route.params)
|
|
132
|
-
const {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
cancelFetchNewerMessages,
|
|
139
|
-
} = useConversationMessages({ conversation_id: conversationId, reply_root_id: replyRootId })
|
|
140
|
-
|
|
141
|
-
const { jumpToUnreadActive } = useJumpToUnreadGates()
|
|
142
|
-
const { initialMessageId } = useConversationContext()
|
|
143
|
-
|
|
144
|
-
useConversationJoltEvents({ conversationId })
|
|
145
|
-
useConversationMessagesJoltEvents({ conversationId })
|
|
91
|
+
const { messages, fetchNextPage } = useConversationMessages({
|
|
92
|
+
conversation_id,
|
|
93
|
+
reply_root_id,
|
|
94
|
+
})
|
|
95
|
+
useConversationJoltEvents({ conversationId: conversation_id })
|
|
96
|
+
useConversationMessagesJoltEvents({ conversationId: conversation_id })
|
|
146
97
|
useEnsureConversationsRouteExists()
|
|
147
98
|
useMarkLatestMessageRead({ conversation, messages })
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
ms: messages,
|
|
154
|
-
inReplyScreen: !!replyRootId,
|
|
155
|
-
jumpToUnreadActive,
|
|
156
|
-
initialMessageId,
|
|
157
|
-
}),
|
|
158
|
-
[messages, replyRootId, jumpToUnreadActive, initialMessageId]
|
|
159
|
-
)
|
|
160
|
-
const noMessages = items.length === 0
|
|
99
|
+
const messagesWithSeparators = groupMessages({
|
|
100
|
+
ms: messages,
|
|
101
|
+
inReplyScreen: !!reply_root_id,
|
|
102
|
+
})
|
|
103
|
+
const noMessages = messagesWithSeparators.length === 0
|
|
161
104
|
|
|
162
105
|
const { repliesDisabled, memberAbility, badges, title } = conversation
|
|
163
106
|
const canReply = memberAbility?.canReply
|
|
164
107
|
const showLeaderDisabledReplyBanner = canReply && repliesDisabled
|
|
165
108
|
const canDeleteNonAuthoredMessages = memberAbility?.canDeleteNonAuthoredMessages ?? false
|
|
166
|
-
const currentlyEditingMessage = messages.find(m => String(m.id) === String(
|
|
167
|
-
const replyRootAuthorFirstName =
|
|
109
|
+
const currentlyEditingMessage = messages.find(m => String(m.id) === String(editing_message_id))
|
|
110
|
+
const replyRootAuthorFirstName = reply_root_author_name?.split(' ')[0]
|
|
168
111
|
const replyHeaderTitle = replyRootAuthorFirstName
|
|
169
112
|
? `Reply to ${replyRootAuthorFirstName}`
|
|
170
113
|
: 'Reply'
|
|
@@ -172,96 +115,21 @@ function ConversationScreenContent({ route }: ConversationScreenProps) {
|
|
|
172
115
|
const muted = conversation.conversationMembership?.muted ?? conversation.muted
|
|
173
116
|
|
|
174
117
|
const listRef = useRef<FlatList>(null)
|
|
175
|
-
const [
|
|
176
|
-
|
|
177
|
-
const observers = useMemo<ViewabilityObserver<EnrichedMessage>[]>(
|
|
178
|
-
() => [
|
|
179
|
-
reportViewableMessages(onMessageSeen),
|
|
180
|
-
detectDividerExitTowardNewer({
|
|
181
|
-
dividerKey: UNREAD_DIVIDER_KEY,
|
|
182
|
-
initialMessageId,
|
|
183
|
-
onExited: () => setDividerScrolledPast(true),
|
|
184
|
-
}),
|
|
185
|
-
],
|
|
186
|
-
[onMessageSeen, initialMessageId]
|
|
187
|
-
)
|
|
118
|
+
const [showJumpToBottomButton, setShowJumpToBottomButton] = useState(false)
|
|
188
119
|
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
onScrollToIndexFailed,
|
|
194
|
-
onScrollBeginDrag: anchorOnScrollBeginDrag,
|
|
195
|
-
} = useJumpToUnreadAnchor({ listRef, items })
|
|
196
|
-
const onScrollBeginDrag = useCallback(() => {
|
|
197
|
-
viewabilityOnScrollBeginDrag()
|
|
198
|
-
anchorOnScrollBeginDrag()
|
|
199
|
-
}, [viewabilityOnScrollBeginDrag, anchorOnScrollBeginDrag])
|
|
200
|
-
const { onScroll, showJumpToBottomButton } = useScrollTracking({
|
|
201
|
-
hasMoreNewerMessages,
|
|
202
|
-
isFetchingNewerMessages,
|
|
203
|
-
fetchNewerMessages,
|
|
204
|
-
cancelFetchNewerMessages,
|
|
205
|
-
})
|
|
206
|
-
const { handleJumpToBottom, isJumpingToBottom } = useJumpToBottomAction({ listRef })
|
|
207
|
-
|
|
208
|
-
const listHeader = useMemo(
|
|
209
|
-
() => (
|
|
210
|
-
<View>
|
|
211
|
-
{isFetchingNewerMessages && (
|
|
212
|
-
<Animated.View
|
|
213
|
-
entering={FadeIn.duration(750)}
|
|
214
|
-
exiting={FadeOut.duration(750)}
|
|
215
|
-
style={styles.loadingFooter}
|
|
216
|
-
accessibilityRole="progressbar"
|
|
217
|
-
accessibilityLabel="Loading more messages"
|
|
218
|
-
>
|
|
219
|
-
<ActivityIndicator />
|
|
220
|
-
</Animated.View>
|
|
221
|
-
)}
|
|
222
|
-
<View style={styles.listHeader} />
|
|
223
|
-
</View>
|
|
224
|
-
),
|
|
225
|
-
[isFetchingNewerMessages, styles.loadingFooter, styles.listHeader]
|
|
226
|
-
)
|
|
120
|
+
const trackScroll = (event: any) => {
|
|
121
|
+
const offsetY = event.nativeEvent.contentOffset.y
|
|
122
|
+
setShowJumpToBottomButton(offsetY > 200)
|
|
123
|
+
}
|
|
227
124
|
|
|
228
|
-
const
|
|
229
|
-
({
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
return (
|
|
234
|
-
<ReplyShadowMessage
|
|
235
|
-
{...item}
|
|
236
|
-
conversation_id={conversationId}
|
|
237
|
-
inReplyScreen={!!replyRootId}
|
|
238
|
-
/>
|
|
239
|
-
)
|
|
240
|
-
}
|
|
241
|
-
if (isSystemMessage(item)) {
|
|
242
|
-
return <SystemMessage message={item} conversationId={conversationId} />
|
|
243
|
-
}
|
|
244
|
-
return (
|
|
245
|
-
<Message
|
|
246
|
-
{...item}
|
|
247
|
-
canDeleteNonAuthoredMessages={canDeleteNonAuthoredMessages}
|
|
248
|
-
conversation_id={conversationId}
|
|
249
|
-
latestReadMessageSortKey={conversation?.latestReadMessageSortKey}
|
|
250
|
-
inReplyScreen={!!replyRootId}
|
|
251
|
-
/>
|
|
252
|
-
)
|
|
253
|
-
},
|
|
254
|
-
[
|
|
255
|
-
dividerScrolledPast,
|
|
256
|
-
conversationId,
|
|
257
|
-
replyRootId,
|
|
258
|
-
canDeleteNonAuthoredMessages,
|
|
259
|
-
conversation?.latestReadMessageSortKey,
|
|
260
|
-
]
|
|
261
|
-
)
|
|
125
|
+
const handleReturnToBottom = useCallback(() => {
|
|
126
|
+
listRef.current?.scrollToOffset({
|
|
127
|
+
offset: 0,
|
|
128
|
+
})
|
|
129
|
+
}, [])
|
|
262
130
|
|
|
263
131
|
useEffect(() => {
|
|
264
|
-
if (
|
|
132
|
+
if (reply_root_id) {
|
|
265
133
|
navigation.setParams({
|
|
266
134
|
title: replyHeaderTitle,
|
|
267
135
|
})
|
|
@@ -273,7 +141,7 @@ function ConversationScreenContent({ route }: ConversationScreenProps) {
|
|
|
273
141
|
muted,
|
|
274
142
|
})
|
|
275
143
|
}
|
|
276
|
-
}, [navigation, title, badges, conversation?.deleted,
|
|
144
|
+
}, [navigation, title, badges, conversation?.deleted, reply_root_id, replyHeaderTitle, muted])
|
|
277
145
|
|
|
278
146
|
if (!conversation || conversation.deleted) {
|
|
279
147
|
return (
|
|
@@ -304,32 +172,51 @@ function ConversationScreenContent({ route }: ConversationScreenProps) {
|
|
|
304
172
|
inverted
|
|
305
173
|
ref={listRef}
|
|
306
174
|
contentContainerStyle={styles.listContainer}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
175
|
+
data={messagesWithSeparators}
|
|
176
|
+
keyExtractor={item => item.id}
|
|
177
|
+
onScroll={trackScroll}
|
|
178
|
+
scrollEventThrottle={10}
|
|
179
|
+
renderItem={({ item }) => {
|
|
180
|
+
if (item.type === 'DateSeparator') {
|
|
181
|
+
return <InlineDateSeparator {...item} />
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (item.type === 'ReplyShadowMessage') {
|
|
185
|
+
return (
|
|
186
|
+
<ReplyShadowMessage
|
|
187
|
+
{...(item as any)}
|
|
188
|
+
conversation_id={conversation_id}
|
|
189
|
+
inReplyScreen={!!reply_root_id}
|
|
190
|
+
/>
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (isSystemMessage(item)) {
|
|
195
|
+
return <SystemMessage message={item} conversationId={conversation_id} />
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
<Message
|
|
200
|
+
{...item}
|
|
201
|
+
canDeleteNonAuthoredMessages={canDeleteNonAuthoredMessages}
|
|
202
|
+
conversation_id={conversation_id}
|
|
203
|
+
latestReadMessageSortKey={conversation?.latestReadMessageSortKey}
|
|
204
|
+
inReplyScreen={!!reply_root_id}
|
|
205
|
+
/>
|
|
206
|
+
)
|
|
207
|
+
}}
|
|
208
|
+
onEndReached={() => fetchNextPage()}
|
|
209
|
+
ListHeaderComponent={<View style={styles.listHeader} />}
|
|
319
210
|
/>
|
|
320
211
|
)}
|
|
321
|
-
<JumpToBottomButton
|
|
322
|
-
onPress={handleJumpToBottom}
|
|
323
|
-
visible={showJumpToBottomButton}
|
|
324
|
-
loading={isJumpingToBottom}
|
|
325
|
-
/>
|
|
212
|
+
<JumpToBottomButton onPress={handleReturnToBottom} visible={showJumpToBottomButton} />
|
|
326
213
|
{!noMessages && <TypingIndicator />}
|
|
327
214
|
{showLeaderDisabledReplyBanner && <LeaderMessagesDisabledBanner />}
|
|
328
215
|
<ConversationBottomBar
|
|
329
216
|
conversation={conversation}
|
|
330
217
|
canReply={canReply}
|
|
331
218
|
replyRootAuthorFirstName={replyRootAuthorFirstName}
|
|
332
|
-
replyRootId={
|
|
219
|
+
replyRootId={reply_root_id}
|
|
333
220
|
currentlyEditingMessage={currentlyEditingMessage}
|
|
334
221
|
/>
|
|
335
222
|
</KeyboardView>
|
|
@@ -506,10 +393,6 @@ const useStyles = () => {
|
|
|
506
393
|
// Just whitespace to provide space where the typing indicator can be
|
|
507
394
|
height: 16,
|
|
508
395
|
},
|
|
509
|
-
loadingFooter: {
|
|
510
|
-
paddingVertical: 12,
|
|
511
|
-
alignItems: 'center',
|
|
512
|
-
},
|
|
513
396
|
})
|
|
514
397
|
}
|
|
515
398
|
|
|
@@ -11,7 +11,7 @@ interface RecipientLinkRowProps {
|
|
|
11
11
|
accessibilityHint: string
|
|
12
12
|
imageUri?: string
|
|
13
13
|
title: string
|
|
14
|
-
subtitle
|
|
14
|
+
subtitle?: string
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export const RecipientLinkRow = ({
|
|
@@ -40,9 +40,11 @@ export const RecipientLinkRow = ({
|
|
|
40
40
|
<Text style={styles.title} numberOfLines={2}>
|
|
41
41
|
{title}
|
|
42
42
|
</Text>
|
|
43
|
-
|
|
44
|
-
{
|
|
45
|
-
|
|
43
|
+
{subtitle && (
|
|
44
|
+
<Text variant="tertiary" numberOfLines={1}>
|
|
45
|
+
{subtitle}
|
|
46
|
+
</Text>
|
|
47
|
+
)}
|
|
46
48
|
</View>
|
|
47
49
|
{Platform.OS === 'ios' && (
|
|
48
50
|
<Icon name="general.rightChevron" size={16} style={styles.icon} />
|
|
@@ -9,7 +9,8 @@ interface TeamRecipientRowProps {
|
|
|
9
9
|
|
|
10
10
|
export const TeamRecipientRow = ({ serviceType, onPress }: TeamRecipientRowProps) => {
|
|
11
11
|
const serviceTypeAccessibilityLabel = `Select ${pluralize(serviceType.teams.length, 'team')} for ${serviceType.name}`
|
|
12
|
-
const teamNames =
|
|
12
|
+
const teamNames =
|
|
13
|
+
serviceType.id > 0 ? serviceType.teams.map(team => team.name).join(', ') : undefined
|
|
13
14
|
|
|
14
15
|
return (
|
|
15
16
|
<RecipientLinkRow
|
|
@@ -2,8 +2,10 @@ import { StackActions, StaticScreenProps, useNavigation } from '@react-navigatio
|
|
|
2
2
|
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
|
3
3
|
import { useEffect } from 'react'
|
|
4
4
|
import { DefaultLoading } from '../components/page/loading'
|
|
5
|
+
import BlankState from '../components/primitive/blank_state_primitive'
|
|
5
6
|
import { useApiClient } from '../hooks'
|
|
6
7
|
import { findOrCreateServicesConversation } from '../hooks/services/use_find_or_create_services_conversation'
|
|
8
|
+
import { ResponseError } from '../utils/response_error'
|
|
7
9
|
|
|
8
10
|
export type TeamConversationRouteProps = {
|
|
9
11
|
plan_id?: number
|
|
@@ -16,7 +18,11 @@ export const TeamConversationScreen = ({ route }: TeamConversationScreenProps) =
|
|
|
16
18
|
const apiClient = useApiClient()
|
|
17
19
|
const queryClient = useQueryClient()
|
|
18
20
|
const navigation = useNavigation()
|
|
19
|
-
const {
|
|
21
|
+
const {
|
|
22
|
+
data: conversation,
|
|
23
|
+
isError,
|
|
24
|
+
error,
|
|
25
|
+
} = useQuery({
|
|
20
26
|
queryKey: ['team-conversation', route.params.team_ids, route.params.plan_id],
|
|
21
27
|
queryFn: () =>
|
|
22
28
|
findOrCreateServicesConversation({
|
|
@@ -24,6 +30,7 @@ export const TeamConversationScreen = ({ route }: TeamConversationScreenProps) =
|
|
|
24
30
|
teamIds: route.params.team_ids ?? [],
|
|
25
31
|
planId: route.params.plan_id,
|
|
26
32
|
}).then(r => r.conversation),
|
|
33
|
+
retry: (failureCount, err) => !(err instanceof ResponseError) && failureCount < 3,
|
|
27
34
|
})
|
|
28
35
|
|
|
29
36
|
useEffect(() => {
|
|
@@ -41,5 +48,30 @@ export const TeamConversationScreen = ({ route }: TeamConversationScreenProps) =
|
|
|
41
48
|
}
|
|
42
49
|
}, [conversation?.id, conversation?.title, navigation, queryClient])
|
|
43
50
|
|
|
51
|
+
if (isError) return <TeamConversationError error={error} onGoBack={navigation.goBack} />
|
|
52
|
+
|
|
44
53
|
return <DefaultLoading />
|
|
45
54
|
}
|
|
55
|
+
|
|
56
|
+
function TeamConversationError({ error, onGoBack }: { error: Error; onGoBack: () => void }) {
|
|
57
|
+
const detail =
|
|
58
|
+
error instanceof ResponseError
|
|
59
|
+
? error.errors
|
|
60
|
+
.map(e => e.detail)
|
|
61
|
+
.filter(Boolean)
|
|
62
|
+
.join('\n')
|
|
63
|
+
: ''
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<BlankState.Root>
|
|
67
|
+
<BlankState.Imagery name="people.noTextMessage" />
|
|
68
|
+
<BlankState.Content>
|
|
69
|
+
<BlankState.Heading>Can't start this conversation</BlankState.Heading>
|
|
70
|
+
<BlankState.Text>
|
|
71
|
+
{detail || 'Something went wrong while starting this conversation.'}
|
|
72
|
+
</BlankState.Text>
|
|
73
|
+
</BlankState.Content>
|
|
74
|
+
<BlankState.Button title="Go back" onPress={onGoBack} size="md" accessibilityRole="link" />
|
|
75
|
+
</BlankState.Root>
|
|
76
|
+
)
|
|
77
|
+
}
|