@messenger-box/platform-mobile 10.0.3-alpha.135 → 10.0.3-alpha.138

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@messenger-box/platform-mobile",
3
- "version": "10.0.3-alpha.135",
3
+ "version": "10.0.3-alpha.138",
4
4
  "description": "Sample core for higher packages to depend on",
5
5
  "license": "ISC",
6
6
  "author": "CDMBase LLC",
@@ -22,8 +22,8 @@
22
22
  "watch-ts": "tsc --watch"
23
23
  },
24
24
  "dependencies": {
25
- "@messenger-box/core": "10.0.3-alpha.135",
26
- "@messenger-box/platform-client": "10.0.3-alpha.135",
25
+ "@messenger-box/core": "10.0.3-alpha.138",
26
+ "@messenger-box/platform-client": "10.0.3-alpha.138",
27
27
  "base-64": "1.0.0",
28
28
  "react-native-gifted-chat": "1.0.4"
29
29
  },
@@ -43,5 +43,5 @@
43
43
  "typescript": {
44
44
  "definition": "lib/index.d.ts"
45
45
  },
46
- "gitHead": "da12d4859c8b3ddb08127de6e79ee69fcc2bcdf7"
46
+ "gitHead": "bc7104dc64d85487b754a56aff32fcb4cdc1afdf"
47
47
  }
@@ -3,7 +3,7 @@ import { Box } from '@admin-layout/gluestack-ui-mobile';
3
3
  import { ConversationView } from './containers/ConversationView';
4
4
  import { navigationRef } from '@common-stack/client-react';
5
5
 
6
- export function DialogMessages({ channelId, role, isShowThreadMessage = true, ...rest }) {
6
+ export function DialogMessages({ channelId, role, isShowThreadMessage = true, isPaymentChatUI = false, ...rest }) {
7
7
  const currentRoute = navigationRef.isReady() ? navigationRef?.getCurrentRoute() : null;
8
8
  return (
9
9
  // <Box bg={'white'} flex={1} pt={5}>
@@ -13,6 +13,7 @@ export function DialogMessages({ channelId, role, isShowThreadMessage = true, ..
13
13
  channelId={currentRoute?.params?.channelId ?? channelId}
14
14
  role={role}
15
15
  isShowThreadMessage={isShowThreadMessage}
16
+ isPaymentChatUI={isPaymentChatUI}
16
17
  />
17
18
  </Box>
18
19
  );
@@ -0,0 +1,199 @@
1
+ import React from 'react';
2
+ import { View, StyleSheet, TouchableOpacity } from 'react-native';
3
+ import { Day, utils } from 'react-native-gifted-chat';
4
+ import { Avatar, AvatarFallbackText, AvatarImage, Text } from '@admin-layout/gluestack-ui-mobile';
5
+ import { MaterialCommunityIcons } from '@expo/vector-icons';
6
+
7
+ const { isSameUser, isSameDay } = utils;
8
+
9
+ export default class PaymentMessage extends React.Component<any> {
10
+ static defaultProps = {
11
+ renderAvatar: undefined,
12
+ renderBubble: null,
13
+ renderDay: null,
14
+ currentMessage: {},
15
+ nextMessage: {},
16
+ previousMessage: {},
17
+ user: {},
18
+ containerStyle: {},
19
+ isShowImageViewer: false,
20
+ setImageViewer: (obj: any, v: boolean) => null,
21
+ };
22
+
23
+ getInnerComponentProps() {
24
+ const { containerStyle, ...props } = this.props;
25
+ return {
26
+ ...props,
27
+ position: 'left',
28
+ isSameUser,
29
+ isSameDay,
30
+ };
31
+ }
32
+
33
+ renderDay() {
34
+ if (this.props.currentMessage.createdAt) {
35
+ const dayProps = this.getInnerComponentProps();
36
+ if (this.props.renderDay) {
37
+ return this.props.renderDay(dayProps);
38
+ }
39
+ return <Day {...dayProps} />;
40
+ }
41
+ return null;
42
+ }
43
+
44
+ renderAvatar(isRight: boolean) {
45
+ let extraStyle: any;
46
+ let isSameUserAndSameDay: boolean = false;
47
+ if (
48
+ isSameUser(this.props.currentMessage, this.props.previousMessage) &&
49
+ isSameDay(this.props.currentMessage, this.props.previousMessage)
50
+ ) {
51
+ extraStyle = { height: 0 };
52
+ isSameUserAndSameDay = true;
53
+ }
54
+ const avatarProps: any = this.getInnerComponentProps();
55
+ return (
56
+ <Avatar
57
+ size={'sm'}
58
+ className="bg-transparent rounded-none"
59
+ style={[isRight ? { marginLeft: 8 } : { marginRight: 8 }]}
60
+ >
61
+ <AvatarFallbackText>
62
+ {isSameUserAndSameDay ? '' : avatarProps?.currentMessage?.user?.name?.[0]}
63
+ </AvatarFallbackText>
64
+ {!isSameUserAndSameDay && avatarProps?.currentMessage?.user?.avatar && (
65
+ <AvatarImage
66
+ alt="image"
67
+ style={{ height: 40, width: 40, borderRadius: 3, marginTop: 1 }}
68
+ source={{
69
+ uri: avatarProps?.currentMessage?.user?.avatar,
70
+ }}
71
+ />
72
+ )}
73
+ </Avatar>
74
+ );
75
+ }
76
+
77
+ renderPaymentBubble(isRight: boolean) {
78
+ const { currentMessage } = this.props;
79
+ // Extract payment details
80
+ const paymentDetails = currentMessage?.propsConfiguration?.payment || {};
81
+ const amount = paymentDetails.amount || currentMessage?.amount;
82
+ const currency = paymentDetails.currency || currentMessage?.currency || '₹';
83
+ const status = paymentDetails.status || currentMessage?.status || 'Sent Securely';
84
+ const paymentNote = paymentDetails.note || currentMessage?.text;
85
+ const time = currentMessage?.createdAt
86
+ ? new Date(currentMessage.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
87
+ : '';
88
+
89
+ return (
90
+ <TouchableOpacity
91
+ activeOpacity={0.85}
92
+ style={[
93
+ styles.bubble,
94
+ isRight ? styles.bubbleRight : styles.bubbleLeft,
95
+ { alignSelf: isRight ? 'flex-end' : 'flex-start' },
96
+ ]}
97
+ >
98
+ <Text style={styles.amount}>
99
+ {currency}
100
+ {amount}
101
+ </Text>
102
+ <View style={styles.secureRow}>
103
+ <MaterialCommunityIcons
104
+ name="check-circle-outline"
105
+ size={20}
106
+ color="#4caf50"
107
+ style={{ marginRight: 4 }}
108
+ />
109
+ <Text style={styles.secureText}>{status}</Text>
110
+ <MaterialCommunityIcons name="chevron-right" size={22} color="#7c3aed" style={{ marginLeft: 4 }} />
111
+ </View>
112
+ {paymentNote && paymentNote !== status && <Text style={styles.note}>{paymentNote}</Text>}
113
+ <Text style={[styles.time, { textAlign: isRight ? 'right' : 'left' }]}>{time}</Text>
114
+ </TouchableOpacity>
115
+ );
116
+ }
117
+
118
+ render() {
119
+ const { currentMessage, user } = this.props;
120
+ const isRight = currentMessage?.user?._id === user?._id;
121
+ const marginBottom = isSameUser(currentMessage, this.props.nextMessage) ? 2 : 10;
122
+ return (
123
+ <View>
124
+ {this.renderDay()}
125
+ <View
126
+ style={[styles.container, { marginBottom }, isRight ? styles.containerRight : styles.containerLeft]}
127
+ >
128
+ {isRight ? null : this.renderAvatar(false)}
129
+ {this.renderPaymentBubble(isRight)}
130
+ {isRight ? this.renderAvatar(true) : null}
131
+ </View>
132
+ </View>
133
+ );
134
+ }
135
+ }
136
+
137
+ const styles = StyleSheet.create({
138
+ container: {
139
+ flexDirection: 'row',
140
+ alignItems: 'flex-end',
141
+ width: '100%',
142
+ },
143
+ containerLeft: {
144
+ justifyContent: 'flex-start',
145
+ },
146
+ containerRight: {
147
+ flexDirection: 'row-reverse',
148
+ justifyContent: 'flex-end',
149
+ },
150
+ bubble: {
151
+ backgroundColor: '#f3e8ff',
152
+ borderRadius: 16,
153
+ paddingVertical: 16,
154
+ paddingHorizontal: 20,
155
+ minWidth: 160,
156
+ maxWidth: 320,
157
+ shadowColor: '#000',
158
+ shadowOffset: { width: 0, height: 1 },
159
+ shadowOpacity: 0.08,
160
+ shadowRadius: 4,
161
+ elevation: 2,
162
+ marginBottom: 2,
163
+ },
164
+ bubbleLeft: {
165
+ marginLeft: 0,
166
+ marginRight: 32,
167
+ },
168
+ bubbleRight: {
169
+ marginRight: 0,
170
+ marginLeft: 32,
171
+ },
172
+ amount: {
173
+ fontSize: 26,
174
+ fontWeight: 'bold',
175
+ color: '#18181b',
176
+ marginBottom: 8,
177
+ },
178
+ secureRow: {
179
+ flexDirection: 'row',
180
+ alignItems: 'center',
181
+ marginBottom: 4,
182
+ },
183
+ secureText: {
184
+ fontSize: 16,
185
+ color: '#18181b',
186
+ fontWeight: '500',
187
+ },
188
+ note: {
189
+ fontSize: 14,
190
+ color: '#6b7280',
191
+ marginTop: 2,
192
+ marginBottom: 2,
193
+ },
194
+ time: {
195
+ fontSize: 12,
196
+ color: '#a1a1aa',
197
+ marginTop: 6,
198
+ },
199
+ });
@@ -40,7 +40,16 @@ import { orderBy, startCase, uniqBy } from 'lodash-es';
40
40
  import * as ImagePicker from 'expo-image-picker';
41
41
  import { encode as atob } from 'base-64';
42
42
  import { Ionicons, MaterialCommunityIcons, MaterialIcons } from '@expo/vector-icons';
43
- import { Actions, GiftedChat, IMessage, MessageText, Send, Composer, InputToolbar } from 'react-native-gifted-chat';
43
+ import {
44
+ Actions,
45
+ GiftedChat,
46
+ IMessage,
47
+ MessageText,
48
+ Send,
49
+ Composer,
50
+ InputToolbar,
51
+ Message as GiftedMessage,
52
+ } from 'react-native-gifted-chat';
44
53
  import { PreDefinedRole, RoomType, IExpoNotificationData, IFileInfo, FileRefType, PostTypeEnum } from 'common';
45
54
  import {
46
55
  CHAT_MESSAGE_ADDED,
@@ -67,6 +76,7 @@ import Reanimated, { useSharedValue, useAnimatedStyle } from 'react-native-reani
67
76
  import Constants from 'expo-constants';
68
77
  import { Keyboard } from 'react-native';
69
78
  import GiftedChatInboxComponent from '../components/GiftedChatInboxComponent';
79
+ import PaymentMessage from '../components/SlackMessageContainer/PaymentMessage';
70
80
 
71
81
  // Define an extended interface for ImagePickerAsset with url property
72
82
  interface ExtendedImagePickerAsset extends ImagePicker.ImagePickerAsset {
@@ -205,7 +215,13 @@ function useGradualKeyboardAnimation() {
205
215
  return { height };
206
216
  }
207
217
 
208
- const ConversationViewComponent = ({ channelId: initialChannelId, role, isShowThreadMessage, ...rest }: any) => {
218
+ const ConversationViewComponent = ({
219
+ channelId: initialChannelId,
220
+ role,
221
+ isShowThreadMessage,
222
+ isPaymentChatUI,
223
+ ...rest
224
+ }: any) => {
209
225
  // Core state management using React hooks instead of XState
210
226
  const { params } = useRoute<any>();
211
227
  const [channelId, setChannelId] = useState<string | null>(initialChannelId || null);
@@ -1679,6 +1695,154 @@ const ConversationViewComponent = ({ channelId: initialChannelId, role, isShowTh
1679
1695
  [isShowImageViewer],
1680
1696
  );
1681
1697
 
1698
+ const renderPaymentMessageText = useCallback(
1699
+ (props: any) => {
1700
+ const { currentMessage } = props;
1701
+ const lastReply: any =
1702
+ currentMessage?.replies?.data?.length > 0 ? currentMessage?.replies?.data?.[0] : null;
1703
+
1704
+ // Do not render anything if the message text is empty or only whitespace
1705
+ if (!currentMessage?.text || currentMessage.text.trim() === '') {
1706
+ return null;
1707
+ }
1708
+
1709
+ // PAYMENT UI
1710
+ if (currentMessage.type === 'PAYMENT') {
1711
+ // Extract payment details if available
1712
+ const paymentDetails = currentMessage?.propsConfiguration?.payment || {};
1713
+ const amount = paymentDetails.amount || currentMessage?.amount;
1714
+ const currency = paymentDetails.currency || currentMessage?.currency || '$';
1715
+ const status = paymentDetails.status || currentMessage?.status || 'Completed';
1716
+ const paymentNote = paymentDetails.note || currentMessage?.text;
1717
+
1718
+ return (
1719
+ <TouchableHighlight
1720
+ underlayColor={'#e0f7fa'}
1721
+ style={{ width: '100%' }}
1722
+ onPress={() => {
1723
+ if (currentMessage?.isShowThreadMessage)
1724
+ navigation.navigate(config.THREAD_MESSEGE_PATH, {
1725
+ channelId: channelId,
1726
+ title: 'Message',
1727
+ postParentId: currentMessage?._id,
1728
+ isPostParentIdThread: true,
1729
+ });
1730
+ }}
1731
+ >
1732
+ <Box
1733
+ style={{
1734
+ backgroundColor: '#e0f7fa',
1735
+ borderRadius: 16,
1736
+ padding: 12,
1737
+ marginVertical: 2,
1738
+ borderWidth: 1,
1739
+ borderColor: '#26c6da',
1740
+ flexDirection: 'row',
1741
+ alignItems: 'center',
1742
+ }}
1743
+ >
1744
+ <MaterialCommunityIcons
1745
+ name="credit-card-check"
1746
+ size={28}
1747
+ color="#00838f"
1748
+ style={{ marginRight: 10 }}
1749
+ />
1750
+ <VStack style={{ flex: 1 }}>
1751
+ <Text style={{ fontWeight: 'bold', color: '#00838f', fontSize: 16 }}>
1752
+ {amount ? `${currency}${amount}` : 'Payment'}
1753
+ </Text>
1754
+ <Text style={{ color: '#00838f', fontSize: 13, marginTop: 2 }}>{status}</Text>
1755
+ {paymentNote && (
1756
+ <Text style={{ color: '#444', fontSize: 13, marginTop: 4 }}>{paymentNote}</Text>
1757
+ )}
1758
+ </VStack>
1759
+ </Box>
1760
+ </TouchableHighlight>
1761
+ );
1762
+ } else {
1763
+ // Default UI (unchanged)
1764
+ return (
1765
+ <TouchableHighlight
1766
+ underlayColor={'#c0c0c0'}
1767
+ style={{ width: '100%' }}
1768
+ onPress={() => {
1769
+ if (currentMessage?.isShowThreadMessage)
1770
+ navigation.navigate(config.THREAD_MESSEGE_PATH, {
1771
+ channelId: channelId,
1772
+ title: 'Message',
1773
+ postParentId: currentMessage?._id,
1774
+ isPostParentIdThread: true,
1775
+ });
1776
+ }}
1777
+ >
1778
+ <>
1779
+ <MessageText {...props} textStyle={{ left: { marginLeft: 5 } }} />
1780
+ {currentMessage?.replies?.data?.length > 0 && (
1781
+ <HStack space={'sm'} className="px-1 items-center">
1782
+ <HStack>
1783
+ {currentMessage?.replies?.data
1784
+ ?.filter(
1785
+ (v: any, i: any, a: any) =>
1786
+ a.findIndex((t: any) => t?.author?.id === v?.author?.id) === i,
1787
+ )
1788
+ ?.slice(0, 2)
1789
+ ?.reverse()
1790
+ ?.map((p: any, i: Number) => (
1791
+ <Avatar
1792
+ key={'conversation-replies-key-' + i}
1793
+ className="bg-transparent"
1794
+ size={'sm'}
1795
+ >
1796
+ <AvatarFallbackText>
1797
+ {startCase(p?.author?.username?.charAt(0))}
1798
+ </AvatarFallbackText>
1799
+ <AvatarImage
1800
+ alt="user image"
1801
+ style={{ borderRadius: 6, borderWidth: 2, borderColor: '#fff' }}
1802
+ source={{
1803
+ uri: p?.author?.picture,
1804
+ }}
1805
+ />
1806
+ </Avatar>
1807
+ ))}
1808
+ </HStack>
1809
+ <Text style={{ fontSize: 12 }} className="font-bold color-blue-800">
1810
+ {currentMessage?.replies?.totalCount}{' '}
1811
+ {currentMessage?.replies?.totalCount == 1 ? 'reply' : 'replies'}
1812
+ </Text>
1813
+ <Text style={{ fontSize: 12 }} className="font-bold color-gray-500">
1814
+ {lastReply ? createdAtText(lastReply?.createdAt) : ''}
1815
+ </Text>
1816
+ </HStack>
1817
+ )}
1818
+ </>
1819
+ </TouchableHighlight>
1820
+ );
1821
+ }
1822
+ },
1823
+ [navigation, channelId, role],
1824
+ );
1825
+
1826
+ const renderPaymentMessage = useCallback(
1827
+ (props: any) => {
1828
+ const { currentMessage } = props;
1829
+ if (currentMessage?.type === 'PAYMENT') {
1830
+ return <PaymentMessage {...props} navigation={navigation} channelId={channelId} config={config} />;
1831
+ }
1832
+ // return <PaymentMessage
1833
+ // {...props}
1834
+ // navigation={navigation}
1835
+ // channelId={channelId}
1836
+ // config={config}
1837
+ // />;
1838
+ // Fallback to SlackMessage for other message types
1839
+ return (
1840
+ <SlackMessage {...props} isShowImageViewer={isShowImageViewer} setImageViewer={setImageViewerObject} />
1841
+ );
1842
+ },
1843
+ [isShowImageViewer, channelId, navigation],
1844
+ );
1845
+
1682
1846
  let onScroll = false;
1683
1847
 
1684
1848
  // Optimize onMomentumScrollBegin for better scroll performance
@@ -1960,8 +2124,8 @@ const ConversationViewComponent = ({ channelId: initialChannelId, role, isShowTh
1960
2124
  _id: auth?.id || '',
1961
2125
  }}
1962
2126
  renderSend={renderSend}
1963
- renderMessageText={renderMessageText}
1964
- renderMessage={renderMessage}
2127
+ renderMessageText={isPaymentChatUI ? renderPaymentMessageText : renderMessageText}
2128
+ renderMessage={isPaymentChatUI ? renderPaymentMessage : renderMessage}
1965
2129
  renderChatFooter={renderChatFooter}
1966
2130
  renderLoadEarlier={renderLoadEarlier}
1967
2131
  loadEarlier={totalCount > channelMessages.length}
@@ -2049,6 +2213,7 @@ export const ConversationView = React.memo(ConversationViewComponent, (prevProps
2049
2213
  return (
2050
2214
  prevProps.channelId === nextProps.channelId &&
2051
2215
  prevProps.role === nextProps.role &&
2052
- prevProps.isShowThreadMessage === nextProps.isShowThreadMessage
2216
+ prevProps.isShowThreadMessage === nextProps.isShowThreadMessage &&
2217
+ prevProps.isPaymentChatUI === nextProps.isPaymentChatUI
2053
2218
  );
2054
2219
  });