ordering-ui-react-native 0.22.67-release → 0.22.68-release

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.
@@ -16,578 +16,578 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'
16
16
  import { getLogisticTag } from '../../utils'
17
17
 
18
18
  const ORDER_STATUS: any = {
19
- 0: 'ORDER_STATUS_PENDING',
20
- 1: 'ORDERS_COMPLETED',
21
- 2: 'ORDER_REJECTED',
22
- 3: 'ORDER_STATUS_IN_BUSINESS',
23
- 4: 'ORDER_READY',
24
- 5: 'ORDER_REJECTED_RESTAURANT',
25
- 6: 'ORDER_STATUS_CANCELLEDBYDRIVER',
26
- 7: 'ORDER_STATUS_ACCEPTEDBYRESTAURANT',
27
- 8: 'ORDER_CONFIRMED_ACCEPTED_BY_DRIVER',
28
- 9: 'ORDER_PICKUP_COMPLETED_BY_DRIVER',
29
- 10: 'ORDER_PICKUP_FAILED_BY_DRIVER',
30
- 11: 'ORDER_DELIVERY_COMPLETED_BY_DRIVER',
31
- 12: 'ORDER_DELIVERY_FAILED_BY_DRIVER',
32
- 13: 'PREORDER',
33
- 14: 'ORDER_NOT_READY',
34
- 15: 'ORDER_PICKEDUP_COMPLETED_BY_CUSTOMER',
35
- 16: 'ORDER_STATUS_CANCELLED_BY_CUSTOMER',
36
- 17: 'ORDER_NOT_PICKEDUP_BY_CUSTOMER',
37
- 18: 'ORDER_DRIVER_ALMOST_ARRIVED_BUSINESS',
38
- 19: 'ORDER_DRIVER_ALMOST_ARRIVED_CUSTOMER',
39
- 20: 'ORDER_CUSTOMER_ALMOST_ARRIVED_BUSINESS',
40
- 21: 'ORDER_CUSTOMER_ARRIVED_BUSINESS',
41
- 22: 'ORDER_LOOKING_FOR_DRIVER',
42
- 23: 'ORDER_DRIVER_ON_WAY',
43
- 24: 'ORDER_DRIVER_WAITING_FOR_ORDER',
44
- 25: 'ORDER_ACCEPTED_BY_DRIVER_COMPANY',
45
- 26: 'ORDER_DRIVER_ARRIVED_CUSTOMER'
19
+ 0: 'ORDER_STATUS_PENDING',
20
+ 1: 'ORDERS_COMPLETED',
21
+ 2: 'ORDER_REJECTED',
22
+ 3: 'ORDER_STATUS_IN_BUSINESS',
23
+ 4: 'ORDER_READY',
24
+ 5: 'ORDER_REJECTED_RESTAURANT',
25
+ 6: 'ORDER_STATUS_CANCELLEDBYDRIVER',
26
+ 7: 'ORDER_STATUS_ACCEPTEDBYRESTAURANT',
27
+ 8: 'ORDER_CONFIRMED_ACCEPTED_BY_DRIVER',
28
+ 9: 'ORDER_PICKUP_COMPLETED_BY_DRIVER',
29
+ 10: 'ORDER_PICKUP_FAILED_BY_DRIVER',
30
+ 11: 'ORDER_DELIVERY_COMPLETED_BY_DRIVER',
31
+ 12: 'ORDER_DELIVERY_FAILED_BY_DRIVER',
32
+ 13: 'PREORDER',
33
+ 14: 'ORDER_NOT_READY',
34
+ 15: 'ORDER_PICKEDUP_COMPLETED_BY_CUSTOMER',
35
+ 16: 'ORDER_STATUS_CANCELLED_BY_CUSTOMER',
36
+ 17: 'ORDER_NOT_PICKEDUP_BY_CUSTOMER',
37
+ 18: 'ORDER_DRIVER_ALMOST_ARRIVED_BUSINESS',
38
+ 19: 'ORDER_DRIVER_ALMOST_ARRIVED_CUSTOMER',
39
+ 20: 'ORDER_CUSTOMER_ALMOST_ARRIVED_BUSINESS',
40
+ 21: 'ORDER_CUSTOMER_ARRIVED_BUSINESS',
41
+ 22: 'ORDER_LOOKING_FOR_DRIVER',
42
+ 23: 'ORDER_DRIVER_ON_WAY',
43
+ 24: 'ORDER_DRIVER_WAITING_FOR_ORDER',
44
+ 25: 'ORDER_ACCEPTED_BY_DRIVER_COMPANY',
45
+ 26: 'ORDER_DRIVER_ARRIVED_CUSTOMER'
46
46
  }
47
47
 
48
48
  const filterSpecialStatus = ['prepared_in', 'delivered_in', 'delivery_datetime']
49
49
 
50
50
 
51
51
  const MessagesUI = (props: MessagesParams) => {
52
- const {
53
- type,
54
- order,
55
- messages,
56
- image,
57
- message,
58
- sendMessage,
59
- setCanRead,
60
- setMessage,
61
- handleSend,
62
- setImage,
63
- readMessages,
64
- onClose,
65
- business,
66
- driver,
67
- onMessages,
68
- isMeesageListing
69
- } = props
70
-
71
- const [{ user }] = useSession()
72
- const [{ configs }] = useConfig()
73
- const [{ parseDate }] = useUtils()
74
- const [, t] = useLanguage()
75
- const [, { showToast }] = useToast();
76
-
77
- const [formattedMessages, setFormattedMessages] = useState<Array<any>>([])
78
- const [isKeyboardShow, setIsKeyboardShow] = useState(false)
79
- const previousStatus = [1, 2, 5, 6, 10, 11, 12, 15, 16, 17]
80
- const chatDisabled = previousStatus.includes(order?.status)
81
- const { height } = useWindowDimensions();
82
- const { top, bottom } = useSafeAreaInsets();
83
-
84
- const theme = useTheme();
85
-
86
- const quickMessageList = [
87
- { key: 'customer_message_1', text: t('CUSTOMER_MESSAGE_1', 'Lorem ipsum 1') },
88
- { key: 'customer_message_2', text: t('CUSTOMER_MESSAGE_2', 'Lorem ipsum 2') },
89
- { key: 'customer_message_3', text: t('CUSTOMER_MESSAGE_3', 'Lorem ipsum 3') },
90
- { key: 'customer_message_4', text: t('CUSTOMER_MESSAGE_4', 'Lorem ipsum 4') }
91
- ]
92
-
93
- const handleClickQuickMessage = (text: string) => {
94
- setMessage && setMessage(`${message}${text}`)
95
- }
96
-
97
- const onChangeMessage = (val: string) => {
98
- setMessage && setMessage(val)
99
- }
100
-
101
- const removeImage = () => {
102
- setImage && setImage(null)
103
- }
104
-
105
- const handleImagePicker = () => {
106
- launchImageLibrary({ mediaType: 'photo', maxHeight: 2048, maxWidth: 2048, includeBase64: true }, (response: any) => {
107
- if (response?.didCancel) {
108
- showToast(ToastType.Error, t('IMAGE_CANCELLED', 'User cancelled image picker'));
109
- } else if (response?.errorMessage) {
110
- showToast(ToastType.Error, response.errorMessage);
111
- } else {
112
- if (response?.assets?.length > 0) {
113
- const image = response?.assets[0]
114
- const url = `data:${image.type};base64,${image.base64}`
115
- setImage && setImage(url);
116
- } else {
117
- showToast(ToastType.Error, t('IMAGE_NOT_FOUND', 'Image not found'));
118
- }
119
- }
120
- });
121
- };
122
-
123
- const onSubmit = (values: any) => {
124
- handleSend && handleSend()
125
- setImage && setImage(null)
126
- setMessage && setMessage('')
127
- }
128
-
129
- const messageConsole = (message: any) => {
130
- return message.change?.attribute !== 'driver_id'
131
- ?
132
- `${t('ORDER', 'Order')} ${t(message.change.attribute.toUpperCase(), message.change.attribute.replace('_', ' '))} ${t('CHANGED_FROM', 'Changed from')} ${filterSpecialStatus.includes(message.change.attribute)
133
- ? `${message.change.old === null ? '0' : message.change.old} ${t('TO', 'to')} ${message.change.new} ${t('MINUTES', 'Minutes')}`
134
- : `${message.change?.attribute !== 'logistic_status'
135
- ? message.change.old !== null && t(ORDER_STATUS[parseInt(message.change.old, 10)])
136
- : message.change.old !== null && getLogisticTag(message.change.old)} ${t('TO', 'to')} ${message.change?.attribute !== 'logistic_status'
137
- ? t(ORDER_STATUS[parseInt(message.change.new, 10)])
138
- : getLogisticTag(message.change.new)}`
139
- }`
140
- : message.change.new
141
- ?
142
- `${message.driver?.name} ${message.driver?.lastname !== null ? message.driver.lastname : ''} ${t('WAS_ASSIGNED_AS_DRIVER', 'Was assigned as driver')} ${message.comment ? message.comment.length : ''}`
143
- :
144
- `${t('DRIVER_UNASSIGNED', 'Driver unassigned')}`
145
- }
146
-
147
- useEffect(() => {
148
- const newMessages: Array<any> = []
149
- const _console = `${t('ORDER_PLACED_FOR', 'Order placed for')} ${parseDate(order?.created_at)} ${t('VIA', 'Via')} ${order?.app_id ? t(order?.app_id.toUpperCase(), order?.app_id) : t('OTHER', 'Other')}`
150
- const firstMessage = {
151
- _id: 0,
152
- type: 0,
153
- text: _console,
154
- createdAt: parseDate(order?.created_at, { outputFormat: 'YYYY-MM-DD HH:mm:ss' }),
155
- system: true
156
- }
157
- messages.messages.map((message: any) => {
158
- if (message.change?.attribute === 'driver_group_id') return
159
- if (business && message.type !== 0 && (props?.messagesToShow?.messages?.length || message?.can_see?.includes('2'))) {
160
- newMessages.push({
161
- _id: message?.id,
162
- text: message.type === 1 ? messageConsole(message) : message.comment,
163
- createdAt: message.type !== 0 && parseDate(message?.created_at, { outputFormat: 'YYYY-MM-DD HH:mm:ss' }),
164
- image: message.source,
165
- type: message.type,
166
- system: message.type === 1,
167
- user: {
168
- _id: message.author && message.author.id,
169
- name: message.author && message.author.name,
170
- avatar: message.author && (message.author.id !== user.id && type === USER_TYPE.DRIVER ? order?.driver?.photo : order?.business?.logo)
171
- }
172
- });
173
- }
174
-
175
- if (driver && message.type !== 0 && (props?.messagesToShow?.messages?.length || message?.can_see?.includes('4'))) {
176
- newMessages.push({
177
- _id: message?.id,
178
- text: message.type === 1 ? messageConsole(message) : message.comment,
179
- createdAt: message.type !== 0 && parseDate(message?.created_at, { outputFormat: 'YYYY-MM-DD HH:mm:ss' }),
180
- image: message.source,
181
- type: message.type,
182
- system: message.type === 1,
183
- user: {
184
- _id: message.author && message.author.id,
185
- name: message.author && message.author.name,
186
- avatar: message.author && (message.author.id !== user.id && type === USER_TYPE.DRIVER ? order?.driver?.photo : order?.business?.logo)
187
- }
188
- });
189
- }
190
-
191
- if (message.type === 0) {
192
- newMessages.push(firstMessage);
193
- }
194
- })
195
- let _arrayMessages = [...newMessages.reverse()]
196
-
197
- setFormattedMessages(_arrayMessages);
198
- }, [messages.messages.length, business, driver])
199
-
200
- useEffect(() => {
201
- const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', () => {
202
- setIsKeyboardShow(true)
203
- })
204
- const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => {
205
- setIsKeyboardShow(false)
206
- })
207
- const keyboardWillHideListener = Keyboard.addListener('keyboardWillHide', () => {
208
- setIsKeyboardShow(false)
209
- })
210
- return () => {
211
- keyboardDidShowListener.remove()
212
- keyboardDidHideListener.remove()
213
- keyboardWillHideListener.remove()
214
- }
215
- }, [])
216
-
217
- useEffect(() => {
218
- if (business) setCanRead({ business: true, administrator: true, customer: true, driver: false })
219
- else if (driver) setCanRead({ business: false, administrator: true, customer: true, driver: true })
220
- }, [business, driver])
221
-
222
- const RenderActions = (props: any) => {
223
- return (
224
- <Actions
225
- {...props}
226
- options={{
227
- 'Send Image': () => handleImagePicker(),
228
- }}
229
- containerStyle={styles.containerActions}
230
- optionTintColor='#222845'
231
- icon={() => (
232
- <>
233
- <OIconButton
234
- borderColor={theme.colors.white}
235
- style={{ width: 32, height: 44, borderRadius: 10, backgroundColor: theme.colors.clear, borderColor: theme.colors.clear }}
236
- icon={image ? { uri: image } : theme.images.general.image}
237
- iconStyle={{ borderRadius: image ? 10 : 0, width: image ? 32 : 16, height: image ? 32 : 16 }}
238
- onClick={handleImagePicker}
239
- iconCover
240
- />
241
- {image && (
242
- <TouchableOpacity
243
- style={{ position: 'absolute', top: -5, right: -5, borderColor: theme.colors.backgroundDark, backgroundColor: theme.colors.white, borderRadius: 25 }}
244
- onPress={removeImage}
245
- >
246
- <MaterialCommunityIcon name='close-circle-outline' color={theme.colors.backgroundDark} size={24} />
247
- </TouchableOpacity>
248
- )}
249
- </>
250
- )}
251
- />
252
- )
253
- }
254
-
255
- const renderAccessory = () => {
256
- return (
257
- !chatDisabled &&
258
- <QuickMessageContainer
259
- style={{
260
- marginLeft: 10,
261
- marginBottom: 10
262
- }}
263
- contentContainerStyle={{
264
- alignItems: 'center',
265
- }}
266
- horizontal
267
- showsHorizontalScrollIndicator={false}
268
- >
269
- {quickMessageList.map((quickMessage, i) => (
270
- <OButton
271
- key={i}
272
- text={quickMessage.text}
273
- bgColor='#E9ECEF'
274
- borderColor='#E9ECEF'
275
- imgRightSrc={null}
276
- textStyle={{
277
- fontSize: 11,
278
- lineHeight: 16,
279
- color: '#414954'
280
- }}
281
- style={{ ...styles.editButton }}
282
- onClick={() => handleClickQuickMessage(quickMessage.text)}
283
- />
284
- ))}
285
- </QuickMessageContainer>
286
- )
287
- }
288
-
289
- const renderInputToolbar = (props: typeof InputToolbarProps) => (
290
- <InputToolbar
291
- {...props}
292
- containerStyle={{
293
- padding: Platform.OS === 'ios' && isKeyboardShow ? 0 : 10,
294
- flexDirection: 'column-reverse'
295
- }}
296
- primaryStyle={{ alignItems: 'center', justifyContent: 'flex-start' }}
297
- renderAccessory={() => renderAccessory()}
298
- />
299
- )
300
-
301
- const renderComposer = (props: typeof ComposerProps) => (
302
- chatDisabled ? (
303
- <View
304
- style={{
305
- width: '100%',
306
- flexDirection: 'column',
307
- alignItems: 'center'
308
- }}
309
- >
310
- <MaterialCommunityIcon
311
- name='close-octagon-outline'
312
- size={24}
313
- />
314
- <OText size={14}>{t('NOT_SEND_MESSAGES', 'You can\'t send messages because the order has ended')}</OText>
315
- </View>
316
- ) : (
317
- <View style={{
318
- flexDirection: 'row', width: '80%', alignItems: 'center', backgroundColor: theme.colors.backgroundGray100,
319
- borderRadius: 7.6,
320
- }}>
321
- <Composer
322
- {...props}
323
- textInputStyle={{
324
- height: 32,
325
- minHeight: 32,
326
- alignItems: 'center',
327
- justifyContent: 'center',
328
- paddingHorizontal: 12,
329
- borderColor: '#DBDCDB',
330
- color: '#010300',
331
- }}
332
- textInputProps={{
333
- value: message,
334
- onSubmitEditing: onSubmit,
335
- returnKeyType: message ? 'send' : 'done',
336
- blurOnSubmit: true,
337
- multiline: false,
338
- numberOfLines: 1,
339
- autoCorrect: false,
340
- autoCompleteType: 'off',
341
- enablesReturnKeyAutomatically: false
342
- }}
343
- placeholder={t('WRITE_MESSAGE', 'Write message...')}
344
- />
345
- <RenderActions {...props} />
346
- </View>
347
- )
348
- )
349
-
350
- const renderSend = (props: any) => {
351
- const isDisabled = (sendMessage?.loading || (message === '' && !image) || messages?.loading)
352
- return (
353
- <Send
354
- {...props}
355
- disabled={isDisabled}
356
- alwaysShowSend
357
- containerStyle={styles.containerSend}
358
- >
359
- <OIconButton
360
- onClick={onSubmit}
361
- style={{
362
- height: 44,
363
- width: 44,
364
- borderRadius: 7.6,
365
- opacity: isDisabled ? 0.2 : 1,
366
- borderColor: isDisabled ? theme.colors.secondary : theme.colors.primary,
367
- backgroundColor: isDisabled ? theme.colors.secondary : theme.colors.primary,
368
- }}
369
- iconStyle={{ marginTop: 3, marginRight: 2 }}
370
- icon={theme.images.general.enter}
371
- iconColor={isDisabled ? '#000' : '#fff'}
372
- disabled={isDisabled}
373
- disabledColor={theme.colors.secondary}
374
- />
375
- </Send>
376
- )
377
- }
378
-
379
- const renderBubble = (props: any) => (
380
- <Bubble
381
- {...props}
382
- textStyle={{
383
- left: {},
384
- right: { color: theme.colors.white }
385
- }}
386
- containerStyle={{
387
- left: { marginVertical: 5, borderBottomRightRadius: 7.6 },
388
- right: { marginVertical: 5, borderBottomRightRadius: 7.6 }
389
- }}
390
- wrapperStyle={{
391
- left: { backgroundColor: '#f7f7f7', padding: 5, borderBottomLeftRadius: 0 },
392
- right: { backgroundColor: theme.colors.primary, padding: 5, borderBottomRightRadius: 0 }
393
- }}
394
- />
395
- )
396
-
397
- const renderMessageImage = (props: any) => (
398
- <MessageImage
399
- {...props}
400
- />
401
- )
402
-
403
- const renderScrollToBottomComponent = () => (
404
- <MaterialCommunityIcon name='chevron-double-down' size={32} />
405
- )
406
-
407
- const getViewHeight = () => {
408
- if (Platform.OS === 'android') {
409
- return '100%';
410
- } else {
411
- return height - top - bottom - (isKeyboardShow ? 48 : 0);
412
- }
413
- }
414
-
415
- const onLongPress = (context: any, message: any) => {
416
- const options = [
417
- t('COPY_TEXT', 'Copy text'),
418
- t('CANCEL', 'Cancel'),
419
- ];
420
- const cancelButtonIndex = options.length - 1;
421
- context.actionSheet().showActionSheetWithOptions({
422
- options,
423
- cancelButtonIndex
424
- }, (buttonIndex: any) => buttonIndex === 0 && Clipboard.setString(message.text)
425
- );
426
- }
427
-
428
- useEffect(() => {
429
- if (!order?.id || messages?.loading) return
430
- readMessages && readMessages()
431
- }, [order?.id, messages?.loading])
432
-
433
- return (
434
- <View style={{ height: getViewHeight(), width: '100%', paddingTop: 12, backgroundColor: 'white' }}>
435
- <Wrapper>
436
- {!isMeesageListing ? (
437
- <Header>
438
- <TouchableOpacity onPress={onClose} style={{ paddingStart: 10, borderColor: theme.colors.clear }}>
439
- <AntDesignIcon name='arrowleft' size={26} />
440
- </TouchableOpacity>
441
- <View style={{ marginRight: 10, shadowColor: theme.colors.black, shadowOpacity: 0.1, shadowOffset: { width: 0, height: 1 }, shadowRadius: 2 }}>
442
- <OIcon
443
- url={type === USER_TYPE.DRIVER ? order?.driver?.photo : order?.business?.logo}
444
- width={32}
445
- height={32}
446
- style={{ borderRadius: 7.6 }}
447
- />
448
- </View>
449
- <TitleHeader>
450
- <OText size={14} lineHeight={21} weight={'600'}>{type === USER_TYPE.DRIVER ? order?.driver?.name : order?.business?.name}</OText>
451
- <OText size={12} color={theme.colors.textSecondary}>{type === USER_TYPE.DRIVER ? t('DRIVER', 'Driver') : t('BUSINESS', 'Business')}</OText>
452
- </TitleHeader>
453
- </Header>
454
- ) : (
455
- <ProfileMessageHeader>
456
- <View style={{ ...styles.headerTitle }}>
457
- <TouchableOpacity onPress={onClose} style={styles.headerItem}>
458
- <AntDesignIcon name='arrowleft' size={26} />
459
- </TouchableOpacity>
460
- <OText size={18}>{t('ORDER', theme?.defaultLanguages?.ORDER || 'Order')} #{order?.id}</OText>
461
- </View>
462
- <View style={{ ...styles.typeWraper }}>
463
- {order.business && (
464
- <TouchableOpacity
465
- onPress={() => onMessages({ business: true, driver: false })}
466
- >
467
- <MessageTypeItem
468
- active={business}
469
- >
470
- <OIcon
471
- url={order?.business?.logo}
472
- width={32}
473
- height={32}
474
- style={{ borderRadius: 32 }}
475
- />
476
- </MessageTypeItem>
477
- </TouchableOpacity>
478
- )}
479
-
480
- {order?.driver && (
481
- <TouchableOpacity
482
- onPress={() => onMessages({ business: false, driver: true })}
483
- >
484
- <MessageTypeItem
485
- active={driver}
486
- >
487
- <OIcon
488
- url={order?.driver?.photo}
489
- width={32}
490
- height={32}
491
- style={{ borderRadius: 32 }}
492
- />
493
- </MessageTypeItem>
494
- </TouchableOpacity>
495
- )}
496
- </View>
497
- </ProfileMessageHeader>
498
- )}
499
- <GiftedChat
500
- messages={formattedMessages}
501
- user={{
502
- _id: user.id,
503
- name: user.name,
504
- avatar: user.photo
505
- }}
506
- onSend={onSubmit}
507
- onInputTextChanged={onChangeMessage}
508
- alignTop
509
- onLongPress={(context: any, message: any) => onLongPress(context, message)}
510
- scrollToBottom
511
- renderAvatarOnTop
512
- renderUsernameOnMessage
513
- renderAvatar={() => null}
514
- renderInputToolbar={renderInputToolbar}
515
- renderComposer={renderComposer}
516
- renderSend={renderSend}
517
- renderBubble={renderBubble}
518
- renderMessageImage={renderMessageImage}
519
- scrollToBottomComponent={() => renderScrollToBottomComponent()}
520
- messagesContainerStyle={{
521
- paddingTop: 18,
522
- paddingHorizontal: 28,
523
- paddingBottom: 55
524
- }}
525
- isLoadingEarlier={messages.loading}
526
- renderLoading={() => <ActivityIndicator size="small" color="#000" />}
527
- keyboardShouldPersistTaps='handled'
528
- />
529
- </Wrapper>
530
- </View>
531
- )
52
+ const {
53
+ type,
54
+ order,
55
+ messages,
56
+ image,
57
+ message,
58
+ sendMessage,
59
+ setCanRead,
60
+ setMessage,
61
+ handleSend,
62
+ setImage,
63
+ readMessages,
64
+ onClose,
65
+ business,
66
+ driver,
67
+ onMessages,
68
+ isMeesageListing
69
+ } = props
70
+
71
+ const [{ user }] = useSession()
72
+ const [{ configs }] = useConfig()
73
+ const [{ parseDate }] = useUtils()
74
+ const [, t] = useLanguage()
75
+ const [, { showToast }] = useToast();
76
+
77
+ const [formattedMessages, setFormattedMessages] = useState<Array<any>>([])
78
+ const [isKeyboardShow, setIsKeyboardShow] = useState(false)
79
+ const previousStatus = [1, 2, 5, 6, 10, 11, 12, 15, 16, 17]
80
+ const chatDisabled = previousStatus.includes(order?.status)
81
+ const { height } = useWindowDimensions();
82
+ const { top, bottom } = useSafeAreaInsets();
83
+
84
+ const theme = useTheme();
85
+
86
+ const quickMessageList = [
87
+ { key: 'customer_message_1', text: t('CUSTOMER_MESSAGE_1', 'Lorem ipsum 1') },
88
+ { key: 'customer_message_2', text: t('CUSTOMER_MESSAGE_2', 'Lorem ipsum 2') },
89
+ { key: 'customer_message_3', text: t('CUSTOMER_MESSAGE_3', 'Lorem ipsum 3') },
90
+ { key: 'customer_message_4', text: t('CUSTOMER_MESSAGE_4', 'Lorem ipsum 4') }
91
+ ]
92
+
93
+ const handleClickQuickMessage = (text: string) => {
94
+ setMessage && setMessage(`${message}${text}`)
95
+ }
96
+
97
+ const onChangeMessage = (val: string) => {
98
+ setMessage && setMessage(val)
99
+ }
100
+
101
+ const removeImage = () => {
102
+ setImage && setImage(null)
103
+ }
104
+
105
+ const handleImagePicker = () => {
106
+ launchImageLibrary({ mediaType: 'photo', maxHeight: 2048, maxWidth: 2048, includeBase64: true }, (response: any) => {
107
+ if (response?.didCancel) {
108
+ showToast(ToastType.Error, t('IMAGE_CANCELLED', 'User cancelled image picker'));
109
+ } else if (response?.errorMessage) {
110
+ showToast(ToastType.Error, response.errorMessage);
111
+ } else {
112
+ if (response?.assets?.length > 0) {
113
+ const image = response?.assets[0]
114
+ const url = `data:${image.type};base64,${image.base64}`
115
+ setImage && setImage(url);
116
+ } else {
117
+ showToast(ToastType.Error, t('IMAGE_NOT_FOUND', 'Image not found'));
118
+ }
119
+ }
120
+ });
121
+ };
122
+
123
+ const onSubmit = (values: any) => {
124
+ handleSend && handleSend()
125
+ setImage && setImage(null)
126
+ setMessage && setMessage('')
127
+ }
128
+
129
+ const messageConsole = (message: any) => {
130
+ return message.change?.attribute !== 'driver_id'
131
+ ?
132
+ `${t('ORDER', 'Order')} ${t(message.change.attribute.toUpperCase(), message.change.attribute.replace('_', ' '))} ${t('CHANGED_FROM', 'Changed from')} ${filterSpecialStatus.includes(message.change.attribute)
133
+ ? `${message.change.old === null ? '0' : message.change.old} ${t('TO', 'to')} ${message.change.new} ${t('MINUTES', 'Minutes')}`
134
+ : `${message.change?.attribute !== 'logistic_status'
135
+ ? message.change.old !== null && t(ORDER_STATUS[parseInt(message.change.old, 10)])
136
+ : message.change.old !== null && getLogisticTag(message.change.old, t)} ${t('TO', 'to')} ${message.change?.attribute !== 'logistic_status'
137
+ ? t(ORDER_STATUS[parseInt(message.change.new, 10)])
138
+ : getLogisticTag(message.change.new, t)}`
139
+ }`
140
+ : message.change.new
141
+ ?
142
+ `${message.driver?.name} ${message.driver?.lastname !== null ? message.driver.lastname : ''} ${t('WAS_ASSIGNED_AS_DRIVER', 'Was assigned as driver')} ${message.comment ? message.comment.length : ''}`
143
+ :
144
+ `${t('DRIVER_UNASSIGNED', 'Driver unassigned')}`
145
+ }
146
+
147
+ useEffect(() => {
148
+ const newMessages: Array<any> = []
149
+ const _console = `${t('ORDER_PLACED_FOR', 'Order placed for')} ${parseDate(order?.created_at)} ${t('VIA', 'Via')} ${order?.app_id ? t(order?.app_id.toUpperCase(), order?.app_id) : t('OTHER', 'Other')}`
150
+ const firstMessage = {
151
+ _id: 0,
152
+ type: 0,
153
+ text: _console,
154
+ createdAt: parseDate(order?.created_at, { outputFormat: 'YYYY-MM-DD HH:mm:ss' }),
155
+ system: true
156
+ }
157
+ messages.messages.map((message: any) => {
158
+ if (message.change?.attribute === 'driver_group_id') return
159
+ if (business && message.type !== 0 && (props?.messagesToShow?.messages?.length || message?.can_see?.includes('2'))) {
160
+ newMessages.push({
161
+ _id: message?.id,
162
+ text: message.type === 1 ? messageConsole(message) : message.comment,
163
+ createdAt: message.type !== 0 && parseDate(message?.created_at, { outputFormat: 'YYYY-MM-DD HH:mm:ss' }),
164
+ image: message.source,
165
+ type: message.type,
166
+ system: message.type === 1,
167
+ user: {
168
+ _id: message.author && message.author.id,
169
+ name: message.author && message.author.name,
170
+ avatar: message.author && (message.author.id !== user.id && type === USER_TYPE.DRIVER ? order?.driver?.photo : order?.business?.logo)
171
+ }
172
+ });
173
+ }
174
+
175
+ if (driver && message.type !== 0 && (props?.messagesToShow?.messages?.length || message?.can_see?.includes('4'))) {
176
+ newMessages.push({
177
+ _id: message?.id,
178
+ text: message.type === 1 ? messageConsole(message) : message.comment,
179
+ createdAt: message.type !== 0 && parseDate(message?.created_at, { outputFormat: 'YYYY-MM-DD HH:mm:ss' }),
180
+ image: message.source,
181
+ type: message.type,
182
+ system: message.type === 1,
183
+ user: {
184
+ _id: message.author && message.author.id,
185
+ name: message.author && message.author.name,
186
+ avatar: message.author && (message.author.id !== user.id && type === USER_TYPE.DRIVER ? order?.driver?.photo : order?.business?.logo)
187
+ }
188
+ });
189
+ }
190
+
191
+ if (message.type === 0) {
192
+ newMessages.push(firstMessage);
193
+ }
194
+ })
195
+ let _arrayMessages = [...newMessages.reverse()]
196
+
197
+ setFormattedMessages(_arrayMessages);
198
+ }, [messages.messages.length, business, driver])
199
+
200
+ useEffect(() => {
201
+ const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', () => {
202
+ setIsKeyboardShow(true)
203
+ })
204
+ const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => {
205
+ setIsKeyboardShow(false)
206
+ })
207
+ const keyboardWillHideListener = Keyboard.addListener('keyboardWillHide', () => {
208
+ setIsKeyboardShow(false)
209
+ })
210
+ return () => {
211
+ keyboardDidShowListener.remove()
212
+ keyboardDidHideListener.remove()
213
+ keyboardWillHideListener.remove()
214
+ }
215
+ }, [])
216
+
217
+ useEffect(() => {
218
+ if (business) setCanRead({ business: true, administrator: true, customer: true, driver: false })
219
+ else if (driver) setCanRead({ business: false, administrator: true, customer: true, driver: true })
220
+ }, [business, driver])
221
+
222
+ const RenderActions = (props: any) => {
223
+ return (
224
+ <Actions
225
+ {...props}
226
+ options={{
227
+ 'Send Image': () => handleImagePicker(),
228
+ }}
229
+ containerStyle={styles.containerActions}
230
+ optionTintColor='#222845'
231
+ icon={() => (
232
+ <>
233
+ <OIconButton
234
+ borderColor={theme.colors.white}
235
+ style={{ width: 32, height: 44, borderRadius: 10, backgroundColor: theme.colors.clear, borderColor: theme.colors.clear }}
236
+ icon={image ? { uri: image } : theme.images.general.image}
237
+ iconStyle={{ borderRadius: image ? 10 : 0, width: image ? 32 : 16, height: image ? 32 : 16 }}
238
+ onClick={handleImagePicker}
239
+ iconCover
240
+ />
241
+ {image && (
242
+ <TouchableOpacity
243
+ style={{ position: 'absolute', top: -5, right: -5, borderColor: theme.colors.backgroundDark, backgroundColor: theme.colors.white, borderRadius: 25 }}
244
+ onPress={removeImage}
245
+ >
246
+ <MaterialCommunityIcon name='close-circle-outline' color={theme.colors.backgroundDark} size={24} />
247
+ </TouchableOpacity>
248
+ )}
249
+ </>
250
+ )}
251
+ />
252
+ )
253
+ }
254
+
255
+ const renderAccessory = () => {
256
+ return (
257
+ !chatDisabled &&
258
+ <QuickMessageContainer
259
+ style={{
260
+ marginLeft: 10,
261
+ marginBottom: 10
262
+ }}
263
+ contentContainerStyle={{
264
+ alignItems: 'center',
265
+ }}
266
+ horizontal
267
+ showsHorizontalScrollIndicator={false}
268
+ >
269
+ {quickMessageList.map((quickMessage, i) => (
270
+ <OButton
271
+ key={i}
272
+ text={quickMessage.text}
273
+ bgColor='#E9ECEF'
274
+ borderColor='#E9ECEF'
275
+ imgRightSrc={null}
276
+ textStyle={{
277
+ fontSize: 11,
278
+ lineHeight: 16,
279
+ color: '#414954'
280
+ }}
281
+ style={{ ...styles.editButton }}
282
+ onClick={() => handleClickQuickMessage(quickMessage.text)}
283
+ />
284
+ ))}
285
+ </QuickMessageContainer>
286
+ )
287
+ }
288
+
289
+ const renderInputToolbar = (props: typeof InputToolbarProps) => (
290
+ <InputToolbar
291
+ {...props}
292
+ containerStyle={{
293
+ padding: Platform.OS === 'ios' && isKeyboardShow ? 0 : 10,
294
+ flexDirection: 'column-reverse'
295
+ }}
296
+ primaryStyle={{ alignItems: 'center', justifyContent: 'flex-start' }}
297
+ renderAccessory={() => renderAccessory()}
298
+ />
299
+ )
300
+
301
+ const renderComposer = (props: typeof ComposerProps) => (
302
+ chatDisabled ? (
303
+ <View
304
+ style={{
305
+ width: '100%',
306
+ flexDirection: 'column',
307
+ alignItems: 'center'
308
+ }}
309
+ >
310
+ <MaterialCommunityIcon
311
+ name='close-octagon-outline'
312
+ size={24}
313
+ />
314
+ <OText size={14}>{t('NOT_SEND_MESSAGES', 'You can\'t send messages because the order has ended')}</OText>
315
+ </View>
316
+ ) : (
317
+ <View style={{
318
+ flexDirection: 'row', width: '80%', alignItems: 'center', backgroundColor: theme.colors.backgroundGray100,
319
+ borderRadius: 7.6,
320
+ }}>
321
+ <Composer
322
+ {...props}
323
+ textInputStyle={{
324
+ height: 32,
325
+ minHeight: 32,
326
+ alignItems: 'center',
327
+ justifyContent: 'center',
328
+ paddingHorizontal: 12,
329
+ borderColor: '#DBDCDB',
330
+ color: '#010300',
331
+ }}
332
+ textInputProps={{
333
+ value: message,
334
+ onSubmitEditing: onSubmit,
335
+ returnKeyType: message ? 'send' : 'done',
336
+ blurOnSubmit: true,
337
+ multiline: false,
338
+ numberOfLines: 1,
339
+ autoCorrect: false,
340
+ autoCompleteType: 'off',
341
+ enablesReturnKeyAutomatically: false
342
+ }}
343
+ placeholder={t('WRITE_MESSAGE', 'Write message...')}
344
+ />
345
+ <RenderActions {...props} />
346
+ </View>
347
+ )
348
+ )
349
+
350
+ const renderSend = (props: any) => {
351
+ const isDisabled = (sendMessage?.loading || (message === '' && !image) || messages?.loading)
352
+ return (
353
+ <Send
354
+ {...props}
355
+ disabled={isDisabled}
356
+ alwaysShowSend
357
+ containerStyle={styles.containerSend}
358
+ >
359
+ <OIconButton
360
+ onClick={onSubmit}
361
+ style={{
362
+ height: 44,
363
+ width: 44,
364
+ borderRadius: 7.6,
365
+ opacity: isDisabled ? 0.2 : 1,
366
+ borderColor: isDisabled ? theme.colors.secondary : theme.colors.primary,
367
+ backgroundColor: isDisabled ? theme.colors.secondary : theme.colors.primary,
368
+ }}
369
+ iconStyle={{ marginTop: 3, marginRight: 2 }}
370
+ icon={theme.images.general.enter}
371
+ iconColor={isDisabled ? '#000' : '#fff'}
372
+ disabled={isDisabled}
373
+ disabledColor={theme.colors.secondary}
374
+ />
375
+ </Send>
376
+ )
377
+ }
378
+
379
+ const renderBubble = (props: any) => (
380
+ <Bubble
381
+ {...props}
382
+ textStyle={{
383
+ left: {},
384
+ right: { color: theme.colors.white }
385
+ }}
386
+ containerStyle={{
387
+ left: { marginVertical: 5, borderBottomRightRadius: 7.6 },
388
+ right: { marginVertical: 5, borderBottomRightRadius: 7.6 }
389
+ }}
390
+ wrapperStyle={{
391
+ left: { backgroundColor: '#f7f7f7', padding: 5, borderBottomLeftRadius: 0 },
392
+ right: { backgroundColor: theme.colors.primary, padding: 5, borderBottomRightRadius: 0 }
393
+ }}
394
+ />
395
+ )
396
+
397
+ const renderMessageImage = (props: any) => (
398
+ <MessageImage
399
+ {...props}
400
+ />
401
+ )
402
+
403
+ const renderScrollToBottomComponent = () => (
404
+ <MaterialCommunityIcon name='chevron-double-down' size={32} />
405
+ )
406
+
407
+ const getViewHeight = () => {
408
+ if (Platform.OS === 'android') {
409
+ return '100%';
410
+ } else {
411
+ return height - top - bottom - (isKeyboardShow ? 48 : 0);
412
+ }
413
+ }
414
+
415
+ const onLongPress = (context: any, message: any) => {
416
+ const options = [
417
+ t('COPY_TEXT', 'Copy text'),
418
+ t('CANCEL', 'Cancel'),
419
+ ];
420
+ const cancelButtonIndex = options.length - 1;
421
+ context.actionSheet().showActionSheetWithOptions({
422
+ options,
423
+ cancelButtonIndex
424
+ }, (buttonIndex: any) => buttonIndex === 0 && Clipboard.setString(message.text)
425
+ );
426
+ }
427
+
428
+ useEffect(() => {
429
+ if (!order?.id || messages?.loading) return
430
+ readMessages && readMessages()
431
+ }, [order?.id, messages?.loading])
432
+
433
+ return (
434
+ <View style={{ height: getViewHeight(), width: '100%', paddingTop: 12, backgroundColor: 'white' }}>
435
+ <Wrapper>
436
+ {!isMeesageListing ? (
437
+ <Header>
438
+ <TouchableOpacity onPress={onClose} style={{ paddingStart: 10, borderColor: theme.colors.clear }}>
439
+ <AntDesignIcon name='arrowleft' size={26} />
440
+ </TouchableOpacity>
441
+ <View style={{ marginRight: 10, shadowColor: theme.colors.black, shadowOpacity: 0.1, shadowOffset: { width: 0, height: 1 }, shadowRadius: 2 }}>
442
+ <OIcon
443
+ url={type === USER_TYPE.DRIVER ? order?.driver?.photo : order?.business?.logo}
444
+ width={32}
445
+ height={32}
446
+ style={{ borderRadius: 7.6 }}
447
+ />
448
+ </View>
449
+ <TitleHeader>
450
+ <OText size={14} lineHeight={21} weight={'600'}>{type === USER_TYPE.DRIVER ? order?.driver?.name : order?.business?.name}</OText>
451
+ <OText size={12} color={theme.colors.textSecondary}>{type === USER_TYPE.DRIVER ? t('DRIVER', 'Driver') : t('BUSINESS', 'Business')}</OText>
452
+ </TitleHeader>
453
+ </Header>
454
+ ) : (
455
+ <ProfileMessageHeader>
456
+ <View style={{ ...styles.headerTitle }}>
457
+ <TouchableOpacity onPress={onClose} style={styles.headerItem}>
458
+ <AntDesignIcon name='arrowleft' size={26} />
459
+ </TouchableOpacity>
460
+ <OText size={18}>{t('ORDER', theme?.defaultLanguages?.ORDER || 'Order')} #{order?.id}</OText>
461
+ </View>
462
+ <View style={{ ...styles.typeWraper }}>
463
+ {order.business && (
464
+ <TouchableOpacity
465
+ onPress={() => onMessages({ business: true, driver: false })}
466
+ >
467
+ <MessageTypeItem
468
+ active={business}
469
+ >
470
+ <OIcon
471
+ url={order?.business?.logo || theme.images.dummies.businessLogo}
472
+ width={32}
473
+ height={32}
474
+ style={{ borderRadius: 32 }}
475
+ />
476
+ </MessageTypeItem>
477
+ </TouchableOpacity>
478
+ )}
479
+
480
+ {order?.driver && (
481
+ <TouchableOpacity
482
+ onPress={() => onMessages({ business: false, driver: true })}
483
+ >
484
+ <MessageTypeItem
485
+ active={driver}
486
+ >
487
+ <OIcon
488
+ url={order?.driver?.photo}
489
+ width={32}
490
+ height={32}
491
+ style={{ borderRadius: 32 }}
492
+ />
493
+ </MessageTypeItem>
494
+ </TouchableOpacity>
495
+ )}
496
+ </View>
497
+ </ProfileMessageHeader>
498
+ )}
499
+ <GiftedChat
500
+ messages={formattedMessages}
501
+ user={{
502
+ _id: user.id,
503
+ name: user.name,
504
+ avatar: user.photo
505
+ }}
506
+ onSend={onSubmit}
507
+ onInputTextChanged={onChangeMessage}
508
+ alignTop
509
+ onLongPress={(context: any, message: any) => onLongPress(context, message)}
510
+ scrollToBottom
511
+ renderAvatarOnTop
512
+ renderUsernameOnMessage
513
+ renderAvatar={() => null}
514
+ renderInputToolbar={renderInputToolbar}
515
+ renderComposer={renderComposer}
516
+ renderSend={renderSend}
517
+ renderBubble={renderBubble}
518
+ renderMessageImage={renderMessageImage}
519
+ scrollToBottomComponent={() => renderScrollToBottomComponent()}
520
+ messagesContainerStyle={{
521
+ paddingTop: 18,
522
+ paddingHorizontal: 28,
523
+ paddingBottom: 55
524
+ }}
525
+ isLoadingEarlier={messages.loading}
526
+ renderLoading={() => <ActivityIndicator size="small" color="#000" />}
527
+ keyboardShouldPersistTaps='handled'
528
+ />
529
+ </Wrapper>
530
+ </View>
531
+ )
532
532
  }
533
533
 
534
534
  const styles = StyleSheet.create({
535
- containerActions: {
536
- width: 44,
537
- height: 44,
538
- alignItems: 'center',
539
- justifyContent: 'center',
540
- marginHorizontal: 4,
541
- marginBottom: 0,
542
- },
543
- containerSend: {
544
- width: 64,
545
- height: 44,
546
- alignItems: 'center',
547
- justifyContent: 'center',
548
- marginHorizontal: 4
549
- },
550
- editButton: {
551
- borderRadius: 50,
552
- backgroundColor: '#E9ECEF',
553
- marginRight: 10,
554
- height: 24,
555
- borderWidth: 1,
556
- paddingLeft: 0,
557
- paddingRight: 0
558
- },
559
- headerTitle: {
560
- flexDirection: 'row',
561
- alignItems: 'center'
562
- },
563
- headerItem: {
564
- overflow: 'hidden',
565
- width: 35,
566
- marginVertical: 18,
567
- },
568
- typeWraper: {
569
- flexDirection: 'row',
570
- height: 45,
571
- alignItems: 'center'
572
- }
535
+ containerActions: {
536
+ width: 44,
537
+ height: 44,
538
+ alignItems: 'center',
539
+ justifyContent: 'center',
540
+ marginHorizontal: 4,
541
+ marginBottom: 0,
542
+ },
543
+ containerSend: {
544
+ width: 64,
545
+ height: 44,
546
+ alignItems: 'center',
547
+ justifyContent: 'center',
548
+ marginHorizontal: 4
549
+ },
550
+ editButton: {
551
+ borderRadius: 50,
552
+ backgroundColor: '#E9ECEF',
553
+ marginRight: 10,
554
+ height: 24,
555
+ borderWidth: 1,
556
+ paddingLeft: 0,
557
+ paddingRight: 0
558
+ },
559
+ headerTitle: {
560
+ flexDirection: 'row',
561
+ alignItems: 'center'
562
+ },
563
+ headerItem: {
564
+ overflow: 'hidden',
565
+ width: 35,
566
+ marginVertical: 18,
567
+ },
568
+ typeWraper: {
569
+ flexDirection: 'row',
570
+ height: 45,
571
+ alignItems: 'center'
572
+ }
573
573
 
574
574
  })
575
575
 
576
576
  export const Messages = (props: MessagesParams) => {
577
- const [allMessages, setAllMessages] = useState(props.messages)
578
-
579
- useEffect(() => {
580
- setAllMessages(props.messages)
581
- }, [props.messages])
582
-
583
- const MessagesProps = {
584
- ...props,
585
- UIComponent: MessagesUI,
586
- messages: allMessages,
587
- setMessages: (values: any) => {
588
- props.setMessages && props.setMessages(values)
589
- setAllMessages(values)
590
- }
591
- }
592
- return <MessagesController {...MessagesProps} />
577
+ const [allMessages, setAllMessages] = useState(props.messages)
578
+
579
+ useEffect(() => {
580
+ setAllMessages(props.messages)
581
+ }, [props.messages])
582
+
583
+ const MessagesProps = {
584
+ ...props,
585
+ UIComponent: MessagesUI,
586
+ messages: allMessages,
587
+ setMessages: (values: any) => {
588
+ props.setMessages && props.setMessages(values)
589
+ setAllMessages(values)
590
+ }
591
+ }
592
+ return <MessagesController {...MessagesProps} />
593
593
  }