react-native-gifted-chat 2.4.1 → 2.6.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 (159) hide show
  1. package/README.md +34 -14
  2. package/lib/Actions.d.ts +5 -6
  3. package/lib/Actions.js +16 -13
  4. package/lib/Actions.js.map +1 -1
  5. package/lib/Avatar.d.ts +9 -25
  6. package/lib/Avatar.js +12 -18
  7. package/lib/Avatar.js.flow +1 -1
  8. package/lib/Avatar.js.map +1 -1
  9. package/lib/Bubble.d.ts +29 -30
  10. package/lib/Bubble.js +99 -92
  11. package/lib/Bubble.js.flow +2 -2
  12. package/lib/Bubble.js.map +1 -1
  13. package/lib/Composer.d.ts +1 -1
  14. package/lib/Composer.js +30 -32
  15. package/lib/Composer.js.map +1 -1
  16. package/lib/Constant.js +1 -0
  17. package/lib/Constant.js.map +1 -1
  18. package/lib/Day.d.ts +3 -15
  19. package/lib/Day.js +2 -14
  20. package/lib/Day.js.flow +1 -1
  21. package/lib/Day.js.map +1 -1
  22. package/lib/GiftedAvatar.d.ts +7 -7
  23. package/lib/GiftedAvatar.js +30 -29
  24. package/lib/GiftedAvatar.js.map +1 -1
  25. package/lib/GiftedChat.d.ts +15 -89
  26. package/lib/GiftedChat.js +204 -350
  27. package/lib/GiftedChat.js.flow +1 -3
  28. package/lib/GiftedChat.js.map +1 -1
  29. package/lib/GiftedChatContext.d.ts +2 -1
  30. package/lib/GiftedChatContext.js.map +1 -1
  31. package/lib/InputToolbar.d.ts +7 -5
  32. package/lib/InputToolbar.js +41 -34
  33. package/lib/InputToolbar.js.map +1 -1
  34. package/lib/LoadEarlier.d.ts +4 -4
  35. package/lib/LoadEarlier.js +8 -6
  36. package/lib/LoadEarlier.js.map +1 -1
  37. package/lib/Message.d.ts +8 -9
  38. package/lib/Message.js +47 -40
  39. package/lib/Message.js.flow +1 -1
  40. package/lib/Message.js.map +1 -1
  41. package/lib/MessageAudio.d.ts +2 -1
  42. package/lib/MessageAudio.js +4 -4
  43. package/lib/MessageAudio.js.flow +1 -1
  44. package/lib/MessageAudio.js.map +1 -1
  45. package/lib/MessageContainer.d.ts +17 -17
  46. package/lib/MessageContainer.js +33 -51
  47. package/lib/MessageContainer.js.map +1 -1
  48. package/lib/MessageImage.d.ts +5 -4
  49. package/lib/MessageImage.js +4 -5
  50. package/lib/MessageImage.js.flow +1 -1
  51. package/lib/MessageImage.js.map +1 -1
  52. package/lib/MessageText.d.ts +11 -10
  53. package/lib/MessageText.js +5 -10
  54. package/lib/MessageText.js.flow +1 -1
  55. package/lib/MessageText.js.map +1 -1
  56. package/lib/MessageVideo.d.ts +2 -1
  57. package/lib/MessageVideo.js +4 -4
  58. package/lib/MessageVideo.js.flow +1 -1
  59. package/lib/MessageVideo.js.map +1 -1
  60. package/lib/Models.d.ts +7 -7
  61. package/lib/QuickReplies.d.ts +3 -3
  62. package/lib/QuickReplies.js +8 -14
  63. package/lib/QuickReplies.js.flow +1 -1
  64. package/lib/QuickReplies.js.map +1 -1
  65. package/lib/Send.d.ts +4 -4
  66. package/lib/Send.js +6 -9
  67. package/lib/Send.js.map +1 -1
  68. package/lib/SystemMessage.d.ts +6 -5
  69. package/lib/SystemMessage.js +1 -2
  70. package/lib/SystemMessage.js.flow +1 -1
  71. package/lib/SystemMessage.js.map +1 -1
  72. package/lib/Time.d.ts +7 -6
  73. package/lib/Time.js +1 -3
  74. package/lib/Time.js.flow +1 -1
  75. package/lib/Time.js.map +1 -1
  76. package/lib/TypingIndicator.d.ts +2 -1
  77. package/lib/TypingIndicator.js +5 -5
  78. package/lib/TypingIndicator.js.map +1 -1
  79. package/lib/hooks/useUpdateLayoutEffect.js +2 -4
  80. package/lib/hooks/useUpdateLayoutEffect.js.map +1 -1
  81. package/lib/index.d.ts +1 -0
  82. package/lib/index.js +1 -0
  83. package/lib/index.js.map +1 -1
  84. package/lib/logging.d.ts +2 -2
  85. package/lib/logging.js.map +1 -1
  86. package/lib/types.js.flow +1 -1
  87. package/lib/utils.d.ts +1 -1
  88. package/lib/utils.js +2 -4
  89. package/lib/utils.js.map +1 -1
  90. package/package.json +86 -59
  91. package/src/Actions.tsx +114 -0
  92. package/src/Avatar.tsx +178 -0
  93. package/src/Bubble.tsx +596 -0
  94. package/src/Color.ts +17 -0
  95. package/src/Composer.tsx +147 -0
  96. package/src/Constant.ts +18 -0
  97. package/src/Day.tsx +71 -0
  98. package/src/GiftedAvatar.tsx +205 -0
  99. package/src/GiftedChat.tsx +670 -0
  100. package/src/GiftedChatContext.ts +23 -0
  101. package/src/InputToolbar.tsx +113 -0
  102. package/src/LoadEarlier.tsx +108 -0
  103. package/src/Message.tsx +229 -0
  104. package/src/MessageAudio.tsx +19 -0
  105. package/src/MessageContainer.tsx +362 -0
  106. package/src/MessageImage.tsx +78 -0
  107. package/src/MessageText.tsx +187 -0
  108. package/src/MessageVideo.tsx +19 -0
  109. package/src/Models.ts +84 -0
  110. package/src/QuickReplies.tsx +186 -0
  111. package/src/Send.tsx +102 -0
  112. package/src/SystemMessage.tsx +61 -0
  113. package/src/Time.tsx +97 -0
  114. package/src/TypingIndicator.tsx +108 -0
  115. package/src/__tests__/Actions.test.tsx +10 -0
  116. package/src/__tests__/Avatar.test.tsx +13 -0
  117. package/src/__tests__/Bubble.test.tsx +23 -0
  118. package/src/__tests__/Color.test.tsx +5 -0
  119. package/src/__tests__/Composer.test.tsx +11 -0
  120. package/src/__tests__/Constant.test.tsx +5 -0
  121. package/src/__tests__/Day.test.tsx +23 -0
  122. package/src/__tests__/GiftedAvatar.test.tsx +11 -0
  123. package/src/__tests__/GiftedChat.test.tsx +36 -0
  124. package/src/__tests__/InputToolbar.test.tsx +11 -0
  125. package/src/__tests__/LoadEarlier.test.tsx +11 -0
  126. package/src/__tests__/Message.test.tsx +77 -0
  127. package/src/__tests__/MessageContainer.test.tsx +11 -0
  128. package/src/__tests__/MessageImage.test.tsx +27 -0
  129. package/src/__tests__/MessageText.test.tsx +11 -0
  130. package/src/__tests__/Send.test.tsx +22 -0
  131. package/src/__tests__/SystemMessage.test.tsx +27 -0
  132. package/src/__tests__/Time.test.tsx +29 -0
  133. package/src/__tests__/__snapshots__/Actions.test.tsx.snap +76 -0
  134. package/src/__tests__/__snapshots__/Avatar.test.tsx.snap +17 -0
  135. package/src/__tests__/__snapshots__/Bubble.test.tsx.snap +145 -0
  136. package/src/__tests__/__snapshots__/Color.test.tsx.snap +21 -0
  137. package/src/__tests__/__snapshots__/Composer.test.tsx.snap +35 -0
  138. package/src/__tests__/__snapshots__/Constant.test.tsx.snap +16 -0
  139. package/src/__tests__/__snapshots__/Day.test.tsx.snap +37 -0
  140. package/src/__tests__/__snapshots__/GiftedAvatar.test.tsx.snap +22 -0
  141. package/src/__tests__/__snapshots__/GiftedChat.test.tsx.snap +15 -0
  142. package/src/__tests__/__snapshots__/InputToolbar.test.tsx.snap +60 -0
  143. package/src/__tests__/__snapshots__/LoadEarlier.test.tsx.snap +74 -0
  144. package/src/__tests__/__snapshots__/Message.test.tsx.snap +628 -0
  145. package/src/__tests__/__snapshots__/MessageContainer.test.tsx.snap +127 -0
  146. package/src/__tests__/__snapshots__/MessageImage.test.tsx.snap +38 -0
  147. package/src/__tests__/__snapshots__/MessageText.test.tsx.snap +30 -0
  148. package/src/__tests__/__snapshots__/Send.test.tsx.snap +129 -0
  149. package/src/__tests__/__snapshots__/SystemMessage.test.tsx.snap +38 -0
  150. package/src/__tests__/__snapshots__/Time.test.tsx.snap +33 -0
  151. package/src/__tests__/data.ts +8 -0
  152. package/src/__tests__/utils.test.ts +31 -0
  153. package/src/hooks/useUpdateLayoutEffect.ts +21 -0
  154. package/src/index.ts +4 -0
  155. package/src/logging.ts +8 -0
  156. package/src/utils.ts +39 -0
  157. package/.eslintignore +0 -2
  158. package/.eslintrc.js +0 -21
  159. package/jest.config.js +0 -15
package/src/Models.ts ADDED
@@ -0,0 +1,84 @@
1
+ import { StyleProp, ViewStyle } from 'react-native'
2
+ import { LightboxProps } from 'react-native-lightbox-v2'
3
+
4
+ export { ActionsProps } from './Actions'
5
+ export { AvatarProps } from './Avatar'
6
+ export {
7
+ BubbleProps,
8
+ RenderMessageImageProps,
9
+ RenderMessageVideoProps,
10
+ RenderMessageAudioProps,
11
+ RenderMessageTextProps
12
+ } from './Bubble'
13
+ export { ComposerProps } from './Composer'
14
+ export { DayProps } from './Day'
15
+ export { GiftedAvatarProps } from './GiftedAvatar'
16
+ export { InputToolbarProps } from './InputToolbar'
17
+ export { LoadEarlierProps } from './LoadEarlier'
18
+ export { MessageProps } from './Message'
19
+ export { MessageContainerProps } from './MessageContainer'
20
+ export { MessageImageProps } from './MessageImage'
21
+ export { MessageTextProps } from './MessageText'
22
+ export { QuickRepliesProps } from './QuickReplies'
23
+ export { SendProps } from './Send'
24
+ export { SystemMessageProps } from './SystemMessage'
25
+ export { TimeProps } from './Time'
26
+
27
+ export type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
28
+
29
+ export interface LeftRightStyle<T> {
30
+ left?: StyleProp<T>
31
+ right?: StyleProp<T>
32
+ }
33
+
34
+ type renderFunction = (x: unknown) => JSX.Element
35
+
36
+ export interface User {
37
+ _id: string | number
38
+ name?: string
39
+ avatar?: string | number | renderFunction
40
+ }
41
+
42
+ export interface Reply {
43
+ title: string
44
+ value: string
45
+ messageId?: number | string
46
+ }
47
+
48
+ export interface QuickReplies {
49
+ type: 'radio' | 'checkbox'
50
+ values: Reply[]
51
+ keepIt?: boolean
52
+ }
53
+
54
+ export interface IMessage {
55
+ _id: string | number
56
+ text: string
57
+ createdAt: Date | number
58
+ user: User
59
+ image?: string
60
+ video?: string
61
+ audio?: string
62
+ system?: boolean
63
+ sent?: boolean
64
+ received?: boolean
65
+ pending?: boolean
66
+ quickReplies?: QuickReplies
67
+ }
68
+
69
+ export type IChatMessage = IMessage
70
+
71
+ export interface MessageVideoProps<TMessage extends IMessage> {
72
+ currentMessage: TMessage
73
+ containerStyle?: StyleProp<ViewStyle>
74
+ videoStyle?: StyleProp<ViewStyle>
75
+ videoProps?: object
76
+ lightboxProps?: LightboxProps
77
+ }
78
+
79
+ export interface MessageAudioProps<TMessage extends IMessage> {
80
+ currentMessage: TMessage
81
+ containerStyle?: StyleProp<ViewStyle>
82
+ audioStyle?: StyleProp<ViewStyle>
83
+ audioProps?: object
84
+ }
@@ -0,0 +1,186 @@
1
+ import PropTypes from 'prop-types'
2
+ import React, { useState, useMemo } from 'react'
3
+ import {
4
+ Text,
5
+ StyleSheet,
6
+ View,
7
+ TouchableOpacity,
8
+ StyleProp,
9
+ ViewStyle,
10
+ TextStyle,
11
+ } from 'react-native'
12
+ import { useCallbackOne } from 'use-memo-one'
13
+ import { IMessage, Reply } from './Models'
14
+ import Color from './Color'
15
+ import { StylePropType } from './utils'
16
+ import { warning } from './logging'
17
+
18
+ const styles = StyleSheet.create({
19
+ container: {
20
+ flexDirection: 'row',
21
+ flexWrap: 'wrap',
22
+ maxWidth: 300,
23
+ },
24
+ quickReply: {
25
+ justifyContent: 'center',
26
+ alignItems: 'center',
27
+ borderWidth: 1,
28
+ maxWidth: 200,
29
+ paddingVertical: 7,
30
+ paddingHorizontal: 12,
31
+ minHeight: 50,
32
+ borderRadius: 13,
33
+ margin: 3,
34
+ },
35
+ quickReplyText: {
36
+ overflow: 'visible',
37
+ },
38
+ sendLink: {
39
+ borderWidth: 0,
40
+ },
41
+ sendLinkText: {
42
+ color: Color.defaultBlue,
43
+ fontWeight: '600',
44
+ fontSize: 17,
45
+ },
46
+ })
47
+
48
+ export interface QuickRepliesProps<TMessage extends IMessage = IMessage> {
49
+ nextMessage?: TMessage
50
+ currentMessage: TMessage
51
+ color?: string
52
+ sendText?: string
53
+ quickReplyStyle?: StyleProp<ViewStyle>
54
+ quickReplyTextStyle?: StyleProp<TextStyle>
55
+ quickReplyContainerStyle?: StyleProp<ViewStyle>
56
+ onQuickReply?(reply: Reply[]): void
57
+ renderQuickReplySend?(): React.ReactNode
58
+ }
59
+
60
+ const sameReply = (currentReply: Reply) => (reply: Reply) =>
61
+ currentReply.value === reply.value
62
+
63
+ const diffReply = (currentReply: Reply) => (reply: Reply) =>
64
+ currentReply.value !== reply.value
65
+
66
+ export function QuickReplies ({
67
+ currentMessage,
68
+ nextMessage,
69
+ color = Color.peterRiver,
70
+ quickReplyStyle,
71
+ quickReplyTextStyle,
72
+ quickReplyContainerStyle,
73
+ onQuickReply,
74
+ sendText = 'Send',
75
+ renderQuickReplySend,
76
+ }: QuickRepliesProps<IMessage>) {
77
+ const { type } = currentMessage!.quickReplies!
78
+ const [replies, setReplies] = useState<Reply[]>([])
79
+
80
+ const shouldComponentDisplay = useMemo(() => {
81
+ const hasReplies = !!currentMessage && !!currentMessage!.quickReplies
82
+ const hasNext = !!nextMessage && !!nextMessage!._id
83
+ const keepIt = currentMessage!.quickReplies!.keepIt
84
+
85
+ if (hasReplies && !hasNext)
86
+ return true
87
+
88
+ if (hasReplies && hasNext && keepIt)
89
+ return true
90
+
91
+ return false
92
+ }, [currentMessage, nextMessage])
93
+
94
+ const handlePress = useCallbackOne(
95
+ (reply: Reply) => () => {
96
+ if (currentMessage) {
97
+ const { type } = currentMessage.quickReplies!
98
+ switch (type) {
99
+ case 'radio': {
100
+ handleSend([reply])()
101
+ return
102
+ }
103
+ case 'checkbox': {
104
+ if (replies.find(sameReply(reply)))
105
+ setReplies(replies.filter(diffReply(reply)))
106
+ else
107
+ setReplies([...replies, reply])
108
+
109
+ return
110
+ }
111
+ default: {
112
+ warning(`onQuickReply unknown type: ${type}`)
113
+ }
114
+ }
115
+ }
116
+ },
117
+ [replies, currentMessage]
118
+ )
119
+
120
+ const handleSend = (repliesData: Reply[]) => () => {
121
+ onQuickReply?.(
122
+ repliesData.map((reply: Reply) => ({
123
+ ...reply,
124
+ messageId: currentMessage!._id,
125
+ }))
126
+ )
127
+ }
128
+
129
+ if (!shouldComponentDisplay)
130
+ return null
131
+
132
+ return (
133
+ <View style={[styles.container, quickReplyContainerStyle]}>
134
+ {currentMessage!.quickReplies!.values.map(
135
+ (reply: Reply, index: number) => {
136
+ const selected =
137
+ type === 'checkbox' && replies.find(sameReply(reply))
138
+
139
+ return (
140
+ <TouchableOpacity
141
+ onPress={handlePress(reply)}
142
+ style={[
143
+ styles.quickReply,
144
+ quickReplyStyle,
145
+ { borderColor: color },
146
+ selected && { backgroundColor: color },
147
+ ]}
148
+ key={`${reply.value}-${index}`}
149
+ >
150
+ <Text
151
+ numberOfLines={10}
152
+ ellipsizeMode='tail'
153
+ style={[
154
+ styles.quickReplyText,
155
+ { color: selected ? Color.white : color },
156
+ quickReplyTextStyle,
157
+ ]}
158
+ >
159
+ {reply.title}
160
+ </Text>
161
+ </TouchableOpacity>
162
+ )
163
+ }
164
+ )}
165
+ {replies.length > 0 && (
166
+ <TouchableOpacity
167
+ style={[styles.quickReply, styles.sendLink]}
168
+ onPress={handleSend(replies)}
169
+ >
170
+ {renderQuickReplySend?.() || (
171
+ <Text style={styles.sendLinkText}>{sendText}</Text>
172
+ )}
173
+ </TouchableOpacity>
174
+ )}
175
+ </View>
176
+ )
177
+ }
178
+
179
+ QuickReplies.propTypes = {
180
+ currentMessage: PropTypes.object.isRequired,
181
+ onQuickReply: PropTypes.func,
182
+ color: PropTypes.string,
183
+ sendText: PropTypes.string,
184
+ renderQuickReplySend: PropTypes.func,
185
+ quickReplyStyle: StylePropType,
186
+ }
package/src/Send.tsx ADDED
@@ -0,0 +1,102 @@
1
+ import React, { useMemo, useCallback } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import {
4
+ StyleSheet,
5
+ Text,
6
+ TouchableOpacity,
7
+ View,
8
+ StyleProp,
9
+ ViewStyle,
10
+ TextStyle,
11
+ TouchableOpacityProps,
12
+ } from 'react-native'
13
+
14
+ import Color from './Color'
15
+ import { IMessage } from './Models'
16
+ import { StylePropType } from './utils'
17
+ import { TEST_ID } from './Constant'
18
+
19
+ const styles = StyleSheet.create({
20
+ container: {
21
+ height: 44,
22
+ justifyContent: 'flex-end',
23
+ },
24
+ text: {
25
+ color: Color.defaultBlue,
26
+ fontWeight: '600',
27
+ fontSize: 17,
28
+ backgroundColor: Color.backgroundTransparent,
29
+ marginBottom: 12,
30
+ marginLeft: 10,
31
+ marginRight: 10,
32
+ },
33
+ })
34
+
35
+ export interface SendProps<TMessage extends IMessage> {
36
+ text?: string
37
+ label?: string
38
+ containerStyle?: StyleProp<ViewStyle>
39
+ textStyle?: StyleProp<TextStyle>
40
+ children?: React.ReactNode
41
+ alwaysShowSend?: boolean
42
+ disabled?: boolean
43
+ sendButtonProps?: Partial<TouchableOpacityProps>
44
+ onSend?(
45
+ messages: Partial<TMessage> | Partial<TMessage>[],
46
+ shouldResetInputToolbar: boolean,
47
+ ): void
48
+ }
49
+
50
+ export const Send = <TMessage extends IMessage = IMessage>({
51
+ text,
52
+ containerStyle,
53
+ children,
54
+ textStyle,
55
+ label = 'Send',
56
+ alwaysShowSend = false,
57
+ disabled = false,
58
+ sendButtonProps,
59
+ onSend,
60
+ }: SendProps<TMessage>) => {
61
+ const handleOnPress = useCallback(() => {
62
+ if (text && onSend)
63
+ onSend({ text: text.trim() } as Partial<TMessage>, true)
64
+ }, [text, onSend])
65
+
66
+ const showSend = useMemo(
67
+ () => alwaysShowSend || (text && text.trim().length > 0),
68
+ [alwaysShowSend, text]
69
+ )
70
+
71
+ if (!showSend)
72
+ return null
73
+
74
+ return (
75
+ <TouchableOpacity
76
+ testID={TEST_ID.SEND_TOUCHABLE}
77
+ accessible
78
+ accessibilityLabel='send'
79
+ style={[styles.container, containerStyle]}
80
+ onPress={handleOnPress}
81
+ accessibilityRole='button'
82
+ disabled={disabled}
83
+ {...sendButtonProps}
84
+ >
85
+ <View>
86
+ {children || <Text style={[styles.text, textStyle]}>{label}</Text>}
87
+ </View>
88
+ </TouchableOpacity>
89
+ )
90
+ }
91
+
92
+ Send.propTypes = {
93
+ text: PropTypes.string,
94
+ onSend: PropTypes.func,
95
+ label: PropTypes.string,
96
+ containerStyle: StylePropType,
97
+ textStyle: StylePropType,
98
+ children: PropTypes.element,
99
+ alwaysShowSend: PropTypes.bool,
100
+ disabled: PropTypes.bool,
101
+ sendButtonProps: PropTypes.object,
102
+ }
@@ -0,0 +1,61 @@
1
+ import React from 'react'
2
+ import {
3
+ StyleSheet,
4
+ Text,
5
+ View,
6
+ ViewStyle,
7
+ StyleProp,
8
+ TextStyle,
9
+ } from 'react-native'
10
+ import PropTypes from 'prop-types'
11
+ import Color from './Color'
12
+ import { IMessage } from './Models'
13
+ import { StylePropType } from './utils'
14
+
15
+ const styles = StyleSheet.create({
16
+ container: {
17
+ alignItems: 'center',
18
+ justifyContent: 'center',
19
+ flex: 1,
20
+ marginTop: 5,
21
+ marginBottom: 10,
22
+ },
23
+ text: {
24
+ backgroundColor: Color.backgroundTransparent,
25
+ color: Color.defaultColor,
26
+ fontSize: 12,
27
+ fontWeight: '300',
28
+ },
29
+ })
30
+
31
+ export interface SystemMessageProps<TMessage extends IMessage> {
32
+ currentMessage: TMessage
33
+ containerStyle?: StyleProp<ViewStyle>
34
+ wrapperStyle?: StyleProp<ViewStyle>
35
+ textStyle?: StyleProp<TextStyle>
36
+ }
37
+
38
+ export function SystemMessage<TMessage extends IMessage = IMessage> ({
39
+ currentMessage,
40
+ containerStyle,
41
+ wrapperStyle,
42
+ textStyle,
43
+ }: SystemMessageProps<TMessage>) {
44
+ if (currentMessage == null || currentMessage.system === false)
45
+ return null
46
+
47
+ return (
48
+ <View style={[styles.container, containerStyle]}>
49
+ <View style={wrapperStyle}>
50
+ <Text style={[styles.text, textStyle]}>{currentMessage.text}</Text>
51
+ </View>
52
+ </View>
53
+ )
54
+ }
55
+
56
+ SystemMessage.propTypes = {
57
+ currentMessage: PropTypes.object,
58
+ containerStyle: StylePropType,
59
+ wrapperStyle: StylePropType,
60
+ textStyle: StylePropType,
61
+ }
package/src/Time.tsx ADDED
@@ -0,0 +1,97 @@
1
+ import * as React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { StyleSheet, Text, View, ViewStyle, TextStyle } from 'react-native'
4
+ import dayjs from 'dayjs'
5
+
6
+ import Color from './Color'
7
+ import { TIME_FORMAT } from './Constant'
8
+ import { LeftRightStyle, IMessage } from './Models'
9
+ import { StylePropType } from './utils'
10
+ import { useChatContext } from './GiftedChatContext'
11
+
12
+ const { containerStyle } = StyleSheet.create({
13
+ containerStyle: {
14
+ marginLeft: 10,
15
+ marginRight: 10,
16
+ marginBottom: 5,
17
+ },
18
+ })
19
+ const { textStyle } = StyleSheet.create({
20
+ textStyle: {
21
+ fontSize: 10,
22
+ textAlign: 'right',
23
+ },
24
+ })
25
+
26
+ const styles = {
27
+ left: StyleSheet.create({
28
+ container: {
29
+ ...containerStyle,
30
+ },
31
+ text: {
32
+ color: Color.timeTextColor,
33
+ ...textStyle,
34
+ },
35
+ }),
36
+ right: StyleSheet.create({
37
+ container: {
38
+ ...containerStyle,
39
+ },
40
+ text: {
41
+ color: Color.white,
42
+ ...textStyle,
43
+ },
44
+ }),
45
+ }
46
+
47
+ export interface TimeProps<TMessage extends IMessage> {
48
+ position?: 'left' | 'right'
49
+ currentMessage: TMessage
50
+ containerStyle?: LeftRightStyle<ViewStyle>
51
+ timeTextStyle?: LeftRightStyle<TextStyle>
52
+ timeFormat?: string
53
+ }
54
+
55
+ export function Time<TMessage extends IMessage = IMessage> ({
56
+ position = 'left',
57
+ containerStyle,
58
+ currentMessage,
59
+ timeFormat = TIME_FORMAT,
60
+ timeTextStyle,
61
+ }: TimeProps<TMessage>) {
62
+ const { getLocale } = useChatContext()
63
+ if (currentMessage == null)
64
+ return null
65
+
66
+ return (
67
+ <View
68
+ style={[
69
+ styles[position].container,
70
+ containerStyle && containerStyle[position],
71
+ ]}
72
+ >
73
+ <Text
74
+ style={[
75
+ styles[position].text,
76
+ timeTextStyle && timeTextStyle[position],
77
+ ]}
78
+ >
79
+ {dayjs(currentMessage.createdAt).locale(getLocale()).format(timeFormat)}
80
+ </Text>
81
+ </View>
82
+ )
83
+ }
84
+
85
+ Time.propTypes = {
86
+ position: PropTypes.oneOf(['left', 'right']),
87
+ currentMessage: PropTypes.object,
88
+ containerStyle: PropTypes.shape({
89
+ left: StylePropType,
90
+ right: StylePropType,
91
+ }),
92
+ timeFormat: PropTypes.string,
93
+ timeTextStyle: PropTypes.shape({
94
+ left: StylePropType,
95
+ right: StylePropType,
96
+ }),
97
+ }
@@ -0,0 +1,108 @@
1
+ import * as React from 'react'
2
+ import { Animated, StyleSheet } from 'react-native'
3
+ import { TypingAnimation } from 'react-native-typing-animation'
4
+ import { useUpdateLayoutEffect } from './hooks/useUpdateLayoutEffect'
5
+ import Color from './Color'
6
+
7
+ interface Props {
8
+ isTyping?: boolean
9
+ }
10
+
11
+ const TypingIndicator = ({ isTyping }: Props) => {
12
+ const { yCoords, heightScale, marginScale } = React.useMemo(
13
+ () => ({
14
+ yCoords: new Animated.Value(200),
15
+ heightScale: new Animated.Value(0),
16
+ marginScale: new Animated.Value(0),
17
+ }),
18
+ []
19
+ )
20
+
21
+ // on isTyping fire side effect
22
+ useUpdateLayoutEffect(() => {
23
+ if (isTyping)
24
+ slideIn()
25
+ else
26
+ slideOut()
27
+ }, [isTyping])
28
+
29
+ // side effect
30
+ const slideIn = () => {
31
+ Animated.parallel([
32
+ Animated.spring(yCoords, {
33
+ toValue: 0,
34
+ useNativeDriver: false,
35
+ }),
36
+ Animated.timing(heightScale, {
37
+ toValue: 35,
38
+ duration: 250,
39
+ useNativeDriver: false,
40
+ }),
41
+ Animated.timing(marginScale, {
42
+ toValue: 8,
43
+ duration: 250,
44
+ useNativeDriver: false,
45
+ }),
46
+ ]).start()
47
+ }
48
+
49
+ // side effect
50
+ const slideOut = () => {
51
+ Animated.parallel([
52
+ Animated.spring(yCoords, {
53
+ toValue: 200,
54
+ useNativeDriver: false,
55
+ }),
56
+ Animated.timing(heightScale, {
57
+ toValue: 0,
58
+ duration: 250,
59
+ useNativeDriver: false,
60
+ }),
61
+ Animated.timing(marginScale, {
62
+ toValue: 0,
63
+ duration: 250,
64
+ useNativeDriver: false,
65
+ }),
66
+ ]).start()
67
+ }
68
+ return (
69
+ <Animated.View
70
+ style={[
71
+ styles.container,
72
+ {
73
+ transform: [
74
+ {
75
+ translateY: yCoords,
76
+ },
77
+ ],
78
+ height: heightScale,
79
+ marginBottom: marginScale,
80
+ },
81
+ ]}
82
+ >
83
+ {
84
+ isTyping
85
+ ? (
86
+ <TypingAnimation
87
+ style={{ marginLeft: 6, marginTop: 7.2 }}
88
+ dotRadius={4}
89
+ dotMargin={5.5}
90
+ dotColor='rgba(0, 0, 0, 0.38)'
91
+ />
92
+ )
93
+ : null
94
+ }
95
+ </Animated.View>
96
+ )
97
+ }
98
+
99
+ const styles = StyleSheet.create({
100
+ container: {
101
+ marginLeft: 8,
102
+ width: 45,
103
+ borderRadius: 15,
104
+ backgroundColor: Color.leftBubbleBackground,
105
+ },
106
+ })
107
+
108
+ export default TypingIndicator
@@ -0,0 +1,10 @@
1
+ import 'react-native'
2
+ import React from 'react'
3
+ import renderer from 'react-test-renderer'
4
+
5
+ import { Actions } from '../GiftedChat'
6
+
7
+ it('should render <Actions /> and compare with snapshot', () => {
8
+ const tree = renderer.create(<Actions />).toJSON()
9
+ expect(tree).toMatchSnapshot()
10
+ })
@@ -0,0 +1,13 @@
1
+ import 'react-native'
2
+ import React from 'react'
3
+ import renderer from 'react-test-renderer'
4
+
5
+ import { Avatar } from '../GiftedChat'
6
+
7
+ it('should render <Avatar /> and compare with snapshot', () => {
8
+ const tree = renderer
9
+ .create(<Avatar renderAvatar={() => 'renderAvatar'} position='left' />)
10
+ .toJSON()
11
+
12
+ expect(tree).toMatchSnapshot()
13
+ })
@@ -0,0 +1,23 @@
1
+ import 'react-native'
2
+ import React from 'react'
3
+ import renderer from 'react-test-renderer'
4
+
5
+ import { Bubble } from '../GiftedChat'
6
+
7
+ it('should render <Bubble /> and compare with snapshot', () => {
8
+ const tree = renderer
9
+ .create(
10
+ <Bubble
11
+ user={{ _id: 1 }}
12
+ currentMessage={{
13
+ _id: 1,
14
+ text: 'test',
15
+ createdAt: 1554744013721,
16
+ user: { _id: 1 },
17
+ }}
18
+ />
19
+ )
20
+ .toJSON()
21
+
22
+ expect(tree).toMatchSnapshot()
23
+ })
@@ -0,0 +1,5 @@
1
+ import Color from '../Color'
2
+
3
+ it('should compare Color with snapshot', () => {
4
+ expect(Color).toMatchSnapshot()
5
+ })
@@ -0,0 +1,11 @@
1
+ import 'react-native'
2
+ import React from 'react'
3
+ import renderer from 'react-test-renderer'
4
+
5
+ import { Composer } from '../GiftedChat'
6
+
7
+ it('should render <Composer /> and compare with snapshot', () => {
8
+ const tree = renderer.create(<Composer />).toJSON()
9
+
10
+ expect(tree).toMatchSnapshot()
11
+ })