@planningcenter/chat-react-native 3.17.2 → 3.17.3-qa-456.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/build/components/conversation/message.d.ts.map +1 -1
  2. package/build/components/conversation/message.js +19 -16
  3. package/build/components/conversation/message.js.map +1 -1
  4. package/build/components/conversation/reply_connectors.d.ts.map +1 -1
  5. package/build/components/conversation/reply_connectors.js +55 -32
  6. package/build/components/conversation/reply_connectors.js.map +1 -1
  7. package/build/components/conversation/reply_shadow_message.js +9 -5
  8. package/build/components/conversation/reply_shadow_message.js.map +1 -1
  9. package/build/components/conversations/conversations.d.ts.map +1 -1
  10. package/build/components/conversations/conversations.js +2 -3
  11. package/build/components/conversations/conversations.js.map +1 -1
  12. package/build/screens/conversation_details_screen.d.ts.map +1 -1
  13. package/build/screens/conversation_details_screen.js +3 -4
  14. package/build/screens/conversation_details_screen.js.map +1 -1
  15. package/build/screens/conversation_new/components/form_list.d.ts +2 -2
  16. package/build/screens/conversation_new/components/form_list.d.ts.map +1 -1
  17. package/build/screens/conversation_new/components/form_list.js +2 -3
  18. package/build/screens/conversation_new/components/form_list.js.map +1 -1
  19. package/build/screens/conversation_screen.d.ts.map +1 -1
  20. package/build/screens/conversation_screen.js +1 -1
  21. package/build/screens/conversation_screen.js.map +1 -1
  22. package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.d.ts.map +1 -1
  23. package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.js +2 -3
  24. package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.js.map +1 -1
  25. package/build/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.d.ts.map +1 -1
  26. package/build/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.js +2 -3
  27. package/build/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.js.map +1 -1
  28. package/package.json +2 -3
  29. package/src/components/conversation/message.tsx +25 -19
  30. package/src/components/conversation/reply_connectors.tsx +90 -32
  31. package/src/components/conversation/reply_shadow_message.tsx +10 -4
  32. package/src/components/conversations/conversations.tsx +7 -9
  33. package/src/screens/conversation_details_screen.tsx +3 -4
  34. package/src/screens/conversation_new/components/form_list.tsx +3 -5
  35. package/src/screens/conversation_screen.tsx +1 -2
  36. package/src/screens/conversation_select_recipients/conversation_select_group_recipients_screen.tsx +2 -4
  37. package/src/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.tsx +2 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/chat-react-native",
3
- "version": "3.17.2",
3
+ "version": "3.17.3-qa-456.0",
4
4
  "description": "",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -29,7 +29,6 @@
29
29
  "@react-navigation/elements": "*",
30
30
  "@react-navigation/native": ">=7.0.0",
31
31
  "@react-navigation/native-stack": ">=7.0.0",
32
- "@shopify/flash-list": "<2.0.0",
33
32
  "@tanstack/react-query": "^5.0.0",
34
33
  "color": "^3.1.2",
35
34
  "lodash": "*",
@@ -59,5 +58,5 @@
59
58
  "react-native-url-polyfill": "^2.0.0",
60
59
  "typescript": "<5.6.0"
61
60
  },
62
- "gitHead": "0dae32d2726a5eb02650377356a9cccf65968166"
61
+ "gitHead": "d509a7a88fb84b1517a4b0dc70be90dfd316a63a"
63
62
  }
@@ -66,10 +66,31 @@ export function Message({
66
66
  enabled: !!message.replyRootId,
67
67
  })
68
68
 
69
+ const metaProps = {
70
+ authorName: message.author.name,
71
+ createdAt: message.createdAt,
72
+ }
73
+
74
+ const renderAuthor = (!message.mine && message.renderAuthor) || false
75
+ const showReplyCountButton =
76
+ !inReplyScreen && message.replyRootId === message.id && REPLIES_FEATURE_ENABLED
77
+ const isReplyRootMessage = message.replyRootId === message.id
78
+ const isDeletedReplyRootMessage = isReplyRootMessage && !!message.deletedAt
79
+
80
+ const messageText = isDeletedReplyRootMessage ? 'Message deleted' : text
81
+ const replyCountText = pluralize(message.replyCount, 'reply')
82
+ const messagePendingLabel = isPersisted ? 'Saving' : 'Sending'
69
83
  const replyRootAuthorName = message.replyRootId
70
84
  ? replyRootMessage?.author.name
71
85
  : message.author.name
72
86
 
87
+ const messageBottomMargin =
88
+ message.lastInGroup || message.nextIsReplyShadowMessage
89
+ ? 16
90
+ : hasReactions || showMessageMeta || showReplyCountButton
91
+ ? 8
92
+ : 4
93
+
73
94
  useEffect(() => {
74
95
  if (pending) {
75
96
  const timer = setTimeout(() => {
@@ -135,23 +156,6 @@ export function Message({
135
156
  })
136
157
  }
137
158
 
138
- const metaProps = {
139
- authorName: message.author.name,
140
- createdAt: message.createdAt,
141
- }
142
-
143
- const renderAuthor = (!message.mine && message.renderAuthor) || false
144
- const messagePendingLabel = isPersisted ? 'Saving' : 'Sending'
145
- const showReplyCountButton =
146
- !inReplyScreen && message.replyRootId === message.id && REPLIES_FEATURE_ENABLED
147
- const replyCountText = pluralize(message.replyCount, 'reply')
148
- const messageBottomMargin =
149
- message.lastInGroup || message.nextIsReplyShadowMessage
150
- ? 16
151
- : hasReactions || showMessageMeta || showReplyCountButton
152
- ? 8
153
- : 4
154
-
155
159
  return (
156
160
  <Pressable
157
161
  onLongPress={handleMessageLongPress}
@@ -171,6 +175,8 @@ export function Message({
171
175
  style={styles.avatar}
172
176
  maxFontSizeMultiplier={1}
173
177
  minFontSizeMultiplier={1}
178
+ fallbackIconName={isDeletedReplyRootMessage ? 'publishing.trash' : 'general.person'}
179
+ showFallback={isDeletedReplyRootMessage}
174
180
  />
175
181
  ) : (
176
182
  <View style={styles.avatarPlaceholder} />
@@ -196,9 +202,9 @@ export function Message({
196
202
  onMessageLongPress={handleMessageLongPress}
197
203
  />
198
204
  </ErrorBoundary>
199
- {text && (
205
+ {messageText && (
200
206
  <View style={styles.messageText}>
201
- <MessageMarkdown text={text} />
207
+ <MessageMarkdown text={messageText} />
202
208
  </View>
203
209
  )}
204
210
  </View>
@@ -94,7 +94,11 @@ export function MyReplyConnector({ message, messageBubbleHeight }: ReplyConnecto
94
94
 
95
95
  const spacerStyle = nextRendersAuthor ? styles.myReplyConnectorSpacer : null
96
96
 
97
- return <View style={[styles.myReplyConnectorContainer, spacerStyle]}>{ConnectorComponent}</View>
97
+ return (
98
+ <EnforceLeftPositionForMyReplyConnector>
99
+ <View style={[styles.myReplyConnectorContainer, spacerStyle]}>{ConnectorComponent}</View>
100
+ </EnforceLeftPositionForMyReplyConnector>
101
+ )
98
102
  }
99
103
 
100
104
  function VerticalLineConnector({ style }: { style?: ViewStyle }) {
@@ -104,22 +108,74 @@ function VerticalLineConnector({ style }: { style?: ViewStyle }) {
104
108
 
105
109
  function LongHeadCurveConnector({ height, style }: { height: number; style?: ViewStyle }) {
106
110
  const styles = useStyles()
107
- return <View style={[styles.longHeadCurveConnector, { height, marginTop: height }, style]} />
111
+ const { colors } = useTheme()
112
+
113
+ return (
114
+ <View style={[styles.longHeadCurveConnector, { marginTop: height }, style]}>
115
+ <Svg width={MY_REPLY_CONNECTOR_WIDTH} height={18} fill="none">
116
+ <Path
117
+ d="M 2 18 L 2 16 A 14 14 0 0 1 16 2 L 38 2"
118
+ stroke={colors.borderColorDefaultBase}
119
+ strokeWidth={CONNECTOR_BORDER_WIDTH}
120
+ />
121
+ </Svg>
122
+ <View style={styles.svgConnnectorVerticalLineTail} />
123
+ </View>
124
+ )
108
125
  }
109
126
 
110
127
  function LongTailCurveConnector({ height, style }: { height: number; style?: ViewStyle }) {
111
128
  const styles = useStyles()
112
- return <View style={[styles.longTailCurveConnector, { height }, style]} />
129
+ const { colors } = useTheme()
130
+
131
+ return (
132
+ <View style={[{ height }, style]}>
133
+ <View style={styles.svgConnnectorVerticalLineHead} />
134
+ <Svg width={MY_REPLY_CONNECTOR_WIDTH} height={18} fill="none">
135
+ <Path
136
+ d="M 2 0 L 2 2 A 14 14 0 0 0 16 16 L 38 16"
137
+ stroke={colors.borderColorDefaultBase}
138
+ strokeWidth={CONNECTOR_BORDER_WIDTH}
139
+ />
140
+ </Svg>
141
+ </View>
142
+ )
113
143
  }
114
144
 
115
145
  function ShortHeadCurveConnector({ height }: { height: number }) {
116
146
  const styles = useStyles()
117
- return <View style={[styles.shortHeadCurveConnector, { marginTop: height }]} />
147
+ const { colors } = useTheme()
148
+
149
+ return (
150
+ <View style={[styles.shortHeadCurveConnector, { marginTop: height }]}>
151
+ <Svg width={20} height={18} fill="none">
152
+ <Path
153
+ d="M 2 18 L 2 16 A 14 14 0 0 1 16 2 L 20 2"
154
+ stroke={colors.borderColorDefaultBase}
155
+ strokeWidth={CONNECTOR_BORDER_WIDTH}
156
+ />
157
+ </Svg>
158
+ <View style={styles.svgConnnectorVerticalLineTail} />
159
+ </View>
160
+ )
118
161
  }
119
162
 
120
163
  function ShortTailCurveConnector({ height }: { height: number }) {
121
164
  const styles = useStyles()
122
- return <View style={[styles.shortTailCurveConnector, { height }]} />
165
+ const { colors } = useTheme()
166
+
167
+ return (
168
+ <View style={{ height }}>
169
+ <View style={styles.svgConnnectorVerticalLineHead} />
170
+ <Svg width={20} height={18} fill="none">
171
+ <Path
172
+ d="M 2 0 L 2 2 A 14 14 0 0 0 16 16 L 20 16"
173
+ stroke={colors.borderColorDefaultBase}
174
+ strokeWidth={CONNECTOR_BORDER_WIDTH}
175
+ />
176
+ </Svg>
177
+ </View>
178
+ )
123
179
  }
124
180
 
125
181
  function SwirlConnector({ style, height }: { style?: ViewStyle; height: number }) {
@@ -129,7 +185,7 @@ function SwirlConnector({ style, height }: { style?: ViewStyle; height: number }
129
185
  return (
130
186
  <View style={[styles.swirlConnectorContainer, style]}>
131
187
  <View style={{ height }}>
132
- <View style={styles.swirlConnectorVerticalLineHead} />
188
+ <View style={styles.svgConnnectorVerticalLineHead} />
133
189
  <Svg width={27} height={34} fill="none">
134
190
  <Path
135
191
  stroke={colors.borderColorDefaultBase}
@@ -137,9 +193,25 @@ function SwirlConnector({ style, height }: { style?: ViewStyle; height: number }
137
193
  d="M2.07 34c0-6.142 1.201-12.283 3.343-17m0 0c2.305-5.075 5.699-8.5 9.857-8.5 4.86 0 8.8 3.806 8.8 8.5s-3.94 8.5-8.8 8.5c-4.158 0-7.552-3.425-9.857-8.5zm0 0C3.271 12.283 2.07 6.142 2.07 0"
138
194
  />
139
195
  </Svg>
140
- <View style={styles.swirlConnectorVerticalLineTail} />
196
+ <View style={styles.svgConnnectorVerticalLineTail} />
141
197
  </View>
142
- <View style={styles.swirlConnectorVerticalLineTail} />
198
+ <View style={styles.svgConnnectorVerticalLineTail} />
199
+ </View>
200
+ )
201
+ }
202
+
203
+ // TODO: Remove when Services React Native bumps to React Native 0.74 or higher.
204
+ // This fixes a React Native bug where using `flexDirection: 'row-reverse'` also switches the `left`/`right` absolute positioning values.
205
+ // The wrapper ensures the MyReplyConnector is always positioned to the left of the message bubble on older versions of React Native.
206
+ // Long term fix is described here: https://reactnative.dev/blog/2024/04/22/release-0.74#new-layout-behaviors
207
+ function EnforceLeftPositionForMyReplyConnector({ children }: { children: React.ReactNode }) {
208
+ const styles = useStyles()
209
+ return (
210
+ <View
211
+ style={styles.resetPosition}
212
+ pointerEvents="none" // ensures wrapper does not block press events
213
+ >
214
+ {children}
143
215
  </View>
144
216
  )
145
217
  }
@@ -177,47 +249,33 @@ const useStyles = () => {
177
249
  borderLeftColor: borderColor,
178
250
  },
179
251
  longHeadCurveConnector: {
180
- width: MY_REPLY_CONNECTOR_WIDTH,
181
- borderColor: borderColor,
182
- borderLeftWidth: CONNECTOR_BORDER_WIDTH,
183
- borderTopWidth: CONNECTOR_BORDER_WIDTH,
184
- borderTopLeftRadius: 16,
185
252
  flex: 1,
186
253
  },
187
- longTailCurveConnector: {
188
- width: MY_REPLY_CONNECTOR_WIDTH,
189
- borderColor: borderColor,
190
- borderLeftWidth: CONNECTOR_BORDER_WIDTH,
191
- borderBottomWidth: CONNECTOR_BORDER_WIDTH,
192
- borderBottomLeftRadius: 16,
193
- },
194
254
  shortHeadCurveConnector: {
195
- borderColor: borderColor,
196
- borderLeftWidth: CONNECTOR_BORDER_WIDTH,
197
- borderTopWidth: CONNECTOR_BORDER_WIDTH,
198
- borderTopLeftRadius: 16,
199
255
  flex: 1,
200
256
  },
201
- shortTailCurveConnector: {
202
- borderColor: borderColor,
203
- borderLeftWidth: CONNECTOR_BORDER_WIDTH,
204
- borderBottomWidth: CONNECTOR_BORDER_WIDTH,
205
- borderBottomLeftRadius: 16,
206
- },
207
257
  swirlConnectorContainer: {
208
258
  flex: 1,
209
259
  },
210
- swirlConnectorVerticalLineHead: {
260
+ svgConnnectorVerticalLineHead: {
211
261
  backgroundColor: borderColor,
212
262
  width: CONNECTOR_BORDER_WIDTH,
213
263
  flex: 1,
214
264
  marginBottom: -1, // Ensures there is no gap between the vertical line and the swirl connector
215
265
  },
216
- swirlConnectorVerticalLineTail: {
266
+ svgConnnectorVerticalLineTail: {
217
267
  backgroundColor: borderColor,
218
268
  width: CONNECTOR_BORDER_WIDTH,
219
269
  flex: 1,
220
270
  marginTop: -1, // Ensures there is no gap between the vertical line and the swirl connector
221
271
  },
272
+ // TODO: Remove when Services React Native bumps to React Native 0.74 or higher.
273
+ resetPosition: {
274
+ position: 'absolute',
275
+ left: 0,
276
+ right: 0,
277
+ top: 0,
278
+ bottom: 0,
279
+ },
222
280
  })
223
281
  }
@@ -45,7 +45,7 @@ export function ReplyShadowMessage({
45
45
 
46
46
  if (inReplyScreen) return null
47
47
  if (isLoading) return <ShadowMessageFallback text="Loading..." />
48
- if (isError || !message) return <ShadowMessageFallback text="Message not found" />
48
+ if (isError || !message) return <ShadowMessageFallback text="Message deleted" />
49
49
 
50
50
  const enrichedMessage = {
51
51
  ...message,
@@ -61,7 +61,7 @@ interface ShadowMessageContentProps extends MessageResource {
61
61
  }
62
62
 
63
63
  function ShadowMessageContent({ conversation_id, ...message }: ShadowMessageContentProps) {
64
- const { text } = message
64
+ const { text, deletedAt } = message
65
65
  const styles = useStyles(message)
66
66
  const { colors } = useTheme()
67
67
  const navigation = useNavigation()
@@ -71,6 +71,8 @@ function ShadowMessageContent({ conversation_id, ...message }: ShadowMessageCont
71
71
  useAnimatedMessageBackgroundColor()
72
72
  const scalableNumberOfLines = useScalableNumberOfLines(2)
73
73
  const replyCountText = pluralize(message.replyCount, 'reply')
74
+ const isDeleted = !!deletedAt
75
+ const messageText = isDeleted ? 'Message deleted' : text
74
76
 
75
77
  const handleNavigateToReplies = () => {
76
78
  navigation.navigate('ConversationReply', {
@@ -92,12 +94,14 @@ function ShadowMessageContent({ conversation_id, ...message }: ShadowMessageCont
92
94
  <Animated.View style={[styles.message, animatedBackgroundColor]}>
93
95
  {!message.mine && (
94
96
  <View>
95
- <View style={styles.avatarWrapper}>
97
+ <View style={[styles.avatarWrapper, !isDeleted && styles.avatarOpacity]}>
96
98
  <Avatar
97
99
  size="xs"
98
100
  sourceUri={message.author.avatar}
99
101
  style={styles.avatar}
100
102
  maxFontSizeMultiplier={1}
103
+ fallbackIconName={isDeleted ? 'publishing.trash' : 'general.person'}
104
+ showFallback={isDeleted}
101
105
  />
102
106
  </View>
103
107
  <TheirReplyConnector message={message} messageBubbleHeight={messageBubbleHeight} />
@@ -115,7 +119,7 @@ function ShadowMessageContent({ conversation_id, ...message }: ShadowMessageCont
115
119
  style={styles.messageText}
116
120
  numberOfLines={scalableNumberOfLines}
117
121
  >
118
- {text}
122
+ {messageText}
119
123
  </Text>
120
124
  )}
121
125
  </View>
@@ -281,6 +285,8 @@ const useStyles = ({ mine, imageWidth = 32, imageHeight = 32 }: StylesProps = {}
281
285
  },
282
286
  avatar: {
283
287
  marginBottom: 8,
288
+ },
289
+ avatarOpacity: {
284
290
  opacity: 0.5,
285
291
  },
286
292
  messageBubble: {
@@ -1,7 +1,6 @@
1
1
  import { useNavigation } from '@react-navigation/native'
2
- import { FlashList } from '@shopify/flash-list'
3
2
  import React, { useMemo } from 'react'
4
- import { StyleSheet, View } from 'react-native'
3
+ import { FlatList, StyleSheet, View } from 'react-native'
5
4
  import { useConversationsContext } from '../../contexts/conversations_context'
6
5
  import { useTheme } from '../../hooks'
7
6
  import { ConversationResource } from '../../types'
@@ -35,7 +34,7 @@ export const Conversations = ({ ListHeaderComponent }: ConversationsProps) => {
35
34
 
36
35
  const showBadges = !chat_group_graph_id
37
36
 
38
- const data: FlashListItem[] = useMemo(() => {
37
+ const data: FlatListItem[] = useMemo(() => {
39
38
  if (isLoading) {
40
39
  return loadingPlaceholder
41
40
  }
@@ -54,9 +53,8 @@ export const Conversations = ({ ListHeaderComponent }: ConversationsProps) => {
54
53
 
55
54
  return (
56
55
  <View style={styles.container}>
57
- <FlashList
56
+ <FlatList
58
57
  data={data}
59
- estimatedItemSize={97}
60
58
  keyExtractor={item => item.id.toString()}
61
59
  contentContainerStyle={styles.contentContainer}
62
60
  onRefresh={refetch}
@@ -114,18 +112,18 @@ const useStyles = () => {
114
112
  })
115
113
  }
116
114
 
117
- interface FlashListLoadingItem {
115
+ interface FlatListLoadingItem {
118
116
  type: 'loading'
119
117
  id: string
120
118
  }
121
- interface FlashListConversationItem {
119
+ interface FlatListConversationItem {
122
120
  type: 'conversation'
123
121
  resource: ConversationResource
124
122
  id: number
125
123
  }
126
- type FlashListItem = FlashListLoadingItem | FlashListConversationItem
124
+ type FlatListItem = FlatListLoadingItem | FlatListConversationItem
127
125
 
128
- const loadingPlaceholder: FlashListItem[] = Array.from({ length: 5 }, (_, i) => ({
126
+ const loadingPlaceholder: FlatListItem[] = Array.from({ length: 5 }, (_, i) => ({
129
127
  type: 'loading',
130
128
  id: `loading${i}`,
131
129
  }))
@@ -9,6 +9,7 @@ import React, {
9
9
  useRef,
10
10
  } from 'react'
11
11
  import {
12
+ FlatList,
12
13
  StyleSheet,
13
14
  TextInput,
14
15
  View,
@@ -40,7 +41,6 @@ import {
40
41
  } from '../hooks/use_conversation'
41
42
  import { MemberResource, isDefined } from '../types'
42
43
  import { HeaderTextButton } from '../components/display/platform_modal_header_buttons'
43
- import { FlashList } from '@shopify/flash-list'
44
44
  import { tokens } from '../vendor/tapestry/tokens'
45
45
  import { ButtonAppearanceUnion } from '../components/display/utils/button_colors'
46
46
  import { GroupResource } from '../types/resources/group_resource'
@@ -300,9 +300,8 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
300
300
 
301
301
  return (
302
302
  <View style={styles.listContainer}>
303
- <FlashList
303
+ <FlatList
304
304
  data={listData as SectionListData}
305
- estimatedItemSize={52}
306
305
  contentContainerStyle={styles.contentContainer}
307
306
  renderItem={({ item, index }) => {
308
307
  const [isStart, isEnd] = [
@@ -375,7 +374,7 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
375
374
  return null
376
375
  }
377
376
  }}
378
- onEndReached={fetchNextPageOfMembers}
377
+ onEndReached={() => fetchNextPageOfMembers()}
379
378
  />
380
379
  </View>
381
380
  )
@@ -1,6 +1,5 @@
1
1
  import React from 'react'
2
- import { StyleSheet, View } from 'react-native'
3
- import { FlashList, type FlashListProps } from '@shopify/flash-list'
2
+ import { FlatList, type FlatListProps, StyleSheet, View } from 'react-native'
4
3
  import { MemberResource } from '../../../types'
5
4
  import { Person, Text } from '../../../components/display'
6
5
  import { useTheme } from '../../../hooks'
@@ -8,7 +7,7 @@ import { useTheme } from '../../../hooks'
8
7
  interface FormListProps {
9
8
  memberData: MemberResource[]
10
9
  loadingMore?: boolean
11
- FormContent?: FlashListProps<MemberResource>['ListHeaderComponent']
10
+ FormContent?: FlatListProps<MemberResource>['ListHeaderComponent']
12
11
  listEmptyText?: string
13
12
  onEndReached?: () => void
14
13
  }
@@ -23,7 +22,7 @@ export const FormList = ({
23
22
  const styles = useStyles()
24
23
 
25
24
  return (
26
- <FlashList
25
+ <FlatList
27
26
  data={memberData}
28
27
  ListHeaderComponent={FormContent}
29
28
  renderItem={({ item }) => <Person person={item} style={styles.person} />}
@@ -31,7 +30,6 @@ export const FormList = ({
31
30
  loadingMore ? <Text style={styles.loadingMore}>Loading more...</Text> : null
32
31
  }
33
32
  keyExtractor={item => item.id.toString()}
34
- estimatedItemSize={45}
35
33
  ListEmptyComponent={<ListEmptyText text={listEmptyText || 'No members found'} />}
36
34
  onEndReached={onEndReached}
37
35
  />
@@ -314,8 +314,7 @@ export const groupMessages = ({ ms, inReplyScreen }: GroupMessagesProps) => {
314
314
  message.threadPosition = null // ensures we don't render a connector for root replies that aren't immediately followed up a reply
315
315
  else if (firstInThread) message.threadPosition = 'first'
316
316
  else if (lastInThread) message.threadPosition = 'last'
317
- else if (!prevMessageDifferentThread && !nextMessageDifferentThread)
318
- message.threadPosition = 'center'
317
+ else message.threadPosition = 'center'
319
318
  }
320
319
 
321
320
  enrichedMessages.push(message)
@@ -1,7 +1,6 @@
1
1
  import { useNavigation } from '@react-navigation/native'
2
2
  import React from 'react'
3
- import { StyleSheet, View } from 'react-native'
4
- import { FlashList } from '@shopify/flash-list'
3
+ import { FlatList, StyleSheet, View } from 'react-native'
5
4
  import { Heading } from '../../components'
6
5
  import { GroupsGroupResource } from '../../types'
7
6
  import { useGroupsGroups } from '../../hooks/use_groups_groups'
@@ -31,10 +30,9 @@ export const ConversationSelectGroupRecipientsScreen = ({
31
30
  }
32
31
 
33
32
  return (
34
- <FlashList
33
+ <FlatList
35
34
  data={groupsWithCreatePermission}
36
35
  keyExtractor={item => item.id.toString()}
37
- estimatedItemSize={65}
38
36
  contentContainerStyle={styles.contentContainer}
39
37
  ListHeaderComponent={
40
38
  <View style={styles.sectionHeader}>
@@ -1,7 +1,6 @@
1
1
  import { useNavigation } from '@react-navigation/native'
2
2
  import React from 'react'
3
- import { StyleSheet, View } from 'react-native'
4
- import { FlashList } from '@shopify/flash-list'
3
+ import { FlatList, StyleSheet, View } from 'react-native'
5
4
  import { Heading } from '../../components'
6
5
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
7
6
  import { ConversationSelectRecipientsScreenProps } from './types/screen_props'
@@ -30,10 +29,9 @@ export const ConversationSelectTeamsILeadRecipientsScreen = ({
30
29
  }
31
30
 
32
31
  return (
33
- <FlashList
32
+ <FlatList
34
33
  data={serviceTypes}
35
34
  keyExtractor={item => item.id.toString()}
36
- estimatedItemSize={65}
37
35
  contentContainerStyle={styles.contentContainer}
38
36
  ListHeaderComponent={
39
37
  <View style={styles.sectionHeader}>