react-native-gifted-chat 2.8.2-alpha.0 → 2.8.2-alpha.2

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 (203) hide show
  1. package/README.md +51 -31
  2. package/package.json +28 -30
  3. package/src/Actions.tsx +1 -1
  4. package/src/Avatar.tsx +1 -1
  5. package/src/Bubble/index.tsx +24 -20
  6. package/src/Bubble/types.ts +7 -6
  7. package/src/Composer.tsx +19 -26
  8. package/src/Constant.ts +0 -1
  9. package/src/Day/index.tsx +3 -3
  10. package/src/GiftedAvatar.tsx +31 -38
  11. package/src/GiftedChat/index.tsx +59 -106
  12. package/src/GiftedChat/styles.ts +3 -0
  13. package/src/GiftedChat/types.ts +17 -67
  14. package/src/InputToolbar.tsx +27 -10
  15. package/src/LoadEarlier.tsx +22 -20
  16. package/src/Message/index.tsx +4 -18
  17. package/src/Message/types.ts +2 -2
  18. package/src/MessageAudio.tsx +19 -7
  19. package/src/MessageContainer/components/DayAnimated/index.tsx +16 -11
  20. package/src/MessageContainer/components/Item/index.tsx +10 -4
  21. package/src/MessageContainer/components/Item/types.ts +1 -1
  22. package/src/MessageContainer/index.tsx +57 -38
  23. package/src/MessageContainer/types.ts +32 -7
  24. package/src/MessageImage.tsx +132 -18
  25. package/src/MessageText.tsx +24 -64
  26. package/src/MessageVideo.tsx +19 -7
  27. package/src/QuickReplies.tsx +19 -12
  28. package/src/Send.tsx +10 -5
  29. package/src/SystemMessage.tsx +10 -3
  30. package/src/Time.tsx +10 -3
  31. package/src/TypingIndicator/index.tsx +4 -3
  32. package/src/TypingIndicator/types.ts +3 -0
  33. package/src/__tests__/Actions.test.tsx +3 -4
  34. package/src/__tests__/Avatar.test.tsx +5 -6
  35. package/src/__tests__/Bubble.test.tsx +14 -19
  36. package/src/__tests__/Composer.test.tsx +3 -4
  37. package/src/__tests__/Day.test.tsx +5 -8
  38. package/src/__tests__/DayAnimated.test.tsx +12 -14
  39. package/src/__tests__/GiftedAvatar.test.tsx +3 -8
  40. package/src/__tests__/GiftedChat.test.tsx +34 -43
  41. package/src/__tests__/InputToolbar.test.tsx +3 -4
  42. package/src/__tests__/LoadEarlier.test.tsx +3 -4
  43. package/src/__tests__/Message.test.tsx +51 -58
  44. package/src/__tests__/MessageContainer.test.tsx +39 -5
  45. package/src/__tests__/MessageImage.test.tsx +12 -15
  46. package/src/__tests__/MessageText.test.tsx +7 -4
  47. package/src/__tests__/Send.test.tsx +7 -8
  48. package/src/__tests__/SystemMessage.test.tsx +12 -15
  49. package/src/__tests__/Time.test.tsx +5 -8
  50. package/src/__tests__/__snapshots__/Actions.test.tsx.snap +39 -7
  51. package/src/__tests__/__snapshots__/Bubble.test.tsx.snap +48 -50
  52. package/src/__tests__/__snapshots__/Composer.test.tsx.snap +1 -2
  53. package/src/__tests__/__snapshots__/Constant.test.tsx.snap +0 -1
  54. package/src/__tests__/__snapshots__/GiftedChat.test.tsx.snap +20 -22
  55. package/src/__tests__/__snapshots__/InputToolbar.test.tsx.snap +2 -2
  56. package/src/__tests__/__snapshots__/LoadEarlier.test.tsx.snap +37 -6
  57. package/src/__tests__/__snapshots__/Message.test.tsx.snap +146 -150
  58. package/src/__tests__/__snapshots__/MessageImage.test.tsx.snap +32 -11
  59. package/src/__tests__/__snapshots__/MessageText.test.tsx.snap +12 -8
  60. package/src/__tests__/__snapshots__/Send.test.tsx.snap +72 -10
  61. package/src/components/TouchableOpacity.tsx +45 -0
  62. package/src/reanimatedCompat.ts +27 -0
  63. package/src/types.ts +4 -2
  64. package/src/utils.ts +2 -2
  65. package/lib/Actions.d.ts +0 -14
  66. package/lib/Actions.js +0 -57
  67. package/lib/Actions.js.map +0 -1
  68. package/lib/Avatar.d.ts +0 -18
  69. package/lib/Avatar.js +0 -93
  70. package/lib/Avatar.js.map +0 -1
  71. package/lib/Bubble/index.d.ts +0 -6
  72. package/lib/Bubble/index.js +0 -242
  73. package/lib/Bubble/index.js.map +0 -1
  74. package/lib/Bubble/styles.d.ts +0 -69
  75. package/lib/Bubble/styles.js +0 -72
  76. package/lib/Bubble/styles.js.map +0 -1
  77. package/lib/Bubble/types.d.ts +0 -47
  78. package/lib/Bubble/types.js +0 -2
  79. package/lib/Bubble/types.js.map +0 -1
  80. package/lib/Color.d.ts +0 -18
  81. package/lib/Color.js +0 -18
  82. package/lib/Color.js.map +0 -1
  83. package/lib/Composer.d.ts +0 -20
  84. package/lib/Composer.js +0 -60
  85. package/lib/Composer.js.map +0 -1
  86. package/lib/Constant.d.ts +0 -10
  87. package/lib/Constant.js +0 -17
  88. package/lib/Constant.js.map +0 -1
  89. package/lib/Day/index.d.ts +0 -4
  90. package/lib/Day/index.js +0 -39
  91. package/lib/Day/index.js.map +0 -1
  92. package/lib/Day/styles.d.ts +0 -20
  93. package/lib/Day/styles.js +0 -22
  94. package/lib/Day/styles.js.map +0 -1
  95. package/lib/Day/types.d.ts +0 -9
  96. package/lib/Day/types.js +0 -2
  97. package/lib/Day/types.js.map +0 -1
  98. package/lib/GiftedAvatar.d.ts +0 -11
  99. package/lib/GiftedAvatar.js +0 -104
  100. package/lib/GiftedAvatar.js.map +0 -1
  101. package/lib/GiftedChat/index.d.ts +0 -26
  102. package/lib/GiftedChat/index.js +0 -317
  103. package/lib/GiftedChat/index.js.map +0 -1
  104. package/lib/GiftedChat/styles.d.ts +0 -6
  105. package/lib/GiftedChat/styles.js +0 -7
  106. package/lib/GiftedChat/styles.js.map +0 -1
  107. package/lib/GiftedChat/types.d.ts +0 -112
  108. package/lib/GiftedChat/types.js +0 -2
  109. package/lib/GiftedChat/types.js.map +0 -1
  110. package/lib/GiftedChatContext.d.ts +0 -9
  111. package/lib/GiftedChatContext.js +0 -9
  112. package/lib/GiftedChatContext.js.map +0 -1
  113. package/lib/InputToolbar.d.ts +0 -23
  114. package/lib/InputToolbar.js +0 -56
  115. package/lib/InputToolbar.js.map +0 -1
  116. package/lib/LoadEarlier.d.ts +0 -14
  117. package/lib/LoadEarlier.js +0 -45
  118. package/lib/LoadEarlier.js.map +0 -1
  119. package/lib/Message/index.d.ts +0 -6
  120. package/lib/Message/index.js +0 -80
  121. package/lib/Message/index.js.map +0 -1
  122. package/lib/Message/styles.d.ts +0 -21
  123. package/lib/Message/styles.js +0 -22
  124. package/lib/Message/styles.js.map +0 -1
  125. package/lib/Message/types.d.ts +0 -22
  126. package/lib/Message/types.js +0 -2
  127. package/lib/Message/types.js.map +0 -1
  128. package/lib/MessageAudio.d.ts +0 -2
  129. package/lib/MessageAudio.js +0 -14
  130. package/lib/MessageAudio.js.map +0 -1
  131. package/lib/MessageContainer/components/DayAnimated/index.d.ts +0 -5
  132. package/lib/MessageContainer/components/DayAnimated/index.js +0 -85
  133. package/lib/MessageContainer/components/DayAnimated/index.js.map +0 -1
  134. package/lib/MessageContainer/components/DayAnimated/styles.d.ts +0 -11
  135. package/lib/MessageContainer/components/DayAnimated/styles.js +0 -12
  136. package/lib/MessageContainer/components/DayAnimated/styles.js.map +0 -1
  137. package/lib/MessageContainer/components/DayAnimated/types.d.ts +0 -17
  138. package/lib/MessageContainer/components/DayAnimated/types.js +0 -2
  139. package/lib/MessageContainer/components/DayAnimated/types.js.map +0 -1
  140. package/lib/MessageContainer/components/Item/index.d.ts +0 -23
  141. package/lib/MessageContainer/components/Item/index.js +0 -88
  142. package/lib/MessageContainer/components/Item/index.js.map +0 -1
  143. package/lib/MessageContainer/components/Item/types.d.ts +0 -17
  144. package/lib/MessageContainer/components/Item/types.js +0 -2
  145. package/lib/MessageContainer/components/Item/types.js.map +0 -1
  146. package/lib/MessageContainer/index.d.ts +0 -6
  147. package/lib/MessageContainer/index.js +0 -235
  148. package/lib/MessageContainer/index.js.map +0 -1
  149. package/lib/MessageContainer/styles.d.ts +0 -35
  150. package/lib/MessageContainer/styles.js +0 -32
  151. package/lib/MessageContainer/styles.js.map +0 -1
  152. package/lib/MessageContainer/types.d.ts +0 -51
  153. package/lib/MessageContainer/types.js +0 -2
  154. package/lib/MessageContainer/types.js.map +0 -1
  155. package/lib/MessageImage.d.ts +0 -13
  156. package/lib/MessageImage.js +0 -30
  157. package/lib/MessageImage.js.map +0 -1
  158. package/lib/MessageText.d.ts +0 -19
  159. package/lib/MessageText.js +0 -69
  160. package/lib/MessageText.js.map +0 -1
  161. package/lib/MessageVideo.d.ts +0 -2
  162. package/lib/MessageVideo.js +0 -14
  163. package/lib/MessageVideo.js.map +0 -1
  164. package/lib/QuickReplies.d.ts +0 -15
  165. package/lib/QuickReplies.js +0 -101
  166. package/lib/QuickReplies.js.map +0 -1
  167. package/lib/Send.d.ts +0 -15
  168. package/lib/Send.js +0 -34
  169. package/lib/Send.js.map +0 -1
  170. package/lib/SystemMessage.d.ts +0 -11
  171. package/lib/SystemMessage.js +0 -27
  172. package/lib/SystemMessage.js.map +0 -1
  173. package/lib/Time.d.ts +0 -11
  174. package/lib/Time.js +0 -56
  175. package/lib/Time.js.map +0 -1
  176. package/lib/TypingIndicator/index.d.ts +0 -5
  177. package/lib/TypingIndicator/index.js +0 -94
  178. package/lib/TypingIndicator/index.js.map +0 -1
  179. package/lib/TypingIndicator/styles.d.ts +0 -20
  180. package/lib/TypingIndicator/styles.js +0 -22
  181. package/lib/TypingIndicator/styles.js.map +0 -1
  182. package/lib/TypingIndicator/types.d.ts +0 -3
  183. package/lib/TypingIndicator/types.js +0 -2
  184. package/lib/TypingIndicator/types.js.map +0 -1
  185. package/lib/hooks/useUpdateLayoutEffect.d.ts +0 -8
  186. package/lib/hooks/useUpdateLayoutEffect.js +0 -17
  187. package/lib/hooks/useUpdateLayoutEffect.js.map +0 -1
  188. package/lib/index.d.ts +0 -4
  189. package/lib/index.js +0 -5
  190. package/lib/index.js.map +0 -1
  191. package/lib/logging.d.ts +0 -2
  192. package/lib/logging.js +0 -5
  193. package/lib/logging.js.map +0 -1
  194. package/lib/styles.d.ts +0 -10
  195. package/lib/styles.js +0 -11
  196. package/lib/styles.js.map +0 -1
  197. package/lib/types.d.ts +0 -67
  198. package/lib/types.js +0 -2
  199. package/lib/types.js.map +0 -1
  200. package/lib/utils.d.ts +0 -5
  201. package/lib/utils.js +0 -83
  202. package/lib/utils.js.map +0 -1
  203. package/src/__tests__/__snapshots__/MessageContainer.test.tsx.snap +0 -108
@@ -1,4 +1,4 @@
1
- import React from 'react'
1
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
2
2
  import {
3
3
  Image,
4
4
  StyleSheet,
@@ -8,11 +8,16 @@ import {
8
8
  StyleProp,
9
9
  ImageStyle,
10
10
  ImageURISource,
11
+ Modal,
12
+ TouchableOpacity,
13
+ LayoutChangeEvent,
14
+ useWindowDimensions,
11
15
  } from 'react-native'
12
- // TODO: support web
13
- import Lightbox, { LightboxProps } from 'react-native-lightbox-v2'
16
+ import { BaseButton, GestureHandlerRootView, Text } from 'react-native-gesture-handler'
17
+ import { useSafeAreaInsets } from 'react-native-safe-area-context'
18
+ import Zoom from 'react-native-zoom-reanimated'
19
+ import commonStyles from './styles'
14
20
  import { IMessage } from './types'
15
- import stylesCommon from './styles'
16
21
 
17
22
  const styles = StyleSheet.create({
18
23
  image: {
@@ -22,8 +27,25 @@ const styles = StyleSheet.create({
22
27
  margin: 3,
23
28
  resizeMode: 'cover',
24
29
  },
25
- imageActive: {
26
- resizeMode: 'contain',
30
+ modalContent: {
31
+ backgroundColor: '#000',
32
+ },
33
+ modalImageContainer: {
34
+ width: '100%',
35
+ height: '100%',
36
+ },
37
+
38
+ closeButtonContainer: {
39
+ flexDirection: 'row',
40
+ justifyContent: 'flex-end',
41
+ },
42
+ closeButtonContent: {
43
+ padding: 10,
44
+ },
45
+ closeButtonIcon: {
46
+ fontSize: 20,
47
+ lineHeight: 20,
48
+ color: 'white',
27
49
  },
28
50
  })
29
51
 
@@ -33,35 +55,127 @@ export interface MessageImageProps<TMessage extends IMessage> {
33
55
  imageSourceProps?: Partial<ImageURISource>
34
56
  imageStyle?: StyleProp<ImageStyle>
35
57
  imageProps?: Partial<ImageProps>
36
- lightboxProps?: LightboxProps
37
58
  }
38
59
 
39
60
  export function MessageImage<TMessage extends IMessage = IMessage> ({
40
61
  containerStyle,
41
- lightboxProps,
42
62
  imageProps,
43
63
  imageSourceProps,
44
64
  imageStyle,
45
65
  currentMessage,
46
66
  }: MessageImageProps<TMessage>) {
67
+ const [isModalVisible, setIsModalVisible] = useState(false)
68
+ const [imageDimensions, setImageDimensions] = useState<{ width: number, height: number }>()
69
+ const windowDimensions = useWindowDimensions()
70
+
71
+ const insets = useSafeAreaInsets()
72
+
73
+ const imageSource = useMemo(() => ({
74
+ ...imageSourceProps,
75
+ uri: currentMessage?.image,
76
+ }), [imageSourceProps, currentMessage?.image])
77
+
78
+ const isImageSourceChanged = useRef(true)
79
+
80
+ const computedImageStyle = useMemo(() => [
81
+ styles.image,
82
+ imageStyle,
83
+ ], [imageStyle])
84
+
85
+ const handleImagePress = useCallback(() => {
86
+ if (!imageSource.uri)
87
+ return
88
+
89
+ setIsModalVisible(true)
90
+
91
+ if (isImageSourceChanged.current || !imageDimensions)
92
+ Image.getSize(imageSource.uri, (width, height) => {
93
+ setImageDimensions({ width, height })
94
+ })
95
+ }, [imageSource.uri, imageDimensions])
96
+
97
+ const handleModalClose = useCallback(() => {
98
+ setIsModalVisible(false)
99
+ }, [])
100
+
101
+ const handleImageLayout = useCallback((e: LayoutChangeEvent) => {
102
+ setImageDimensions({
103
+ width: e.nativeEvent.layout.width,
104
+ height: e.nativeEvent.layout.height,
105
+ })
106
+ }, [])
107
+
108
+ const modalImageDimensions = useMemo(() => {
109
+ if (!imageDimensions)
110
+ return undefined
111
+
112
+ const aspectRatio = imageDimensions.width / imageDimensions.height
113
+
114
+ let width = windowDimensions.width
115
+ let height = width / aspectRatio
116
+
117
+ if (height > windowDimensions.height) {
118
+ height = windowDimensions.height
119
+ width = height * aspectRatio
120
+ }
121
+
122
+ return {
123
+ width,
124
+ height,
125
+ }
126
+ }, [imageDimensions, windowDimensions.height, windowDimensions.width])
127
+
128
+ useEffect(() => {
129
+ isImageSourceChanged.current = true
130
+ }, [imageSource.uri])
131
+
47
132
  if (currentMessage == null)
48
133
  return null
49
134
 
50
135
  return (
51
136
  <View style={containerStyle}>
52
- {/* @ts-expect-error: Lightbox types are not fully compatible */}
53
- <Lightbox
54
- activeProps={{
55
- style: [stylesCommon.fill, styles.imageActive],
56
- }}
57
- {...lightboxProps}
58
- >
137
+ <TouchableOpacity onPress={handleImagePress}>
59
138
  <Image
60
139
  {...imageProps}
61
- style={[styles.image, imageStyle]}
62
- source={{ ...imageSourceProps, uri: currentMessage.image }}
140
+ style={computedImageStyle}
141
+ source={imageSource}
142
+ onLayout={handleImageLayout}
63
143
  />
64
- </Lightbox>
144
+ </TouchableOpacity>
145
+
146
+ <Modal
147
+ visible={isModalVisible}
148
+ onRequestClose={handleModalClose}
149
+ animationType='slide'
150
+ transparent={false}
151
+ >
152
+ <GestureHandlerRootView style={commonStyles.fill}>
153
+ <View style={[commonStyles.fill, styles.modalContent, { paddingTop: insets.top, paddingBottom: insets.bottom }]}>
154
+
155
+ {/* close button */}
156
+ <View style={styles.closeButtonContainer}>
157
+ <BaseButton onPress={handleModalClose}>
158
+ <View style={styles.closeButtonContent}>
159
+ <Text style={styles.closeButtonIcon}>
160
+ {'X'}
161
+ </Text>
162
+ </View>
163
+ </BaseButton>
164
+ </View>
165
+
166
+ <View style={[commonStyles.fill, commonStyles.centerItems]}>
167
+ <Zoom>
168
+ <Image
169
+ style={modalImageDimensions}
170
+ source={imageSource}
171
+ resizeMode='contain'
172
+ {...imageProps}
173
+ />
174
+ </Zoom>
175
+ </View>
176
+ </View>
177
+ </GestureHandlerRootView>
178
+ </Modal>
65
179
  </View>
66
180
  )
67
181
  }
@@ -1,21 +1,15 @@
1
1
  import React, { useMemo, useCallback } from 'react'
2
2
  import {
3
- Linking,
4
3
  StyleSheet,
5
4
  StyleProp,
6
5
  ViewStyle,
7
6
  TextStyle,
7
+ View,
8
8
  } from 'react-native'
9
9
 
10
- import Autolink, { AutolinkProps } from 'react-native-autolink'
11
10
  import { Match } from 'autolinker/dist/es2015'
11
+ import Autolink, { AutolinkProps } from 'react-native-autolink'
12
12
  import { LeftRightStyle, IMessage } from './types'
13
- import { error } from './logging'
14
-
15
- export interface MessageOption {
16
- title: string
17
- action: (phone: string) => void
18
- }
19
13
 
20
14
  export type MessageTextProps<TMessage extends IMessage> = {
21
15
  position?: 'left' | 'right'
@@ -41,82 +35,48 @@ export const MessageText: React.FC<MessageTextProps<IMessage>> = ({
41
35
  onPress: onPressProp,
42
36
  ...rest
43
37
  }) => {
44
- // TODO: React.memo
45
- // const shouldComponentUpdate = (nextProps: MessageTextProps<TMessage>) => {
46
- // return (
47
- // !!currentMessage &&
48
- // !!nextProps.currentMessage &&
49
- // currentMessage.text !== nextProps.currentMessage.text
50
- // )
51
- // }
52
-
53
- const onUrlPress = useCallback((url: string) => {
54
- if (/^www\./i.test(url))
55
- url = `https://${url}`
56
-
57
- Linking.openURL(url).catch(e => {
58
- error(e, 'No handler for URL:', url)
59
- })
60
- }, [])
61
-
62
- const onPhonePress = useCallback((phone: string) => {
63
- Linking.openURL(`tel:${phone}`).catch(e => {
64
- error(e, 'No handler for telephone')
65
- })
66
- }, [])
67
-
68
- const onEmailPress = useCallback((email: string) =>
69
- Linking.openURL(`mailto:${email}`).catch(e =>
70
- error(e, 'No handler for mailto')
71
- ), [])
72
-
73
38
  const linkStyle = useMemo(() => StyleSheet.flatten([
74
39
  styles.link,
75
40
  linkStyleProp?.[position],
76
41
  ]), [position, linkStyleProp])
77
42
 
78
- const handlePress = useCallback((url: string, match: Match) => {
79
- const type = match.getType()
80
-
81
- if (onPressProp)
82
- onPressProp(currentMessage, url, match)
83
- else if (type === 'url')
84
- onUrlPress(url)
85
- else if (type === 'phone')
86
- onPhonePress(url)
87
- else if (type === 'email')
88
- onEmailPress(url)
89
- }, [onUrlPress, onPhonePress, onEmailPress, onPressProp, currentMessage])
90
-
91
43
  const style = useMemo(() => [
92
- containerStyle?.[position],
93
44
  styles[`text_${position}`],
94
45
  textStyle?.[position],
95
46
  customTextStyle,
96
- ], [containerStyle, position, textStyle, customTextStyle])
47
+ ], [position, textStyle, customTextStyle])
48
+
49
+ const handlePress = useCallback((url: string, match: Match) => {
50
+ onPressProp?.(currentMessage, url, match)
51
+ }, [onPressProp, currentMessage])
97
52
 
98
53
  return (
99
- <Autolink
100
- style={style}
101
- {...rest}
102
- text={currentMessage!.text}
103
- email
104
- link
105
- linkStyle={linkStyle}
106
- onPress={handlePress}
107
- />
54
+ <View style={[styles.container, containerStyle?.[position]]}>
55
+ <Autolink
56
+ email
57
+ phone
58
+ link
59
+ {...rest}
60
+ onPress={onPressProp ? handlePress : undefined}
61
+ linkStyle={linkStyle}
62
+ style={style}
63
+ text={currentMessage!.text}
64
+ />
65
+ </View>
108
66
  )
109
67
  }
110
68
 
111
69
  const styles = StyleSheet.create({
112
- text: {
113
- fontSize: 16,
114
- lineHeight: 20,
70
+ container: {
115
71
  marginTop: 5,
116
72
  marginBottom: 5,
117
73
  marginLeft: 10,
118
74
  marginRight: 10,
119
75
  },
76
+ text: {
77
+ fontSize: 16,
78
+ lineHeight: 20,
79
+ },
120
80
  text_left: {
121
81
  color: 'black',
122
82
  },
@@ -1,16 +1,28 @@
1
- import React from 'react'
1
+ import React, { useMemo } from 'react'
2
+ import { View, Text, StyleSheet } from 'react-native'
2
3
  import Color from './Color'
3
- import { View, Text } from 'react-native'
4
+
5
+ const styles = StyleSheet.create({
6
+ container: {
7
+ padding: 20,
8
+ },
9
+ text: {
10
+ color: Color.alizarin,
11
+ fontWeight: '600',
12
+ },
13
+ })
4
14
 
5
15
  export function MessageVideo () {
6
- return (
7
- <View style={{ padding: 20 }}>
8
- <Text style={{ color: Color.alizarin, fontWeight: '600' }}>
16
+ const content = useMemo(() => (
17
+ <View style={styles.container}>
18
+ <Text style={styles.text}>
9
19
  {'Video is not implemented by GiftedChat.'}
10
20
  </Text>
11
- <Text style={{ color: Color.alizarin, fontWeight: '600' }}>
21
+ <Text style={styles.text}>
12
22
  {'\nYou need to provide your own implementation by using renderMessageVideo prop.'}
13
23
  </Text>
14
24
  </View>
15
- )
25
+ ), [])
26
+
27
+ return content
16
28
  }
@@ -3,15 +3,15 @@ import {
3
3
  Text,
4
4
  StyleSheet,
5
5
  View,
6
- TouchableOpacity,
7
6
  StyleProp,
8
7
  ViewStyle,
9
8
  TextStyle,
10
9
  } from 'react-native'
11
- import { IMessage, Reply } from './types'
12
10
  import Color from './Color'
11
+ import { TouchableOpacity } from './components/TouchableOpacity'
13
12
  import { warning } from './logging'
14
13
  import stylesCommon from './styles'
14
+ import { IMessage, Reply } from './types'
15
15
 
16
16
  const styles = StyleSheet.create({
17
17
  container: {
@@ -122,6 +122,22 @@ export function QuickReplies ({
122
122
  [replies, currentMessage, handleSend]
123
123
  )
124
124
 
125
+ const renderSendButton = useMemo(() => {
126
+ if (!replies.length)
127
+ return null
128
+
129
+ return (
130
+ <TouchableOpacity
131
+ style={[stylesCommon.centerItems, styles.quickReply, styles.sendLink]}
132
+ onPress={handleSend(replies)}
133
+ >
134
+ {renderQuickReplySend?.() || (
135
+ <Text style={styles.sendLinkText}>{sendText}</Text>
136
+ )}
137
+ </TouchableOpacity>
138
+ )
139
+ }, [replies, handleSend, renderQuickReplySend, sendText])
140
+
125
141
  if (!shouldComponentDisplay)
126
142
  return null
127
143
 
@@ -159,16 +175,7 @@ export function QuickReplies ({
159
175
  )
160
176
  }
161
177
  )}
162
- {replies.length > 0 && (
163
- <TouchableOpacity
164
- style={[stylesCommon.centerItems, styles.quickReply, styles.sendLink]}
165
- onPress={handleSend(replies)}
166
- >
167
- {renderQuickReplySend?.() || (
168
- <Text style={styles.sendLinkText}>{sendText}</Text>
169
- )}
170
- </TouchableOpacity>
171
- )}
178
+ {renderSendButton}
172
179
  </View>
173
180
  )
174
181
  }
package/src/Send.tsx CHANGED
@@ -2,17 +2,17 @@ import React, { useMemo, useCallback } from 'react'
2
2
  import {
3
3
  StyleSheet,
4
4
  Text,
5
- TouchableOpacity,
6
5
  View,
7
6
  StyleProp,
8
7
  ViewStyle,
9
8
  TextStyle,
10
- TouchableOpacityProps,
9
+ useColorScheme,
11
10
  } from 'react-native'
12
-
13
11
  import Color from './Color'
14
- import { IMessage } from './types'
12
+
13
+ import { TouchableOpacity, TouchableOpacityProps } from './components/TouchableOpacity'
15
14
  import { TEST_ID } from './Constant'
15
+ import { IMessage } from './types'
16
16
 
17
17
  const styles = StyleSheet.create({
18
18
  container: {
@@ -28,6 +28,9 @@ const styles = StyleSheet.create({
28
28
  marginLeft: 10,
29
29
  marginRight: 10,
30
30
  },
31
+ text_dark: {
32
+ color: '#4da6ff',
33
+ },
31
34
  })
32
35
 
33
36
  export interface SendProps<TMessage extends IMessage> {
@@ -56,6 +59,8 @@ export const Send = <TMessage extends IMessage = IMessage>({
56
59
  sendButtonProps,
57
60
  onSend,
58
61
  }: SendProps<TMessage>) => {
62
+ const colorScheme = useColorScheme()
63
+
59
64
  const handleOnPress = useCallback(() => {
60
65
  if (text && onSend)
61
66
  onSend({ text: text.trim() } as Partial<TMessage>, true)
@@ -81,7 +86,7 @@ export const Send = <TMessage extends IMessage = IMessage>({
81
86
  {...sendButtonProps}
82
87
  >
83
88
  <View>
84
- {children || <Text style={[styles.text, textStyle]}>{label}</Text>}
89
+ {children || <Text style={[styles.text, styles[`text_${colorScheme}`], textStyle]}>{label}</Text>}
85
90
  </View>
86
91
  </TouchableOpacity>
87
92
  )
@@ -1,4 +1,4 @@
1
- import React from 'react'
1
+ import React, { useMemo } from 'react'
2
2
  import {
3
3
  StyleSheet,
4
4
  Text,
@@ -8,8 +8,8 @@ import {
8
8
  TextStyle,
9
9
  } from 'react-native'
10
10
  import Color from './Color'
11
- import { IMessage } from './types'
12
11
  import stylesCommon from './styles'
12
+ import { IMessage } from './types'
13
13
 
14
14
  const styles = StyleSheet.create({
15
15
  container: {
@@ -39,13 +39,20 @@ export function SystemMessage<TMessage extends IMessage = IMessage> ({
39
39
  textStyle,
40
40
  children,
41
41
  }: SystemMessageProps<TMessage>) {
42
+ const textContent = useMemo(() => {
43
+ if (!currentMessage?.text)
44
+ return null
45
+
46
+ return <Text style={[styles.text, textStyle]}>{currentMessage.text}</Text>
47
+ }, [currentMessage?.text, textStyle])
48
+
42
49
  if (currentMessage == null || currentMessage.system === false)
43
50
  return null
44
51
 
45
52
  return (
46
53
  <View style={[stylesCommon.fill, stylesCommon.centerItems, styles.container, containerStyle]}>
47
54
  <View style={wrapperStyle}>
48
- {!!currentMessage.text && <Text style={[styles.text, textStyle]}>{currentMessage.text}</Text>}
55
+ {textContent}
49
56
  {children}
50
57
  </View>
51
58
  </View>
package/src/Time.tsx CHANGED
@@ -1,11 +1,11 @@
1
- import React from 'react'
1
+ import React, { useMemo } from 'react'
2
2
  import { StyleSheet, Text, View, ViewStyle, TextStyle } from 'react-native'
3
3
  import dayjs from 'dayjs'
4
4
 
5
5
  import Color from './Color'
6
6
  import { TIME_FORMAT } from './Constant'
7
- import { LeftRightStyle, IMessage } from './types'
8
7
  import { useChatContext } from './GiftedChatContext'
8
+ import { LeftRightStyle, IMessage } from './types'
9
9
 
10
10
  const { containerStyle } = StyleSheet.create({
11
11
  containerStyle: {
@@ -60,6 +60,13 @@ export function Time<TMessage extends IMessage = IMessage> ({
60
60
  }: TimeProps<TMessage>) {
61
61
  const { getLocale } = useChatContext()
62
62
 
63
+ const formattedTime = useMemo(() => {
64
+ if (currentMessage == null)
65
+ return null
66
+
67
+ return dayjs(currentMessage.createdAt).locale(getLocale()).format(timeFormat)
68
+ }, [currentMessage, getLocale, timeFormat])
69
+
63
70
  if (currentMessage == null)
64
71
  return null
65
72
 
@@ -76,7 +83,7 @@ export function Time<TMessage extends IMessage = IMessage> ({
76
83
  timeTextStyle?.[position],
77
84
  ]}
78
85
  >
79
- {dayjs(currentMessage.createdAt).locale(getLocale()).format(timeFormat)}
86
+ {formattedTime}
80
87
  </Text>
81
88
  </View>
82
89
  )
@@ -9,10 +9,10 @@ import Animated, {
9
9
  withSequence,
10
10
  withTiming,
11
11
  } from 'react-native-reanimated'
12
- import { TypingIndicatorProps } from './types'
13
-
14
12
  import stylesCommon from '../styles'
13
+
15
14
  import styles from './styles'
15
+ import { TypingIndicatorProps } from './types'
16
16
 
17
17
  export * from './types'
18
18
 
@@ -89,7 +89,7 @@ const DotsAnimation = () => {
89
89
  )
90
90
  }
91
91
 
92
- const TypingIndicator = ({ isTyping }: TypingIndicatorProps) => {
92
+ const TypingIndicator = ({ isTyping, style }: TypingIndicatorProps) => {
93
93
  const yCoords = useSharedValue(200)
94
94
  const heightScale = useSharedValue(0)
95
95
  const marginScale = useSharedValue(0)
@@ -146,6 +146,7 @@ const TypingIndicator = ({ isTyping }: TypingIndicatorProps) => {
146
146
  style={[
147
147
  styles.container,
148
148
  containerStyle,
149
+ style,
149
150
  ]}
150
151
  >
151
152
  <DotsAnimation />
@@ -1,3 +1,6 @@
1
+ import { StyleProp, ViewStyle } from 'react-native'
2
+
1
3
  export interface TypingIndicatorProps {
2
4
  isTyping?: boolean
5
+ style?: StyleProp<ViewStyle>
3
6
  }
@@ -1,10 +1,9 @@
1
- import 'react-native'
2
1
  import React from 'react'
3
- import renderer from 'react-test-renderer'
2
+ import { render } from '@testing-library/react-native'
4
3
 
5
4
  import { Actions } from '../GiftedChat'
6
5
 
7
6
  it('should render <Actions /> and compare with snapshot', () => {
8
- const tree = renderer.create(<Actions />).toJSON()
9
- expect(tree).toMatchSnapshot()
7
+ const { toJSON } = render(<Actions />)
8
+ expect(toJSON()).toMatchSnapshot()
10
9
  })
@@ -1,13 +1,12 @@
1
- import 'react-native'
2
1
  import React from 'react'
3
- import renderer from 'react-test-renderer'
2
+ import { render } from '@testing-library/react-native'
4
3
 
5
4
  import { Avatar } from '../GiftedChat'
6
5
 
7
6
  it('should render <Avatar /> and compare with snapshot', () => {
8
- const tree = renderer
9
- .create(<Avatar renderAvatar={() => 'renderAvatar'} position='left' />)
10
- .toJSON()
7
+ const { toJSON } = render(
8
+ <Avatar renderAvatar={() => 'renderAvatar'} position='left' />
9
+ )
11
10
 
12
- expect(tree).toMatchSnapshot()
11
+ expect(toJSON()).toMatchSnapshot()
13
12
  })
@@ -1,26 +1,21 @@
1
- import 'react-native'
2
1
  import React from 'react'
3
- import renderer from 'react-test-renderer'
2
+ import { render } from '@testing-library/react-native'
4
3
 
5
4
  import { Bubble } from '../GiftedChat'
6
5
 
7
6
  it('should render <Bubble /> and compare with snapshot', () => {
8
- let tree
7
+ const { toJSON } = render(
8
+ <Bubble
9
+ user={{ _id: 1 }}
10
+ currentMessage={{
11
+ _id: 1,
12
+ text: 'test',
13
+ createdAt: 1554744013721,
14
+ user: { _id: 1 },
15
+ }}
16
+ position='left'
17
+ />
18
+ )
9
19
 
10
- renderer.act(() => {
11
- tree = renderer.create(
12
- <Bubble
13
- user={{ _id: 1 }}
14
- currentMessage={{
15
- _id: 1,
16
- text: 'test',
17
- createdAt: 1554744013721,
18
- user: { _id: 1 },
19
- }}
20
- position='left'
21
- />
22
- )
23
- })
24
-
25
- expect(tree.toJSON()).toMatchSnapshot()
20
+ expect(toJSON()).toMatchSnapshot()
26
21
  })
@@ -1,11 +1,10 @@
1
- import 'react-native'
2
1
  import React from 'react'
3
- import renderer from 'react-test-renderer'
2
+ import { render } from '@testing-library/react-native'
4
3
 
5
4
  import { Composer } from '../GiftedChat'
6
5
 
7
6
  it('should render <Composer /> and compare with snapshot', () => {
8
- const tree = renderer.create(<Composer />).toJSON()
7
+ const { toJSON } = render(<Composer />)
9
8
 
10
- expect(tree).toMatchSnapshot()
9
+ expect(toJSON()).toMatchSnapshot()
11
10
  })