@developer_tribe/react-native-comnyx 0.3.2 → 0.3.4

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 (179) hide show
  1. package/lib/commonjs/App.js +8 -3
  2. package/lib/commonjs/App.js.map +1 -1
  3. package/lib/commonjs/assets/arrow-right.png +0 -0
  4. package/lib/commonjs/assets/headphones-01.png +0 -0
  5. package/lib/commonjs/assets/iconamoon_clock-fill.png +0 -0
  6. package/lib/commonjs/assets/info-circle.png +0 -0
  7. package/lib/commonjs/assets/message-notification-square.png +0 -0
  8. package/lib/commonjs/assets/x-close.png +0 -0
  9. package/lib/commonjs/components/ChatList.js +356 -50
  10. package/lib/commonjs/components/ChatList.js.map +1 -1
  11. package/lib/commonjs/components/CustomAlert.js +132 -0
  12. package/lib/commonjs/components/CustomAlert.js.map +1 -0
  13. package/lib/commonjs/components/CustomerForm.js +252 -198
  14. package/lib/commonjs/components/CustomerForm.js.map +1 -1
  15. package/lib/commonjs/components/EmptyList.js +36 -11
  16. package/lib/commonjs/components/EmptyList.js.map +1 -1
  17. package/lib/commonjs/components/InitFailed.js +8 -5
  18. package/lib/commonjs/components/InitFailed.js.map +1 -1
  19. package/lib/commonjs/components/MessageInput.js +49 -16
  20. package/lib/commonjs/components/MessageInput.js.map +1 -1
  21. package/lib/commonjs/components/MessageItem.js +133 -42
  22. package/lib/commonjs/components/MessageItem.js.map +1 -1
  23. package/lib/commonjs/components/ScaledSheet.js +67 -0
  24. package/lib/commonjs/components/ScaledSheet.js.map +1 -0
  25. package/lib/commonjs/components/SizeMatter.js +27 -0
  26. package/lib/commonjs/components/SizeMatter.js.map +1 -0
  27. package/lib/commonjs/index.js +7 -0
  28. package/lib/commonjs/index.js.map +1 -1
  29. package/lib/commonjs/store.js +15 -9
  30. package/lib/commonjs/store.js.map +1 -1
  31. package/lib/commonjs/types/Theme.js +20 -2
  32. package/lib/commonjs/types/Theme.js.map +1 -1
  33. package/lib/commonjs/utils/deepMap.js +38 -0
  34. package/lib/commonjs/utils/deepMap.js.map +1 -0
  35. package/lib/commonjs/utils/formatDate.js +20 -0
  36. package/lib/commonjs/utils/formatDate.js.map +1 -0
  37. package/lib/commonjs/utils/scalingUtils.js +33 -0
  38. package/lib/commonjs/utils/scalingUtils.js.map +1 -0
  39. package/lib/commonjs/viewabilityConfig.js +11 -0
  40. package/lib/commonjs/viewabilityConfig.js.map +1 -0
  41. package/lib/module/App.js +8 -3
  42. package/lib/module/App.js.map +1 -1
  43. package/lib/module/assets/arrow-right.png +0 -0
  44. package/lib/module/assets/headphones-01.png +0 -0
  45. package/lib/module/assets/iconamoon_clock-fill.png +0 -0
  46. package/lib/module/assets/info-circle.png +0 -0
  47. package/lib/module/assets/message-notification-square.png +0 -0
  48. package/lib/module/assets/x-close.png +0 -0
  49. package/lib/module/components/ChatList.js +358 -54
  50. package/lib/module/components/ChatList.js.map +1 -1
  51. package/lib/module/components/CustomAlert.js +127 -0
  52. package/lib/module/components/CustomAlert.js.map +1 -0
  53. package/lib/module/components/CustomerForm.js +253 -200
  54. package/lib/module/components/CustomerForm.js.map +1 -1
  55. package/lib/module/components/EmptyList.js +38 -13
  56. package/lib/module/components/EmptyList.js.map +1 -1
  57. package/lib/module/components/InitFailed.js +9 -6
  58. package/lib/module/components/InitFailed.js.map +1 -1
  59. package/lib/module/components/MessageInput.js +50 -17
  60. package/lib/module/components/MessageInput.js.map +1 -1
  61. package/lib/module/components/MessageItem.js +134 -44
  62. package/lib/module/components/MessageItem.js.map +1 -1
  63. package/lib/module/components/ScaledSheet.js +62 -0
  64. package/lib/module/components/ScaledSheet.js.map +1 -0
  65. package/lib/module/components/SizeMatter.js +23 -0
  66. package/lib/module/components/SizeMatter.js.map +1 -0
  67. package/lib/module/index.js +1 -0
  68. package/lib/module/index.js.map +1 -1
  69. package/lib/module/store.js +15 -9
  70. package/lib/module/store.js.map +1 -1
  71. package/lib/module/types/Theme.js +20 -2
  72. package/lib/module/types/Theme.js.map +1 -1
  73. package/lib/module/utils/deepMap.js +34 -0
  74. package/lib/module/utils/deepMap.js.map +1 -0
  75. package/lib/module/utils/formatDate.js +15 -0
  76. package/lib/module/utils/formatDate.js.map +1 -0
  77. package/lib/module/utils/scalingUtils.js +25 -0
  78. package/lib/module/utils/scalingUtils.js.map +1 -0
  79. package/lib/module/viewabilityConfig.js +7 -0
  80. package/lib/module/viewabilityConfig.js.map +1 -0
  81. package/lib/typescript/commonjs/src/App.d.ts +2 -1
  82. package/lib/typescript/commonjs/src/App.d.ts.map +1 -1
  83. package/lib/typescript/commonjs/src/components/ChatList.d.ts +3 -1
  84. package/lib/typescript/commonjs/src/components/ChatList.d.ts.map +1 -1
  85. package/lib/typescript/commonjs/src/components/CustomAlert.d.ts +15 -0
  86. package/lib/typescript/commonjs/src/components/CustomAlert.d.ts.map +1 -0
  87. package/lib/typescript/commonjs/src/components/CustomerForm.d.ts +3 -1
  88. package/lib/typescript/commonjs/src/components/CustomerForm.d.ts.map +1 -1
  89. package/lib/typescript/commonjs/src/components/EmptyList.d.ts.map +1 -1
  90. package/lib/typescript/commonjs/src/components/InitFailed.d.ts.map +1 -1
  91. package/lib/typescript/commonjs/src/components/MessageInput.d.ts +1 -0
  92. package/lib/typescript/commonjs/src/components/MessageInput.d.ts.map +1 -1
  93. package/lib/typescript/commonjs/src/components/MessageItem.d.ts +2 -1
  94. package/lib/typescript/commonjs/src/components/MessageItem.d.ts.map +1 -1
  95. package/lib/typescript/commonjs/src/components/ScaledSheet.d.ts +2 -0
  96. package/lib/typescript/commonjs/src/components/ScaledSheet.d.ts.map +1 -0
  97. package/lib/typescript/commonjs/src/components/SizeMatter.d.ts +7 -0
  98. package/lib/typescript/commonjs/src/components/SizeMatter.d.ts.map +1 -0
  99. package/lib/typescript/commonjs/src/index.d.ts +1 -0
  100. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  101. package/lib/typescript/commonjs/src/store.d.ts +6 -0
  102. package/lib/typescript/commonjs/src/store.d.ts.map +1 -1
  103. package/lib/typescript/commonjs/src/types/Conversation.d.ts +2 -0
  104. package/lib/typescript/commonjs/src/types/Conversation.d.ts.map +1 -1
  105. package/lib/typescript/commonjs/src/types/Theme.d.ts +9 -0
  106. package/lib/typescript/commonjs/src/types/Theme.d.ts.map +1 -1
  107. package/lib/typescript/commonjs/src/utils/deepMap.d.ts +7 -0
  108. package/lib/typescript/commonjs/src/utils/deepMap.d.ts.map +1 -0
  109. package/lib/typescript/commonjs/src/utils/formatDate.d.ts +2 -0
  110. package/lib/typescript/commonjs/src/utils/formatDate.d.ts.map +1 -0
  111. package/lib/typescript/commonjs/src/utils/scalingUtils.d.ts +10 -0
  112. package/lib/typescript/commonjs/src/utils/scalingUtils.d.ts.map +1 -0
  113. package/lib/typescript/commonjs/src/viewabilityConfig.d.ts +5 -0
  114. package/lib/typescript/commonjs/src/viewabilityConfig.d.ts.map +1 -0
  115. package/lib/typescript/module/src/App.d.ts +2 -1
  116. package/lib/typescript/module/src/App.d.ts.map +1 -1
  117. package/lib/typescript/module/src/components/ChatList.d.ts +3 -1
  118. package/lib/typescript/module/src/components/ChatList.d.ts.map +1 -1
  119. package/lib/typescript/module/src/components/CustomAlert.d.ts +15 -0
  120. package/lib/typescript/module/src/components/CustomAlert.d.ts.map +1 -0
  121. package/lib/typescript/module/src/components/CustomerForm.d.ts +3 -1
  122. package/lib/typescript/module/src/components/CustomerForm.d.ts.map +1 -1
  123. package/lib/typescript/module/src/components/EmptyList.d.ts.map +1 -1
  124. package/lib/typescript/module/src/components/InitFailed.d.ts.map +1 -1
  125. package/lib/typescript/module/src/components/MessageInput.d.ts +1 -0
  126. package/lib/typescript/module/src/components/MessageInput.d.ts.map +1 -1
  127. package/lib/typescript/module/src/components/MessageItem.d.ts +2 -1
  128. package/lib/typescript/module/src/components/MessageItem.d.ts.map +1 -1
  129. package/lib/typescript/module/src/components/ScaledSheet.d.ts +2 -0
  130. package/lib/typescript/module/src/components/ScaledSheet.d.ts.map +1 -0
  131. package/lib/typescript/module/src/components/SizeMatter.d.ts +7 -0
  132. package/lib/typescript/module/src/components/SizeMatter.d.ts.map +1 -0
  133. package/lib/typescript/module/src/index.d.ts +1 -0
  134. package/lib/typescript/module/src/index.d.ts.map +1 -1
  135. package/lib/typescript/module/src/store.d.ts +6 -0
  136. package/lib/typescript/module/src/store.d.ts.map +1 -1
  137. package/lib/typescript/module/src/types/Conversation.d.ts +2 -0
  138. package/lib/typescript/module/src/types/Conversation.d.ts.map +1 -1
  139. package/lib/typescript/module/src/types/Theme.d.ts +9 -0
  140. package/lib/typescript/module/src/types/Theme.d.ts.map +1 -1
  141. package/lib/typescript/module/src/utils/deepMap.d.ts +7 -0
  142. package/lib/typescript/module/src/utils/deepMap.d.ts.map +1 -0
  143. package/lib/typescript/module/src/utils/formatDate.d.ts +2 -0
  144. package/lib/typescript/module/src/utils/formatDate.d.ts.map +1 -0
  145. package/lib/typescript/module/src/utils/scalingUtils.d.ts +10 -0
  146. package/lib/typescript/module/src/utils/scalingUtils.d.ts.map +1 -0
  147. package/lib/typescript/module/src/viewabilityConfig.d.ts +5 -0
  148. package/lib/typescript/module/src/viewabilityConfig.d.ts.map +1 -0
  149. package/package.json +1 -1
  150. package/src/App.tsx +4 -2
  151. package/src/assets/arrow-right.png +0 -0
  152. package/src/assets/headphones-01.png +0 -0
  153. package/src/assets/iconamoon_clock-fill.png +0 -0
  154. package/src/assets/info-circle.png +0 -0
  155. package/src/assets/message-notification-square.png +0 -0
  156. package/src/assets/x-close.png +0 -0
  157. package/src/components/ChatList.tsx +420 -60
  158. package/src/components/CustomAlert.tsx +132 -0
  159. package/src/components/CustomerForm.tsx +272 -224
  160. package/src/components/EmptyList.tsx +31 -7
  161. package/src/components/InitFailed.tsx +9 -6
  162. package/src/components/MessageInput.tsx +86 -49
  163. package/src/components/MessageItem.tsx +181 -58
  164. package/src/components/ScaledSheet.ts +93 -0
  165. package/src/components/SizeMatter.tsx +22 -0
  166. package/src/index.tsx +1 -0
  167. package/src/store.ts +13 -3
  168. package/src/types/Conversation.ts +2 -0
  169. package/src/types/Theme.ts +27 -0
  170. package/src/utils/deepMap.ts +47 -0
  171. package/src/utils/formatDate.ts +13 -0
  172. package/src/utils/scalingUtils.ts +27 -0
  173. package/src/viewabilityConfig.ts +4 -0
  174. package/lib/commonjs/assets/double-check.png +0 -0
  175. package/lib/commonjs/assets/send.png +0 -0
  176. package/lib/module/assets/double-check.png +0 -0
  177. package/lib/module/assets/send.png +0 -0
  178. package/src/assets/double-check.png +0 -0
  179. package/src/assets/send.png +0 -0
@@ -1,21 +1,17 @@
1
- import {
2
- StyleSheet,
3
- TextInput,
4
- View,
5
- Image,
6
- TouchableOpacity,
7
- } from 'react-native';
1
+ import { TextInput, View, Image, TouchableOpacity } from 'react-native';
8
2
  import { useAppStore } from '../store';
9
3
  import { useState } from 'react';
10
4
  import { sendCustomerMessage } from '../api';
11
5
  import { useThemeColors } from '../hooks/useThemeColors';
6
+ import { ScaledSheet } from './ScaledSheet';
12
7
 
13
- const sendDark = require('../assets/send.png');
8
+ const sendDark = require('../assets/arrow-right.png');
14
9
 
15
10
  export function MessageInput({
16
11
  scrollToBottom,
17
12
  }: {
18
13
  scrollToBottom: (animated: boolean) => void;
14
+ selectedMessage?: string;
19
15
  }) {
20
16
  const [value, setValue] = useState('');
21
17
  const customer = useAppStore((s) => s.customer!);
@@ -39,34 +35,65 @@ export function MessageInput({
39
35
  ]);
40
36
  sendCustomerMessage(customer.external_id, value, {
41
37
  fake: useAppStore.getState().fake,
42
- }).then((res) => {
43
- const data = useAppStore.getState().data;
44
- if (data) {
45
- const itemIndex = data.findIndex(
46
- (item) =>
47
- item.id === null &&
48
- new Date(item.created_at).getTime() === date.getTime()
49
- );
50
- if (itemIndex === -1) {
51
- //TODO:??
38
+ })
39
+ .then((res) => {
40
+ const data = useAppStore.getState().data;
41
+ if (data) {
42
+ const itemIndex = data.findIndex(
43
+ (item) =>
44
+ item.id === null &&
45
+ new Date(item.created_at).getTime() === date.getTime()
46
+ );
47
+ if (itemIndex === -1) {
48
+ //TODO:??
49
+ } else {
50
+ const alteredData = [...data];
51
+ alteredData[itemIndex] = {
52
+ id: res.message.id,
53
+ content: res.message.content,
54
+ user: res.message.user ?? alteredData[itemIndex]?.user,
55
+ created_at: new Date(res.message.created_at),
56
+ approved: true,
57
+ };
58
+ useAppStore.setState({
59
+ data: alteredData,
60
+ });
61
+ scrollToBottom(false);
62
+ }
52
63
  } else {
53
- const alteredData = [...data];
54
- alteredData[itemIndex] = {
55
- id: res.message.id,
56
- content: res.message.content,
57
- user: res.message.user ?? alteredData[itemIndex]?.user,
58
- created_at: new Date(res.message.created_at),
59
- approved: true,
60
- };
61
- useAppStore.setState({
62
- data: alteredData,
63
- });
64
- scrollToBottom(false);
64
+ //TODO: ??
65
65
  }
66
- } else {
67
- //TODO: ??
68
- }
69
- });
66
+ })
67
+ .catch(() => {
68
+ const data = useAppStore.getState().data;
69
+ if (data) {
70
+ const itemIndex = data.findIndex(
71
+ (item) =>
72
+ item.id === null &&
73
+ new Date(item.created_at).getTime() === date.getTime()
74
+ );
75
+ if (itemIndex === -1) {
76
+ //TODO:??
77
+ } else {
78
+ const alteredData = [...data];
79
+ alteredData[itemIndex] = {
80
+ ...alteredData[itemIndex],
81
+ id: alteredData[itemIndex]?.id ?? null,
82
+ content: alteredData[itemIndex]?.content ?? '',
83
+ created_at: alteredData[itemIndex]?.created_at ?? new Date(),
84
+ approved: alteredData[itemIndex]?.approved ?? false,
85
+ user: alteredData[itemIndex]?.user ?? null,
86
+ error: true,
87
+ };
88
+ useAppStore.setState({
89
+ data: alteredData,
90
+ });
91
+ scrollToBottom(false);
92
+ }
93
+ } else {
94
+ //TODO: ??
95
+ }
96
+ });
70
97
  setValue('');
71
98
  }
72
99
  };
@@ -80,6 +107,8 @@ export function MessageInput({
80
107
  onChangeText={(newValue) => setValue(newValue)}
81
108
  onSubmitEditing={sendMessage}
82
109
  returnKeyType="send"
110
+ multiline={true}
111
+ maxLength={1000}
83
112
  style={[
84
113
  styles.textInput,
85
114
  {
@@ -92,11 +121,14 @@ export function MessageInput({
92
121
  placeholderTextColor={themeColors.text + '80'}
93
122
  />
94
123
  <TouchableOpacity
95
- style={[styles.sendButton, { backgroundColor: themeColors.primary }]}
124
+ style={[
125
+ styles.sendButton,
126
+ { backgroundColor: themeColors.dark_background },
127
+ ]}
96
128
  onPress={sendMessage}
97
129
  >
98
130
  <Image
99
- style={[styles.sendIcon, { tintColor: themeColors.text }]}
131
+ style={[styles.sendIcon, { tintColor: themeColors.light_text }]}
100
132
  source={sendDark}
101
133
  />
102
134
  </TouchableOpacity>
@@ -104,30 +136,35 @@ export function MessageInput({
104
136
  );
105
137
  }
106
138
 
107
- const styles = StyleSheet.create({
139
+ const styles = ScaledSheet.create({
108
140
  container: {
109
141
  flexDirection: 'row',
110
- padding: 10,
111
- borderTopWidth: 1,
142
+ paddingHorizontal: '10@s',
143
+ paddingVertical: '10@vs',
144
+ alignItems: 'center',
145
+ position: 'relative',
146
+ zIndex: 1,
112
147
  },
113
148
  textInput: {
114
149
  flex: 1,
115
150
  borderWidth: 1,
116
- borderRadius: 20,
117
- paddingHorizontal: 15,
118
- paddingVertical: 8,
119
- marginRight: 10,
120
- fontSize: 16,
151
+ borderRadius: '20@s',
152
+ paddingHorizontal: '15@s',
153
+ marginRight: '10@s',
154
+ fontSize: '16@vs',
155
+ paddingVertical: '10@vs',
156
+ minHeight: 40,
157
+ maxHeight: 190,
121
158
  },
122
159
  sendButton: {
123
- width: 40,
124
- height: 40,
125
- borderRadius: 20,
160
+ width: '40@s',
161
+ height: '40@vs',
162
+ borderRadius: '20@vs',
126
163
  justifyContent: 'center',
127
164
  alignItems: 'center',
128
165
  },
129
166
  sendIcon: {
130
- width: 20,
131
- height: 20,
167
+ width: '20@vs',
168
+ height: '20@vs',
132
169
  },
133
170
  });
@@ -1,13 +1,34 @@
1
- import { View, Image } from 'react-native';
1
+ import { View, Image, TouchableOpacity } from 'react-native';
2
2
  import { AppText } from './AppText';
3
3
  import type { AppConversationMessage } from '../types/Conversation';
4
- import { StyleSheet } from 'react-native';
5
4
  import { useThemeColors } from '../hooks/useThemeColors';
5
+ import { useState, useRef, useEffect } from 'react';
6
+ import { ScaledSheet } from './ScaledSheet';
6
7
 
7
- const clockIcon = require('../assets/clock.png');
8
- const doubleCheckIcon = require('../assets/double-check.png');
8
+ const clockIcon = require('../assets/iconamoon_clock-fill.png');
9
+ const infoIcon = require('../assets/info-circle.png');
9
10
 
10
- export function MessageItem({ item }: { item: AppConversationMessage }) {
11
+ interface TextLine {
12
+ width: number;
13
+ height: number;
14
+ x: number;
15
+ y: number;
16
+ text: string;
17
+ }
18
+
19
+ interface MessageLayout {
20
+ lines: number;
21
+ lastLineWidth: number;
22
+ totalWidth: number;
23
+ }
24
+
25
+ export function MessageItem({
26
+ item,
27
+ onShowPopup,
28
+ }: {
29
+ item: AppConversationMessage;
30
+ onShowPopup: () => void;
31
+ }) {
11
32
  const themeColors = useThemeColors();
12
33
  const isDeviceOwner = !!item.user;
13
34
  const formattedDate = new Date(item.created_at).toLocaleTimeString([], {
@@ -15,73 +36,144 @@ export function MessageItem({ item }: { item: AppConversationMessage }) {
15
36
  minute: '2-digit',
16
37
  });
17
38
 
39
+ const layoutRef = useRef<MessageLayout>({
40
+ lines: 0,
41
+ lastLineWidth: 0,
42
+ totalWidth: 0,
43
+ });
44
+
45
+ const [layoutComplete, setLayoutComplete] = useState(false);
46
+
47
+ const renderFooterContent = () => {
48
+ if (isDeviceOwner && item.error) {
49
+ return <Image source={infoIcon} style={styles.infoIcon} />;
50
+ } else if (isDeviceOwner && !item.error && !item.approved) {
51
+ return <Image source={clockIcon} style={styles.clockIcon} />;
52
+ } else {
53
+ return (
54
+ <AppText
55
+ style={[
56
+ styles.timestamp,
57
+ isDeviceOwner
58
+ ? styles.rightTimestamp
59
+ : [styles.leftTimestamp, { color: themeColors.text }],
60
+ ]}
61
+ >
62
+ {formattedDate}
63
+ </AppText>
64
+ );
65
+ }
66
+ };
67
+
68
+ const onTextLayout = (event: any) => {
69
+ const { lines } = event.nativeEvent;
70
+
71
+ const numLines = lines.length;
72
+ let lastLineWidth = 0;
73
+ let totalWidth = 0;
74
+
75
+ if (numLines >= 1) {
76
+ // Get the width of the last line
77
+ lastLineWidth = lines[numLines - 1].width;
78
+ // Use the widest line as the total width
79
+ totalWidth = Math.max(...lines.map((line: TextLine) => line.width));
80
+ }
81
+
82
+ layoutRef.current = {
83
+ lines: numLines,
84
+ lastLineWidth,
85
+ totalWidth,
86
+ };
87
+
88
+ if (!layoutComplete) {
89
+ setLayoutComplete(true);
90
+ }
91
+ };
92
+
93
+ useEffect(() => {
94
+ setLayoutComplete(false);
95
+ }, [item.content]);
96
+
97
+ // Check if there's enough space for the timestamp
98
+ const hasSpaceInLastLine =
99
+ layoutRef.current.lines >= 1 &&
100
+ layoutRef.current.lastLineWidth < layoutRef.current.totalWidth * 0.8;
101
+
102
+ // A single line message with enough space for the timestamp
103
+ const isSingleLineWithSpace =
104
+ layoutRef.current.lines === 1 &&
105
+ layoutRef.current.lastLineWidth < layoutRef.current.totalWidth * 0.8;
106
+
107
+ // For multi-line messages, check if the last line has space
108
+ const isMultiLineWithSpace =
109
+ layoutRef.current.lines > 1 && hasSpaceInLastLine;
110
+
111
+ const shouldShowInlineFooter = isSingleLineWithSpace || isMultiLineWithSpace;
112
+
18
113
  return (
19
- <View
114
+ <TouchableOpacity
20
115
  key={item.id}
21
116
  style={[
22
117
  styles.messageItem,
23
118
  isDeviceOwner ? styles.rightMessage : styles.leftMessage,
24
119
  ]}
120
+ activeOpacity={isDeviceOwner && item.error ? 0.8 : 1}
121
+ onPress={() => {
122
+ isDeviceOwner && item.error ? onShowPopup() : {};
123
+ }}
25
124
  >
26
125
  <View
27
126
  style={[
28
127
  styles.messageBubble,
29
128
  isDeviceOwner
30
- ? [styles.rightBubble, { backgroundColor: themeColors.primary }]
31
- : [styles.leftBubble, { backgroundColor: themeColors.border }],
129
+ ? [styles.rightBubble, { backgroundColor: themeColors.light_green }]
130
+ : [styles.leftBubble, { backgroundColor: themeColors.ghost }],
32
131
  ]}
33
132
  >
34
- <AppText
35
- style={[
36
- styles.messageText,
37
- isDeviceOwner
38
- ? styles.rightMessageText
39
- : [styles.leftMessageText, { color: themeColors.text }],
40
- ]}
133
+ <View
134
+ style={
135
+ shouldShowInlineFooter
136
+ ? styles.inlineContainer
137
+ : styles.multiLineContainer
138
+ }
41
139
  >
42
- {item.content}
43
- </AppText>
44
- <View style={styles.footer}>
45
140
  <AppText
46
141
  style={[
47
- styles.timestamp,
142
+ styles.messageText,
48
143
  isDeviceOwner
49
- ? styles.rightTimestamp
50
- : [styles.leftTimestamp, { color: themeColors.text }],
144
+ ? styles.rightMessageText
145
+ : [styles.leftMessageText, { color: themeColors.text }],
51
146
  ]}
147
+ onTextLayout={onTextLayout}
52
148
  >
53
- {formattedDate}
149
+ {item.content}
54
150
  </AppText>
55
- {!item.approved ? (
56
- <Image
57
- source={clockIcon}
58
- style={[
59
- styles.clockIcon,
60
- isDeviceOwner
61
- ? styles.rightClockIcon
62
- : [styles.leftClockIcon, { tintColor: themeColors.text }],
63
- ]}
64
- />
65
- ) : (
66
- <Image
67
- source={doubleCheckIcon}
151
+
152
+ {shouldShowInlineFooter && (
153
+ <View
68
154
  style={[
69
- styles.clockIcon,
70
- isDeviceOwner
71
- ? styles.rightClockIcon
72
- : [styles.leftClockIcon, { tintColor: themeColors.text }],
155
+ styles.inlineFooter,
156
+ layoutRef.current.lines > 1 &&
157
+ hasSpaceInLastLine &&
158
+ styles.lastLineFooter,
73
159
  ]}
74
- />
160
+ >
161
+ {renderFooterContent()}
162
+ </View>
75
163
  )}
76
164
  </View>
165
+
166
+ {!shouldShowInlineFooter && (
167
+ <View style={styles.footer}>{renderFooterContent()}</View>
168
+ )}
77
169
  </View>
78
- </View>
170
+ </TouchableOpacity>
79
171
  );
80
172
  }
81
173
 
82
- const styles = StyleSheet.create({
174
+ const styles = ScaledSheet.create({
83
175
  messageItem: {
84
- marginVertical: 5,
176
+ marginVertical: '5@vs',
85
177
  maxWidth: '80%',
86
178
  minHeight: 60,
87
179
  minWidth: 100,
@@ -93,8 +185,9 @@ const styles = StyleSheet.create({
93
185
  alignSelf: 'flex-end',
94
186
  },
95
187
  messageBubble: {
96
- padding: 12,
97
- borderRadius: 20,
188
+ paddingVertical: '12@vs',
189
+ paddingHorizontal: '12@s',
190
+ borderRadius: '20@vs',
98
191
  },
99
192
  leftBubble: {
100
193
  borderTopLeftRadius: 4,
@@ -102,40 +195,70 @@ const styles = StyleSheet.create({
102
195
  rightBubble: {
103
196
  borderTopRightRadius: 4,
104
197
  },
198
+ inlineContainer: {
199
+ flexDirection: 'row',
200
+ justifyContent: 'space-between',
201
+ alignItems: 'center',
202
+ },
203
+ multiLineContainer: {
204
+ flexDirection: 'column',
205
+ },
105
206
  messageText: {
106
- fontSize: 16,
207
+ fontSize: '16@vs',
208
+ flexShrink: 1,
107
209
  },
108
210
  leftMessageText: {
109
211
  color: '#000000',
110
212
  },
111
213
  rightMessageText: {
112
- color: '#FFFFFF',
214
+ color: '#000000',
113
215
  },
114
216
  footer: {
115
217
  flexDirection: 'row',
116
- justifyContent: 'space-between',
218
+ justifyContent: 'flex-end',
117
219
  alignItems: 'center',
118
- marginTop: 4,
220
+ marginTop: '4@vs',
221
+ },
222
+ inlineFooter: {
223
+ flexDirection: 'row',
224
+ alignItems: 'center',
225
+ marginLeft: '8@s',
226
+ },
227
+ secondLineFooter: {
228
+ position: 'absolute',
229
+ bottom: 0,
230
+ right: 0,
231
+ backgroundColor: 'transparent',
232
+ },
233
+ lastLineFooter: {
234
+ position: 'absolute',
235
+ bottom: 0,
236
+ right: 0,
237
+ backgroundColor: 'transparent',
119
238
  },
120
239
  timestamp: {
121
- fontSize: 12,
240
+ fontSize: '10@vs',
122
241
  opacity: 0.7,
123
242
  },
124
243
  leftTimestamp: {
125
244
  color: '#666666',
126
245
  },
127
246
  rightTimestamp: {
128
- color: '#FFFFFF',
247
+ color: '#000000',
129
248
  },
130
249
  clockIcon: {
131
- width: 12,
132
- height: 12,
133
- marginLeft: 4,
250
+ width: '12@vs',
251
+ height: '12@vs',
252
+ marginLeft: '4@s',
253
+ tintColor: '#000000',
134
254
  },
135
- leftClockIcon: {
136
- tintColor: '#666666',
255
+ infoIcon: {
256
+ width: '12@vs',
257
+ height: '12@vs',
258
+ marginLeft: '4@s',
137
259
  },
138
- rightClockIcon: {
139
- tintColor: '#FFFFFF',
260
+ iconContainer: {
261
+ paddingVertical: '5@vs',
262
+ paddingHorizontal: '5@vs',
140
263
  },
141
264
  });
@@ -0,0 +1,93 @@
1
+ import {
2
+ type ImageStyle,
3
+ StyleSheet,
4
+ type TextStyle,
5
+ type ViewStyle,
6
+ } from 'react-native';
7
+ import deepMap from '../utils/deepMap';
8
+ import { s, vs, ms, mvs } from '../utils/scalingUtils';
9
+ const validScaleSheetRegex =
10
+ /^(-?\d+(?:\.\d{1,3})?)@(mv?s(\d+(?:\.\d{1,2})?)?|s|vs)r?$/;
11
+
12
+ type NamedStyles<T> = { [P in keyof T]: ViewStyle | TextStyle | ImageStyle };
13
+
14
+ const scaleByAnnotation =
15
+ (
16
+ scale: (size: number) => number,
17
+ verticalScale: (size: number) => number,
18
+ moderateScale: (size: number, factor?: number) => number,
19
+ moderateVerticalScale: (size: number, factor?: number) => number
20
+ ) =>
21
+ (value: string | number): string | number => {
22
+ if (typeof value !== 'string' || !validScaleSheetRegex.test(value)) {
23
+ return value;
24
+ }
25
+
26
+ // Type assertion for regex result
27
+ const regexExecResult = validScaleSheetRegex.exec(value);
28
+ if (!regexExecResult) {
29
+ return value;
30
+ }
31
+
32
+ // Safely extract matched groups
33
+ const sizeStr = regexExecResult[1] || '0';
34
+ const size = parseFloat(sizeStr);
35
+ const scaleFunc = regexExecResult[2] || '';
36
+
37
+ // Extract scale factor if present
38
+ let scaleFactor: number | undefined;
39
+ let scaleFuncName = scaleFunc;
40
+
41
+ // Handle ms and mvs cases which may have a factor
42
+ if (scaleFunc.startsWith('ms') || scaleFunc.startsWith('mvs')) {
43
+ const matchResult = /^(ms|mvs)(\d+(?:\.\d{1,2})?)$/.exec(scaleFunc);
44
+ if (matchResult && matchResult[1] && matchResult[2]) {
45
+ scaleFuncName = matchResult[1];
46
+ scaleFactor = parseFloat(matchResult[2]);
47
+ }
48
+ }
49
+
50
+ const shouldRound = value.endsWith('r');
51
+
52
+ let result: number;
53
+
54
+ switch (scaleFuncName) {
55
+ case 's':
56
+ result = scale(size);
57
+ break;
58
+ case 'vs':
59
+ result = verticalScale(size);
60
+ break;
61
+ case 'ms':
62
+ result = moderateScale(size, scaleFactor);
63
+ break;
64
+ case 'mvs':
65
+ result = moderateVerticalScale(size, scaleFactor);
66
+ break;
67
+ default:
68
+ return value;
69
+ }
70
+
71
+ return shouldRound ? Math.round(result) : result;
72
+ };
73
+
74
+ type ScaledSheetType = any;
75
+
76
+ const scaledSheetCreator = (
77
+ scale: (size: number) => number,
78
+ verticalScale: (size: number) => number,
79
+ moderateScale: (size: number, factor?: number) => number,
80
+ moderateVerticalScale: (size: number, factor?: number) => number
81
+ ): ScaledSheetType => {
82
+ const scaleFunc = scaleByAnnotation(
83
+ scale,
84
+ verticalScale,
85
+ moderateScale,
86
+ moderateVerticalScale
87
+ );
88
+ return {
89
+ create: (styleSheet: NamedStyles<any>) =>
90
+ StyleSheet.create(deepMap(styleSheet, scaleFunc) as NamedStyles<any>),
91
+ };
92
+ };
93
+ export const ScaledSheet = scaledSheetCreator(s, vs, ms, mvs);
@@ -0,0 +1,22 @@
1
+ import { useEffect } from 'react';
2
+ import { useAppStore } from '../store';
3
+
4
+ interface ISizeMatter {
5
+ baseWidth: number;
6
+ baseHeight: number;
7
+ }
8
+
9
+ export function SizeMatter({ baseWidth = 390, baseHeight = 844 }: ISizeMatter) {
10
+ const { updateBaseDimensions } = useAppStore((s) => ({
11
+ updateBaseDimensions: s.updateBaseDimensions,
12
+ }));
13
+
14
+ useEffect(() => {
15
+ updateBaseDimensions({
16
+ baseWidth,
17
+ baseHeight,
18
+ });
19
+ }, [baseHeight, baseWidth, updateBaseDimensions]);
20
+
21
+ return <></>;
22
+ }
package/src/index.tsx CHANGED
@@ -2,3 +2,4 @@ import './__dev__';
2
2
  export { registerComnyx } from './register';
3
3
  export type { CreateCustomerRequest } from './types/Customer';
4
4
  export { Comnyx } from './App';
5
+ export { SizeMatter } from './components/SizeMatter';
package/src/store.ts CHANGED
@@ -8,6 +8,8 @@ import type { AppConversationMessage } from './types/Conversation';
8
8
  import type { LanguageCode } from './types/Language';
9
9
 
10
10
  interface AppStoreState {
11
+ baseHeight: number;
12
+ baseWidth: number;
11
13
  initialized: boolean;
12
14
  customer: Customer | null;
13
15
  data: AppConversationMessage[] | null;
@@ -27,9 +29,15 @@ interface AppStoreState {
27
29
  setLanguage: (language: LanguageCode) => void;
28
30
  setTheme: (theme: 'light' | 'dark') => void;
29
31
  setFake: (fake: boolean) => void;
32
+ updateBaseDimensions: (config: {
33
+ baseHeight: number;
34
+ baseWidth: number;
35
+ }) => void;
30
36
  }
31
37
 
32
38
  const storeCreator: StateCreator<AppStoreState> = (set, get) => ({
39
+ baseWidth: 390,
40
+ baseHeight: 844,
33
41
  initialized: false,
34
42
  customer: null,
35
43
  data: null,
@@ -60,6 +68,9 @@ const storeCreator: StateCreator<AppStoreState> = (set, get) => ({
60
68
  setFake: (fake: boolean) => {
61
69
  set({ fake });
62
70
  },
71
+ updateBaseDimensions: ({ baseWidth, baseHeight }) => {
72
+ set({ baseWidth, baseHeight });
73
+ },
63
74
  });
64
75
 
65
76
  export const useAppStore = createWithEqualityFn<AppStoreState>()(
@@ -70,15 +81,14 @@ export const useAppStore = createWithEqualityFn<AppStoreState>()(
70
81
  partialize: (state) => ({
71
82
  customer: state.customer,
72
83
  externalId: state.externalId,
73
- data: [],
74
- /* data: state.data
84
+ data: state.data
75
85
  ?.filter((message) => message.approved)
76
86
  .map((message) => ({
77
87
  ...message,
78
88
  created_at: message.created_at
79
89
  ? new Date(message.created_at)
80
90
  : undefined,
81
- })),*/
91
+ })),
82
92
  }),
83
93
  }),
84
94
  shallow
@@ -10,6 +10,8 @@ export interface AppConversationMessage
10
10
  id: number | null;
11
11
  approved?: boolean;
12
12
  created_at: Date;
13
+ error?: boolean;
14
+ resending?: boolean;
13
15
  }
14
16
 
15
17
  export interface ConversationPage {