UNPKG

13.1 kBJavaScriptView Raw
1import PropTypes from 'prop-types';
2import React from 'react';
3import { Text, Clipboard, StyleSheet, TouchableWithoutFeedback, View, } from 'react-native';
4import { GiftedChatContext } from './GiftedChatContext';
5import { QuickReplies } from './QuickReplies';
6import { MessageText } from './MessageText';
7import { MessageImage } from './MessageImage';
8import { MessageVideo } from './MessageVideo';
9import { MessageAudio } from './MessageAudio';
10import { Time } from './Time';
11import Color from './Color';
12import { StylePropType, isSameUser, isSameDay } from './utils';
13const styles = {
14 left: StyleSheet.create({
15 container: {
16 flex: 1,
17 alignItems: 'flex-start',
18 },
19 wrapper: {
20 borderRadius: 15,
21 backgroundColor: Color.leftBubbleBackground,
22 marginRight: 60,
23 minHeight: 20,
24 justifyContent: 'flex-end',
25 },
26 containerToNext: {
27 borderBottomLeftRadius: 3,
28 },
29 containerToPrevious: {
30 borderTopLeftRadius: 3,
31 },
32 bottom: {
33 flexDirection: 'row',
34 justifyContent: 'flex-start',
35 },
36 }),
37 right: StyleSheet.create({
38 container: {
39 flex: 1,
40 alignItems: 'flex-end',
41 },
42 wrapper: {
43 borderRadius: 15,
44 backgroundColor: Color.defaultBlue,
45 marginLeft: 60,
46 minHeight: 20,
47 justifyContent: 'flex-end',
48 },
49 containerToNext: {
50 borderBottomRightRadius: 3,
51 },
52 containerToPrevious: {
53 borderTopRightRadius: 3,
54 },
55 bottom: {
56 flexDirection: 'row',
57 justifyContent: 'flex-end',
58 },
59 }),
60 content: StyleSheet.create({
61 tick: {
62 fontSize: 10,
63 backgroundColor: Color.backgroundTransparent,
64 color: Color.white,
65 },
66 tickView: {
67 flexDirection: 'row',
68 marginRight: 10,
69 },
70 username: {
71 top: -3,
72 left: 0,
73 fontSize: 12,
74 backgroundColor: 'transparent',
75 color: '#aaa',
76 },
77 usernameView: {
78 flexDirection: 'row',
79 marginHorizontal: 10,
80 },
81 }),
82};
83const DEFAULT_OPTION_TITLES = ['Copy Text', 'Cancel'];
84export default class Bubble extends React.Component {
85 constructor() {
86 super(...arguments);
87 this.onPress = () => {
88 if (this.props.onPress) {
89 this.props.onPress(this.context, this.props.currentMessage);
90 }
91 };
92 this.onLongPress = () => {
93 const { currentMessage } = this.props;
94 if (this.props.onLongPress) {
95 this.props.onLongPress(this.context, this.props.currentMessage);
96 }
97 else if (currentMessage && currentMessage.text) {
98 const { optionTitles } = this.props;
99 const options = optionTitles && optionTitles.length > 0
100 ? optionTitles.slice(0, 2)
101 : DEFAULT_OPTION_TITLES;
102 const cancelButtonIndex = options.length - 1;
103 this.context.actionSheet().showActionSheetWithOptions({
104 options,
105 cancelButtonIndex,
106 }, (buttonIndex) => {
107 switch (buttonIndex) {
108 case 0:
109 Clipboard.setString(currentMessage.text);
110 break;
111 default:
112 break;
113 }
114 });
115 }
116 };
117 }
118 styledBubbleToNext() {
119 const { currentMessage, nextMessage, position, containerToNextStyle, } = this.props;
120 if (currentMessage &&
121 nextMessage &&
122 position &&
123 isSameUser(currentMessage, nextMessage) &&
124 isSameDay(currentMessage, nextMessage)) {
125 return [
126 styles[position].containerToNext,
127 containerToNextStyle && containerToNextStyle[position],
128 ];
129 }
130 return null;
131 }
132 styledBubbleToPrevious() {
133 const { currentMessage, previousMessage, position, containerToPreviousStyle, } = this.props;
134 if (currentMessage &&
135 previousMessage &&
136 position &&
137 isSameUser(currentMessage, previousMessage) &&
138 isSameDay(currentMessage, previousMessage)) {
139 return [
140 styles[position].containerToPrevious,
141 containerToPreviousStyle && containerToPreviousStyle[position],
142 ];
143 }
144 return null;
145 }
146 renderQuickReplies() {
147 const { currentMessage, onQuickReply, nextMessage, renderQuickReplySend, quickReplyStyle, quickReplyTextStyle, } = this.props;
148 if (currentMessage && currentMessage.quickReplies) {
149 const { containerStyle, wrapperStyle, ...quickReplyProps } = this.props;
150 if (this.props.renderQuickReplies) {
151 return this.props.renderQuickReplies(quickReplyProps);
152 }
153 return (<QuickReplies currentMessage={currentMessage} onQuickReply={onQuickReply} renderQuickReplySend={renderQuickReplySend} quickReplyStyle={quickReplyStyle} quickReplyTextStyle={quickReplyTextStyle} nextMessage={nextMessage}/>);
154 }
155 return null;
156 }
157 renderMessageText() {
158 if (this.props.currentMessage && this.props.currentMessage.text) {
159 const { containerStyle, wrapperStyle, optionTitles, ...messageTextProps } = this.props;
160 if (this.props.renderMessageText) {
161 return this.props.renderMessageText(messageTextProps);
162 }
163 return <MessageText {...messageTextProps}/>;
164 }
165 return null;
166 }
167 renderMessageImage() {
168 if (this.props.currentMessage && this.props.currentMessage.image) {
169 const { containerStyle, wrapperStyle, ...messageImageProps } = this.props;
170 if (this.props.renderMessageImage) {
171 return this.props.renderMessageImage(messageImageProps);
172 }
173 return <MessageImage {...messageImageProps}/>;
174 }
175 return null;
176 }
177 renderMessageVideo() {
178 if (this.props.currentMessage && this.props.currentMessage.video) {
179 const { containerStyle, wrapperStyle, ...messageVideoProps } = this.props;
180 if (this.props.renderMessageVideo) {
181 return this.props.renderMessageVideo(messageVideoProps);
182 }
183 return <MessageVideo {...messageVideoProps}/>;
184 }
185 return null;
186 }
187 renderMessageAudio() {
188 if (this.props.currentMessage && this.props.currentMessage.audio) {
189 const { containerStyle, wrapperStyle, ...messageAudioProps } = this.props;
190 if (this.props.renderMessageAudio) {
191 return this.props.renderMessageAudio(messageAudioProps);
192 }
193 return <MessageAudio {...messageAudioProps}/>;
194 }
195 return null;
196 }
197 renderTicks() {
198 const { currentMessage, renderTicks, user } = this.props;
199 if (renderTicks && currentMessage) {
200 return renderTicks(currentMessage);
201 }
202 if (currentMessage &&
203 user &&
204 currentMessage.user &&
205 currentMessage.user._id !== user._id) {
206 return null;
207 }
208 if (currentMessage &&
209 (currentMessage.sent || currentMessage.received || currentMessage.pending)) {
210 return (<View style={styles.content.tickView}>
211 {!!currentMessage.sent && (<Text style={[styles.content.tick, this.props.tickStyle]}>✓</Text>)}
212 {!!currentMessage.received && (<Text style={[styles.content.tick, this.props.tickStyle]}>✓</Text>)}
213 {!!currentMessage.pending && (<Text style={[styles.content.tick, this.props.tickStyle]}>🕓</Text>)}
214 </View>);
215 }
216 return null;
217 }
218 renderTime() {
219 if (this.props.currentMessage && this.props.currentMessage.createdAt) {
220 const { containerStyle, wrapperStyle, textStyle, ...timeProps } = this.props;
221 if (this.props.renderTime) {
222 return this.props.renderTime(timeProps);
223 }
224 return <Time {...timeProps}/>;
225 }
226 return null;
227 }
228 renderUsername() {
229 const { currentMessage, user, renderUsername } = this.props;
230 if (this.props.renderUsernameOnMessage && currentMessage) {
231 if (user && currentMessage.user._id === user._id) {
232 return null;
233 }
234 if (renderUsername) {
235 return renderUsername(currentMessage.user);
236 }
237 return (<View style={styles.content.usernameView}>
238 <Text style={[styles.content.username, this.props.usernameStyle]}>
239 ~ {currentMessage.user.name}
240 </Text>
241 </View>);
242 }
243 return null;
244 }
245 renderCustomView() {
246 if (this.props.renderCustomView) {
247 return this.props.renderCustomView(this.props);
248 }
249 return null;
250 }
251 renderBubbleContent() {
252 return this.props.isCustomViewBottom ? (<View>
253 {this.renderMessageImage()}
254 {this.renderMessageVideo()}
255 {this.renderMessageAudio()}
256 {this.renderMessageText()}
257 {this.renderCustomView()}
258 </View>) : (<View>
259 {this.renderCustomView()}
260 {this.renderMessageImage()}
261 {this.renderMessageVideo()}
262 {this.renderMessageAudio()}
263 {this.renderMessageText()}
264 </View>);
265 }
266 render() {
267 const { position, containerStyle, wrapperStyle, bottomContainerStyle, } = this.props;
268 return (<View style={[
269 styles[position].container,
270 containerStyle && containerStyle[position],
271 ]}>
272 <View style={[
273 styles[position].wrapper,
274 this.styledBubbleToNext(),
275 this.styledBubbleToPrevious(),
276 wrapperStyle && wrapperStyle[position],
277 ]}>
278 <TouchableWithoutFeedback onPress={this.onPress} onLongPress={this.onLongPress} accessibilityRole='text' {...this.props.touchableProps}>
279 <View>
280 {this.renderBubbleContent()}
281 <View style={[
282 styles[position].bottom,
283 bottomContainerStyle && bottomContainerStyle[position],
284 ]}>
285 {this.renderUsername()}
286 {this.renderTime()}
287 {this.renderTicks()}
288 </View>
289 </View>
290 </TouchableWithoutFeedback>
291 </View>
292 {this.renderQuickReplies()}
293 </View>);
294 }
295}
296Bubble.contextType = GiftedChatContext;
297Bubble.defaultProps = {
298 touchableProps: {},
299 onPress: null,
300 onLongPress: null,
301 renderMessageImage: null,
302 renderMessageVideo: null,
303 renderMessageAudio: null,
304 renderMessageText: null,
305 renderCustomView: null,
306 renderUsername: null,
307 renderTicks: null,
308 renderTime: null,
309 renderQuickReplies: null,
310 onQuickReply: null,
311 position: 'left',
312 optionTitles: DEFAULT_OPTION_TITLES,
313 currentMessage: {
314 text: null,
315 createdAt: null,
316 image: null,
317 },
318 nextMessage: {},
319 previousMessage: {},
320 containerStyle: {},
321 wrapperStyle: {},
322 bottomContainerStyle: {},
323 tickStyle: {},
324 usernameStyle: {},
325 containerToNextStyle: {},
326 containerToPreviousStyle: {},
327};
328Bubble.propTypes = {
329 user: PropTypes.object.isRequired,
330 touchableProps: PropTypes.object,
331 onLongPress: PropTypes.func,
332 renderMessageImage: PropTypes.func,
333 renderMessageVideo: PropTypes.func,
334 renderMessageAudio: PropTypes.func,
335 renderMessageText: PropTypes.func,
336 renderCustomView: PropTypes.func,
337 isCustomViewBottom: PropTypes.bool,
338 renderUsernameOnMessage: PropTypes.bool,
339 renderUsername: PropTypes.func,
340 renderTime: PropTypes.func,
341 renderTicks: PropTypes.func,
342 renderQuickReplies: PropTypes.func,
343 onQuickReply: PropTypes.func,
344 position: PropTypes.oneOf(['left', 'right']),
345 optionTitles: PropTypes.arrayOf(PropTypes.string),
346 currentMessage: PropTypes.object,
347 nextMessage: PropTypes.object,
348 previousMessage: PropTypes.object,
349 containerStyle: PropTypes.shape({
350 left: StylePropType,
351 right: StylePropType,
352 }),
353 wrapperStyle: PropTypes.shape({
354 left: StylePropType,
355 right: StylePropType,
356 }),
357 bottomContainerStyle: PropTypes.shape({
358 left: StylePropType,
359 right: StylePropType,
360 }),
361 tickStyle: StylePropType,
362 usernameStyle: StylePropType,
363 containerToNextStyle: PropTypes.shape({
364 left: StylePropType,
365 right: StylePropType,
366 }),
367 containerToPreviousStyle: PropTypes.shape({
368 left: StylePropType,
369 right: StylePropType,
370 }),
371};
372//# sourceMappingURL=Bubble.js.map
\No newline at end of file