react-native-gifted-chat 2.9.0-alpha.0 → 3.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +183 -262
- package/package.json +1 -1
- package/src/Avatar.tsx +12 -12
- package/src/Bubble/index.tsx +2 -2
- package/src/Bubble/types.ts +22 -22
- package/src/Composer.tsx +0 -3
- package/src/GiftedAvatar.tsx +1 -1
- package/src/GiftedChat/index.tsx +20 -23
- package/src/GiftedChat/types.ts +38 -47
- package/src/InputToolbar.tsx +6 -6
- package/src/Message/index.tsx +5 -6
- package/src/Message/types.ts +8 -12
- package/src/MessageContainer/components/DayAnimated/types.ts +1 -1
- package/src/MessageContainer/components/Item/index.tsx +2 -2
- package/src/MessageContainer/components/Item/types.ts +1 -1
- package/src/MessageContainer/index.tsx +28 -30
- package/src/MessageContainer/types.ts +12 -16
- package/src/MessageImage.tsx +1 -1
- package/src/MessageText.tsx +1 -1
- package/src/Models.ts +63 -0
- package/src/QuickReplies.tsx +1 -1
- package/src/Send.tsx +30 -31
- package/src/SystemMessage.tsx +1 -1
- package/src/Time.tsx +1 -1
- package/src/__tests__/Message.test.tsx +2 -2
- package/src/__tests__/Send.test.tsx +1 -1
- package/src/__tests__/__snapshots__/Actions.test.tsx.snap +2 -86
- package/src/__tests__/__snapshots__/LoadEarlier.test.tsx.snap +3 -89
- package/src/__tests__/__snapshots__/Message.test.tsx.snap +2 -2
- package/src/__tests__/__snapshots__/Send.test.tsx.snap +10 -142
- package/src/__tests__/data.ts +1 -1
- package/src/components/TouchableOpacity.tsx +19 -8
- package/src/types.ts +1 -63
- package/src/utils.ts +1 -1
|
@@ -11,10 +11,9 @@ import { FlatList } from 'react-native-gesture-handler'
|
|
|
11
11
|
import Animated, { runOnJS, useAnimatedScrollHandler, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'
|
|
12
12
|
import { LoadEarlierMessages } from '../LoadEarlierMessages'
|
|
13
13
|
import { warning } from '../logging'
|
|
14
|
+
import { IMessage } from '../Models'
|
|
14
15
|
import { ReanimatedScrollEvent } from '../reanimatedCompat'
|
|
15
|
-
|
|
16
16
|
import stylesCommon from '../styles'
|
|
17
|
-
import { IMessage } from '../types'
|
|
18
17
|
import { TypingIndicator } from '../TypingIndicator'
|
|
19
18
|
import { isSameDay, useCallbackThrottled } from '../utils'
|
|
20
19
|
import { DayAnimated } from './components/DayAnimated'
|
|
@@ -35,23 +34,23 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
35
34
|
user,
|
|
36
35
|
isTyping = false,
|
|
37
36
|
renderChatEmpty: renderChatEmptyProp,
|
|
38
|
-
|
|
37
|
+
isInverted = true,
|
|
39
38
|
listProps,
|
|
40
|
-
extraData,
|
|
41
39
|
isScrollToBottomEnabled = false,
|
|
42
40
|
scrollToBottomOffset = 200,
|
|
43
|
-
|
|
41
|
+
isAlignedTop = false,
|
|
44
42
|
scrollToBottomStyle,
|
|
45
43
|
loadEarlierMessagesProps,
|
|
46
44
|
renderTypingIndicator: renderTypingIndicatorProp,
|
|
47
45
|
renderFooter: renderFooterProp,
|
|
48
46
|
renderLoadEarlier: renderLoadEarlierProp,
|
|
49
47
|
forwardRef,
|
|
50
|
-
handleOnScroll: handleOnScrollProp,
|
|
51
48
|
scrollToBottomComponent: scrollToBottomComponentProp,
|
|
52
49
|
renderDay: renderDayProp,
|
|
53
50
|
} = props
|
|
54
51
|
|
|
52
|
+
const listPropsOnScrollProp = listProps?.onScroll
|
|
53
|
+
|
|
55
54
|
const scrollToBottomOpacity = useSharedValue(0)
|
|
56
55
|
const isScrollingDown = useSharedValue(false)
|
|
57
56
|
const lastScrolledY = useSharedValue(0)
|
|
@@ -111,14 +110,14 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
111
110
|
isScrollingDown.value = true
|
|
112
111
|
changeScrollToBottomVisibility(false)
|
|
113
112
|
|
|
114
|
-
if (
|
|
113
|
+
if (isInverted)
|
|
115
114
|
scrollTo({ offset: 0, animated })
|
|
116
115
|
else if (forwardRef?.current)
|
|
117
116
|
forwardRef.current.scrollToEnd({ animated })
|
|
118
|
-
}, [forwardRef,
|
|
117
|
+
}, [forwardRef, isInverted, scrollTo, isScrollingDown, changeScrollToBottomVisibility])
|
|
119
118
|
|
|
120
119
|
const handleOnScroll = useCallback((event: ReanimatedScrollEvent) => {
|
|
121
|
-
|
|
120
|
+
listPropsOnScrollProp?.(event as any)
|
|
122
121
|
|
|
123
122
|
const {
|
|
124
123
|
contentOffset: { y: contentOffsetY },
|
|
@@ -127,12 +126,12 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
127
126
|
} = event
|
|
128
127
|
|
|
129
128
|
isScrollingDown.value =
|
|
130
|
-
(
|
|
131
|
-
(!
|
|
129
|
+
(isInverted && lastScrolledY.value > contentOffsetY) ||
|
|
130
|
+
(!isInverted && lastScrolledY.value < contentOffsetY)
|
|
132
131
|
|
|
133
132
|
lastScrolledY.value = contentOffsetY
|
|
134
133
|
|
|
135
|
-
if (
|
|
134
|
+
if (isInverted)
|
|
136
135
|
if (contentOffsetY > scrollToBottomOffset!)
|
|
137
136
|
changeScrollToBottomVisibility(true)
|
|
138
137
|
else
|
|
@@ -144,7 +143,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
144
143
|
changeScrollToBottomVisibility(false)
|
|
145
144
|
else
|
|
146
145
|
changeScrollToBottomVisibility(false)
|
|
147
|
-
}, [
|
|
146
|
+
}, [isInverted, scrollToBottomOffset, changeScrollToBottomVisibility, isScrollingDown, lastScrolledY, listPropsOnScrollProp])
|
|
148
147
|
|
|
149
148
|
const restProps = useMemo(() => {
|
|
150
149
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -170,9 +169,9 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
170
169
|
|
|
171
170
|
if (messages && user) {
|
|
172
171
|
const previousMessage =
|
|
173
|
-
(
|
|
172
|
+
(isInverted ? messages[index + 1] : messages[index - 1]) || {}
|
|
174
173
|
const nextMessage =
|
|
175
|
-
(
|
|
174
|
+
(isInverted ? messages[index - 1] : messages[index + 1]) || {}
|
|
176
175
|
|
|
177
176
|
const messageProps: ItemProps<TMessage> = {
|
|
178
177
|
...restProps,
|
|
@@ -191,7 +190,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
191
190
|
}
|
|
192
191
|
|
|
193
192
|
return null
|
|
194
|
-
}, [messages, restProps,
|
|
193
|
+
}, [messages, restProps, isInverted, scrolledY, daysPositions, listHeight, user])
|
|
195
194
|
|
|
196
195
|
const emptyContent = useMemo(() => {
|
|
197
196
|
if (!renderChatEmptyProp)
|
|
@@ -202,7 +201,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
202
201
|
|
|
203
202
|
const renderChatEmpty = useCallback(() => {
|
|
204
203
|
if (renderChatEmptyProp)
|
|
205
|
-
return
|
|
204
|
+
return isInverted
|
|
206
205
|
? (
|
|
207
206
|
emptyContent
|
|
208
207
|
)
|
|
@@ -213,7 +212,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
213
212
|
)
|
|
214
213
|
|
|
215
214
|
return <View style={stylesCommon.fill} />
|
|
216
|
-
}, [
|
|
215
|
+
}, [isInverted, renderChatEmptyProp, emptyContent])
|
|
217
216
|
|
|
218
217
|
const ListHeaderComponent = useMemo(() => {
|
|
219
218
|
const content = renderLoadEarlier()
|
|
@@ -273,7 +272,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
273
272
|
listHeight.value = event.nativeEvent.layout.height
|
|
274
273
|
|
|
275
274
|
if (
|
|
276
|
-
!
|
|
275
|
+
!isInverted &&
|
|
277
276
|
messages?.length &&
|
|
278
277
|
isScrollToBottomEnabled
|
|
279
278
|
)
|
|
@@ -282,7 +281,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
282
281
|
}, 500)
|
|
283
282
|
|
|
284
283
|
listProps?.onLayout?.(event)
|
|
285
|
-
}, [
|
|
284
|
+
}, [isInverted, messages, doScrollToBottom, listHeight, listProps, isScrollToBottomEnabled])
|
|
286
285
|
|
|
287
286
|
const onEndReached = useCallback(() => {
|
|
288
287
|
if (
|
|
@@ -326,7 +325,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
326
325
|
}
|
|
327
326
|
|
|
328
327
|
for (const [key, item] of Object.entries(value))
|
|
329
|
-
if (isSameDay(newValue.createdAt, item.createdAt) && (
|
|
328
|
+
if (isSameDay(newValue.createdAt, item.createdAt) && (isInverted ? item.y <= newValue.y : item.y >= newValue.y)) {
|
|
330
329
|
delete value[key]
|
|
331
330
|
break
|
|
332
331
|
}
|
|
@@ -345,7 +344,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
345
344
|
{children}
|
|
346
345
|
</View>
|
|
347
346
|
)
|
|
348
|
-
}, [daysPositions,
|
|
347
|
+
}, [daysPositions, isInverted])
|
|
349
348
|
|
|
350
349
|
const scrollHandler = useAnimatedScrollHandler({
|
|
351
350
|
onScroll: event => {
|
|
@@ -362,7 +361,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
362
361
|
let shouldRemove = messageIndex === -1
|
|
363
362
|
|
|
364
363
|
if (!shouldRemove) {
|
|
365
|
-
const prevMessage = messages[messageIndex + (
|
|
364
|
+
const prevMessage = messages[messageIndex + (isInverted ? 1 : -1)]
|
|
366
365
|
const message = messages[messageIndex]
|
|
367
366
|
shouldRemove = !!prevMessage && isSameDay(message, prevMessage)
|
|
368
367
|
}
|
|
@@ -375,36 +374,35 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
375
374
|
return value
|
|
376
375
|
})
|
|
377
376
|
})
|
|
378
|
-
}, [messages, daysPositions,
|
|
377
|
+
}, [messages, daysPositions, isInverted])
|
|
379
378
|
|
|
380
379
|
return (
|
|
381
380
|
<View
|
|
382
381
|
style={[
|
|
383
382
|
styles.contentContainerStyle,
|
|
384
|
-
|
|
383
|
+
isAlignedTop ? styles.containerAlignTop : stylesCommon.fill,
|
|
385
384
|
]}
|
|
386
385
|
>
|
|
387
386
|
<AnimatedFlatList
|
|
388
|
-
extraData={extraData}
|
|
389
387
|
ref={forwardRef}
|
|
390
388
|
keyExtractor={keyExtractor}
|
|
391
389
|
data={messages}
|
|
392
390
|
renderItem={renderItem}
|
|
393
|
-
inverted={
|
|
391
|
+
inverted={isInverted}
|
|
394
392
|
automaticallyAdjustContentInsets={false}
|
|
395
393
|
style={stylesCommon.fill}
|
|
396
394
|
ListEmptyComponent={renderChatEmpty}
|
|
397
395
|
ListFooterComponent={
|
|
398
|
-
|
|
396
|
+
isInverted ? ListHeaderComponent : ListFooterComponent
|
|
399
397
|
}
|
|
400
398
|
ListHeaderComponent={
|
|
401
|
-
|
|
399
|
+
isInverted ? ListFooterComponent : ListHeaderComponent
|
|
402
400
|
}
|
|
403
|
-
onScroll={scrollHandler}
|
|
404
401
|
scrollEventThrottle={1}
|
|
405
402
|
onEndReached={onEndReached}
|
|
406
403
|
onEndReachedThreshold={0.1}
|
|
407
404
|
{...listProps}
|
|
405
|
+
onScroll={scrollHandler}
|
|
408
406
|
onLayout={onLayoutList}
|
|
409
407
|
CellRendererComponent={renderCell}
|
|
410
408
|
/>
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
} from 'react-native'
|
|
7
7
|
import { FlatList } from 'react-native-gesture-handler'
|
|
8
8
|
|
|
9
|
+
import { DayProps } from '../Day'
|
|
9
10
|
import { LoadEarlierMessagesProps } from '../LoadEarlierMessages'
|
|
10
11
|
import { MessageProps } from '../Message'
|
|
11
|
-
import {
|
|
12
|
-
import { User, IMessage, Reply, DayProps } from '../types'
|
|
12
|
+
import { User, IMessage, Reply } from '../Models'
|
|
13
13
|
import { TypingIndicatorProps } from '../TypingIndicator/types'
|
|
14
14
|
|
|
15
15
|
export type ListProps<TMessage extends IMessage = IMessage> = Partial<FlatListProps<TMessage>>
|
|
@@ -27,37 +27,33 @@ export interface MessageContainerProps<TMessage extends IMessage = IMessage>
|
|
|
27
27
|
/** Additional props for FlatList */
|
|
28
28
|
listProps?: ListProps<TMessage>
|
|
29
29
|
/** Reverses display order of messages; default is true */
|
|
30
|
-
|
|
30
|
+
isInverted?: boolean
|
|
31
31
|
/** Controls whether or not the message bubbles appear at the top of the chat */
|
|
32
|
-
|
|
32
|
+
isAlignedTop?: boolean
|
|
33
33
|
/** Enables the isScrollToBottomEnabled Component */
|
|
34
34
|
isScrollToBottomEnabled?: boolean
|
|
35
35
|
/** Scroll to bottom wrapper style */
|
|
36
36
|
scrollToBottomStyle?: StyleProp<ViewStyle>
|
|
37
|
-
/** This can be used to pass unknown data which needs to be re-rendered */
|
|
38
|
-
extraData?: object
|
|
39
37
|
/** Distance from bottom before showing scroll to bottom button */
|
|
40
38
|
scrollToBottomOffset?: number
|
|
41
39
|
/** Custom component to render when messages are empty */
|
|
42
|
-
renderChatEmpty
|
|
40
|
+
renderChatEmpty?: () => React.ReactNode
|
|
43
41
|
/** Custom footer component on the ListView, e.g. 'User is typing...' */
|
|
44
|
-
renderFooter
|
|
42
|
+
renderFooter?: (props: MessageContainerProps<TMessage>) => React.ReactNode
|
|
45
43
|
/** Custom message container */
|
|
46
|
-
renderMessage
|
|
44
|
+
renderMessage?: (props: MessageProps<TMessage>) => React.ReactElement
|
|
47
45
|
/** Custom day above a message */
|
|
48
|
-
renderDay
|
|
46
|
+
renderDay?: (props: DayProps) => React.ReactNode
|
|
49
47
|
/** Custom "Load earlier messages" button */
|
|
50
|
-
renderLoadEarlier
|
|
48
|
+
renderLoadEarlier?: (props: LoadEarlierMessagesProps) => React.ReactNode
|
|
51
49
|
/** Custom typing indicator */
|
|
52
|
-
renderTypingIndicator
|
|
50
|
+
renderTypingIndicator?: () => React.ReactNode
|
|
53
51
|
/** Scroll to bottom custom component */
|
|
54
|
-
scrollToBottomComponent
|
|
52
|
+
scrollToBottomComponent?: () => React.ReactNode
|
|
55
53
|
/** Callback when quick reply is sent */
|
|
56
|
-
onQuickReply
|
|
54
|
+
onQuickReply?: (replies: Reply[]) => void
|
|
57
55
|
/** Props to pass to the LoadEarlierMessages component. The LoadEarlierMessages button is only visible when isAvailable is true. Includes isAvailable (controls button visibility), isInfiniteScrollEnabled (infinite scroll up when reach the top of messages container, automatically call onPress function if it exists - not yet supported for web), onPress (callback when button is pressed), isLoading (display loading indicator), label (override default "Load earlier messages" text), and styling props (containerStyle, wrapperStyle, textStyle, activityIndicatorStyle, activityIndicatorColor, activityIndicatorSize). */
|
|
58
56
|
loadEarlierMessagesProps?: LoadEarlierMessagesProps
|
|
59
|
-
/** Custom scroll event handler */
|
|
60
|
-
handleOnScroll?(event: ReanimatedScrollEvent): void
|
|
61
57
|
/** Style for TypingIndicator component */
|
|
62
58
|
typingIndicatorStyle?: StyleProp<ViewStyle>
|
|
63
59
|
}
|
package/src/MessageImage.tsx
CHANGED
|
@@ -16,8 +16,8 @@ import {
|
|
|
16
16
|
import { BaseButton, GestureHandlerRootView, Text } from 'react-native-gesture-handler'
|
|
17
17
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
18
18
|
import Zoom from 'react-native-zoom-reanimated'
|
|
19
|
+
import { IMessage } from './Models'
|
|
19
20
|
import commonStyles from './styles'
|
|
20
|
-
import { IMessage } from './types'
|
|
21
21
|
|
|
22
22
|
const styles = StyleSheet.create({
|
|
23
23
|
image: {
|
package/src/MessageText.tsx
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
|
|
10
10
|
import { Match } from 'autolinker/dist/es2015'
|
|
11
11
|
import Autolink, { AutolinkProps } from 'react-native-autolink'
|
|
12
|
-
import { LeftRightStyle, IMessage } from './
|
|
12
|
+
import { LeftRightStyle, IMessage } from './Models'
|
|
13
13
|
|
|
14
14
|
export type MessageTextProps<TMessage extends IMessage> = {
|
|
15
15
|
position?: 'left' | 'right'
|
package/src/Models.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { StyleProp, ViewStyle } from 'react-native'
|
|
2
|
+
|
|
3
|
+
export type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
|
|
4
|
+
|
|
5
|
+
export interface LeftRightStyle<T> {
|
|
6
|
+
left?: StyleProp<T>
|
|
7
|
+
right?: StyleProp<T>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type renderFunction = (x: unknown) => React.ReactNode
|
|
11
|
+
|
|
12
|
+
export interface User {
|
|
13
|
+
_id: string | number
|
|
14
|
+
name?: string
|
|
15
|
+
avatar?: string | number | renderFunction
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface Reply {
|
|
19
|
+
title: string
|
|
20
|
+
value: string
|
|
21
|
+
messageId?: number | string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface QuickReplies {
|
|
25
|
+
type: 'radio' | 'checkbox'
|
|
26
|
+
values: Reply[]
|
|
27
|
+
keepIt?: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface IMessage {
|
|
31
|
+
_id: string | number
|
|
32
|
+
text: string
|
|
33
|
+
createdAt: Date | number
|
|
34
|
+
user: User
|
|
35
|
+
image?: string
|
|
36
|
+
video?: string
|
|
37
|
+
audio?: string
|
|
38
|
+
system?: boolean
|
|
39
|
+
sent?: boolean
|
|
40
|
+
received?: boolean
|
|
41
|
+
pending?: boolean
|
|
42
|
+
quickReplies?: QuickReplies
|
|
43
|
+
location?: {
|
|
44
|
+
latitude: number
|
|
45
|
+
longitude: number
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type IChatMessage = IMessage
|
|
50
|
+
|
|
51
|
+
export interface MessageVideoProps<TMessage extends IMessage> {
|
|
52
|
+
currentMessage: TMessage
|
|
53
|
+
containerStyle?: StyleProp<ViewStyle>
|
|
54
|
+
videoStyle?: StyleProp<ViewStyle>
|
|
55
|
+
videoProps?: object
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface MessageAudioProps<TMessage extends IMessage> {
|
|
59
|
+
currentMessage: TMessage
|
|
60
|
+
containerStyle?: StyleProp<ViewStyle>
|
|
61
|
+
audioStyle?: StyleProp<ViewStyle>
|
|
62
|
+
audioProps?: object
|
|
63
|
+
}
|
package/src/QuickReplies.tsx
CHANGED
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
import { Color } from './Color'
|
|
11
11
|
import { TouchableOpacity } from './components/TouchableOpacity'
|
|
12
12
|
import { warning } from './logging'
|
|
13
|
+
import { IMessage, Reply } from './Models'
|
|
13
14
|
import stylesCommon from './styles'
|
|
14
|
-
import { IMessage, Reply } from './types'
|
|
15
15
|
|
|
16
16
|
const styles = StyleSheet.create({
|
|
17
17
|
container: {
|
package/src/Send.tsx
CHANGED
|
@@ -12,26 +12,7 @@ import { Color } from './Color'
|
|
|
12
12
|
|
|
13
13
|
import { TouchableOpacity, TouchableOpacityProps } from './components/TouchableOpacity'
|
|
14
14
|
import { TEST_ID } from './Constant'
|
|
15
|
-
import { IMessage } from './
|
|
16
|
-
|
|
17
|
-
const styles = StyleSheet.create({
|
|
18
|
-
container: {
|
|
19
|
-
height: 44,
|
|
20
|
-
justifyContent: 'flex-end',
|
|
21
|
-
},
|
|
22
|
-
text: {
|
|
23
|
-
color: Color.defaultBlue,
|
|
24
|
-
fontWeight: '600',
|
|
25
|
-
fontSize: 17,
|
|
26
|
-
backgroundColor: Color.backgroundTransparent,
|
|
27
|
-
marginBottom: 12,
|
|
28
|
-
marginLeft: 10,
|
|
29
|
-
marginRight: 10,
|
|
30
|
-
},
|
|
31
|
-
text_dark: {
|
|
32
|
-
color: '#4da6ff',
|
|
33
|
-
},
|
|
34
|
-
})
|
|
15
|
+
import { IMessage } from './Models'
|
|
35
16
|
|
|
36
17
|
export interface SendProps<TMessage extends IMessage> {
|
|
37
18
|
text?: string
|
|
@@ -39,8 +20,7 @@ export interface SendProps<TMessage extends IMessage> {
|
|
|
39
20
|
containerStyle?: StyleProp<ViewStyle>
|
|
40
21
|
textStyle?: StyleProp<TextStyle>
|
|
41
22
|
children?: React.ReactNode
|
|
42
|
-
|
|
43
|
-
disabled?: boolean
|
|
23
|
+
isSendButtonAlwaysVisible?: boolean
|
|
44
24
|
sendButtonProps?: Partial<TouchableOpacityProps>
|
|
45
25
|
onSend?(
|
|
46
26
|
messages: Partial<TMessage> | Partial<TMessage>[],
|
|
@@ -54,21 +34,22 @@ export const Send = <TMessage extends IMessage = IMessage>({
|
|
|
54
34
|
children,
|
|
55
35
|
textStyle,
|
|
56
36
|
label = 'Send',
|
|
57
|
-
|
|
58
|
-
disabled = false,
|
|
37
|
+
isSendButtonAlwaysVisible = false,
|
|
59
38
|
sendButtonProps,
|
|
60
39
|
onSend,
|
|
61
40
|
}: SendProps<TMessage>) => {
|
|
62
41
|
const colorScheme = useColorScheme()
|
|
63
42
|
|
|
64
43
|
const handleOnPress = useCallback(() => {
|
|
65
|
-
|
|
66
|
-
|
|
44
|
+
const message = { text: text?.trim() } as Partial<TMessage>
|
|
45
|
+
|
|
46
|
+
if (onSend && message.text?.length)
|
|
47
|
+
onSend(message, true)
|
|
67
48
|
}, [text, onSend])
|
|
68
49
|
|
|
69
50
|
const showSend = useMemo(
|
|
70
|
-
() =>
|
|
71
|
-
[
|
|
51
|
+
() => isSendButtonAlwaysVisible || !!text?.trim().length,
|
|
52
|
+
[isSendButtonAlwaysVisible, text]
|
|
72
53
|
)
|
|
73
54
|
|
|
74
55
|
if (!showSend)
|
|
@@ -77,12 +58,11 @@ export const Send = <TMessage extends IMessage = IMessage>({
|
|
|
77
58
|
return (
|
|
78
59
|
<TouchableOpacity
|
|
79
60
|
testID={TEST_ID.SEND_TOUCHABLE}
|
|
80
|
-
accessible
|
|
81
|
-
accessibilityLabel='send'
|
|
82
61
|
style={[styles.container, containerStyle]}
|
|
83
62
|
onPress={handleOnPress}
|
|
63
|
+
accessible
|
|
64
|
+
accessibilityLabel='send'
|
|
84
65
|
accessibilityRole='button'
|
|
85
|
-
disabled={disabled}
|
|
86
66
|
{...sendButtonProps}
|
|
87
67
|
>
|
|
88
68
|
<View>
|
|
@@ -91,3 +71,22 @@ export const Send = <TMessage extends IMessage = IMessage>({
|
|
|
91
71
|
</TouchableOpacity>
|
|
92
72
|
)
|
|
93
73
|
}
|
|
74
|
+
|
|
75
|
+
const styles = StyleSheet.create({
|
|
76
|
+
container: {
|
|
77
|
+
height: 44,
|
|
78
|
+
justifyContent: 'flex-end',
|
|
79
|
+
},
|
|
80
|
+
text: {
|
|
81
|
+
color: Color.defaultBlue,
|
|
82
|
+
fontWeight: '600',
|
|
83
|
+
fontSize: 17,
|
|
84
|
+
backgroundColor: Color.backgroundTransparent,
|
|
85
|
+
marginBottom: 12,
|
|
86
|
+
marginLeft: 10,
|
|
87
|
+
marginRight: 10,
|
|
88
|
+
},
|
|
89
|
+
text_dark: {
|
|
90
|
+
color: '#4da6ff',
|
|
91
|
+
},
|
|
92
|
+
})
|
package/src/SystemMessage.tsx
CHANGED
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
} from 'react-native'
|
|
9
9
|
import { Color } from './Color'
|
|
10
10
|
import { MessageText } from './MessageText'
|
|
11
|
+
import { IMessage } from './Models'
|
|
11
12
|
import stylesCommon from './styles'
|
|
12
|
-
import { IMessage } from './types'
|
|
13
13
|
|
|
14
14
|
export interface SystemMessageProps<TMessage extends IMessage> {
|
|
15
15
|
currentMessage: TMessage
|
package/src/Time.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import dayjs from 'dayjs'
|
|
|
5
5
|
import { Color } from './Color'
|
|
6
6
|
import { TIME_FORMAT } from './Constant'
|
|
7
7
|
import { useChatContext } from './GiftedChatContext'
|
|
8
|
-
import { LeftRightStyle, IMessage } from './
|
|
8
|
+
import { LeftRightStyle, IMessage } from './Models'
|
|
9
9
|
|
|
10
10
|
const { containerStyle } = StyleSheet.create({
|
|
11
11
|
containerStyle: {
|
|
@@ -38,7 +38,7 @@ describe('Message component', () => {
|
|
|
38
38
|
user={{ _id: 1 }}
|
|
39
39
|
currentMessage={DEFAULT_TEST_MESSAGE}
|
|
40
40
|
position='left'
|
|
41
|
-
|
|
41
|
+
isUserAvatarVisible
|
|
42
42
|
/>
|
|
43
43
|
)
|
|
44
44
|
|
|
@@ -58,7 +58,7 @@ describe('Message component', () => {
|
|
|
58
58
|
},
|
|
59
59
|
}}
|
|
60
60
|
position='left'
|
|
61
|
-
|
|
61
|
+
isUserAvatarVisible
|
|
62
62
|
/>
|
|
63
63
|
)
|
|
64
64
|
|
|
@@ -10,7 +10,7 @@ describe('Send', () => {
|
|
|
10
10
|
})
|
|
11
11
|
|
|
12
12
|
it('should always render <Send /> and compare with snapshot', () => {
|
|
13
|
-
const { toJSON } = render(<Send
|
|
13
|
+
const { toJSON } = render(<Send isSendButtonAlwaysVisible />)
|
|
14
14
|
expect(toJSON()).toMatchSnapshot()
|
|
15
15
|
})
|
|
16
16
|
|
|
@@ -6,7 +6,7 @@ exports[`should render <Actions /> and compare with snapshot 1`] = `
|
|
|
6
6
|
{
|
|
7
7
|
"busy": undefined,
|
|
8
8
|
"checked": undefined,
|
|
9
|
-
"disabled":
|
|
9
|
+
"disabled": false,
|
|
10
10
|
"expanded": undefined,
|
|
11
11
|
"selected": undefined,
|
|
12
12
|
}
|
|
@@ -20,97 +20,13 @@ exports[`should render <Actions /> and compare with snapshot 1`] = `
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
accessible={true}
|
|
23
|
-
collapsable={false}
|
|
24
23
|
focusable={true}
|
|
25
|
-
onBlur={[Function]}
|
|
26
24
|
onClick={[Function]}
|
|
27
|
-
onFocus={[Function]}
|
|
28
25
|
onResponderGrant={[Function]}
|
|
29
26
|
onResponderMove={[Function]}
|
|
30
27
|
onResponderRelease={[Function]}
|
|
31
28
|
onResponderTerminate={[Function]}
|
|
32
29
|
onResponderTerminationRequest={[Function]}
|
|
33
30
|
onStartShouldSetResponder={[Function]}
|
|
34
|
-
|
|
35
|
-
<View
|
|
36
|
-
collapsable={false}
|
|
37
|
-
jestAnimatedProps={
|
|
38
|
-
{
|
|
39
|
-
"value": {},
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
jestAnimatedStyle={
|
|
43
|
-
{
|
|
44
|
-
"value": {
|
|
45
|
-
"opacity": 1,
|
|
46
|
-
},
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
jestInlineStyle={
|
|
50
|
-
[
|
|
51
|
-
[
|
|
52
|
-
{
|
|
53
|
-
"height": 26,
|
|
54
|
-
"marginBottom": 10,
|
|
55
|
-
"marginLeft": 10,
|
|
56
|
-
"width": 26,
|
|
57
|
-
},
|
|
58
|
-
undefined,
|
|
59
|
-
],
|
|
60
|
-
]
|
|
61
|
-
}
|
|
62
|
-
style={
|
|
63
|
-
[
|
|
64
|
-
{
|
|
65
|
-
"height": 26,
|
|
66
|
-
"marginBottom": 10,
|
|
67
|
-
"marginLeft": 10,
|
|
68
|
-
"width": 26,
|
|
69
|
-
},
|
|
70
|
-
undefined,
|
|
71
|
-
{
|
|
72
|
-
"opacity": 1,
|
|
73
|
-
},
|
|
74
|
-
]
|
|
75
|
-
}
|
|
76
|
-
>
|
|
77
|
-
<View
|
|
78
|
-
style={
|
|
79
|
-
[
|
|
80
|
-
{
|
|
81
|
-
"flex": 1,
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
"alignItems": "center",
|
|
85
|
-
"justifyContent": "center",
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
"borderColor": "#b2b2b2",
|
|
89
|
-
"borderRadius": 13,
|
|
90
|
-
"borderWidth": 2,
|
|
91
|
-
},
|
|
92
|
-
undefined,
|
|
93
|
-
]
|
|
94
|
-
}
|
|
95
|
-
>
|
|
96
|
-
<Text
|
|
97
|
-
style={
|
|
98
|
-
[
|
|
99
|
-
{
|
|
100
|
-
"backgroundColor": "transparent",
|
|
101
|
-
"color": "#b2b2b2",
|
|
102
|
-
"fontSize": 16,
|
|
103
|
-
"fontWeight": "bold",
|
|
104
|
-
"lineHeight": 16,
|
|
105
|
-
"textAlign": "center",
|
|
106
|
-
},
|
|
107
|
-
undefined,
|
|
108
|
-
]
|
|
109
|
-
}
|
|
110
|
-
>
|
|
111
|
-
+
|
|
112
|
-
</Text>
|
|
113
|
-
</View>
|
|
114
|
-
</View>
|
|
115
|
-
</View>
|
|
31
|
+
/>
|
|
116
32
|
`;
|