@developer_tribe/react-native-comnyx 0.3.1 → 0.3.3

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 +355 -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 +16 -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 +357 -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 +11 -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 +2 -2
  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 +413 -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 +8 -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,24 +1,29 @@
1
- import { useCallback, useEffect, useRef, useState } from 'react';
1
+ import { useCallback, useEffect, useRef, useState, useMemo } from 'react';
2
2
  import { useAppStore } from '../store';
3
3
  import {
4
- FlatList,
4
+ SectionList,
5
5
  View,
6
6
  useWindowDimensions,
7
7
  ActivityIndicator,
8
8
  Image,
9
9
  TouchableOpacity,
10
- Keyboard,
11
- Pressable,
12
10
  } from 'react-native';
13
- import { getCustomerConversation } from '../api';
11
+ import { getCustomerConversation, sendCustomerMessage } from '../api';
14
12
  import type { AppConversationMessage } from '../types/Conversation';
15
- import { StyleSheet } from 'react-native';
16
13
  import { MessageItem } from './MessageItem';
17
14
  import { MessageInput } from './MessageInput';
18
15
  import { useThemeColors } from '../hooks/useThemeColors';
19
16
  import { AppText } from './AppText';
20
17
  import { InitFailed } from './InitFailed';
21
18
  import { EmptyList } from './EmptyList';
19
+ import CustomPopup from './CustomAlert';
20
+ import type { LocalizationKeys } from '../types/LocalizationKeys';
21
+ import { ScaledSheet } from './ScaledSheet';
22
+ import { formatDate } from '../utils/formatDate';
23
+ import { viewabilityConfig } from '../viewabilityConfig';
24
+
25
+ const headphonesIcon = require('../assets/headphones-01.png');
26
+ const closeIcon = require('../assets/x-close.png');
22
27
 
23
28
  function LoadingItem() {
24
29
  const themeColors = useThemeColors();
@@ -31,7 +36,7 @@ function LoadingItem() {
31
36
  );
32
37
  }
33
38
 
34
- export function ChatList() {
39
+ export function ChatList({ onBack }: { onBack?: () => void }) {
35
40
  const themeColors = useThemeColors();
36
41
  const { height: windowHeight } = useWindowDimensions();
37
42
  const MESSAGE_MIN_HEIGHT = 60;
@@ -45,13 +50,124 @@ export function ChatList() {
45
50
  data: s.data,
46
51
  setData: s.setData,
47
52
  }));
48
- const ref = useRef<FlatList<AppConversationMessage>>(null);
53
+ const ref = useRef<SectionList<AppConversationMessage>>(null);
49
54
  const [page, setPage] = useState(1);
50
55
  const nextPageStatus = useRef<'fail' | 'loading' | 'empty'>();
51
56
  const [nexPageFailed, setNexPageFailed] = useState(false);
52
57
  const [initFailed, setInitFailed] = useState(false);
53
58
  const [isScrollingUp, setIsScrollingUp] = useState(false);
54
59
  const listChangedRef = useRef(false);
60
+ const [popupVisible, setPopupVisible] = useState(false);
61
+ const [selectedMessage, setSelectedMessage] = useState<string>('');
62
+ const [currentSection, setCurrentSection] = useState<string>('');
63
+
64
+ const sections = useMemo(() => {
65
+ if (!data || data.length === 0) return [];
66
+
67
+ const messagesByDate: Record<string, AppConversationMessage[]> = {};
68
+
69
+ data.forEach((message: AppConversationMessage) => {
70
+ const dateKey = formatDate(message.created_at);
71
+ if (!messagesByDate[dateKey]) {
72
+ messagesByDate[dateKey] = [];
73
+ }
74
+ // @ts-ignore
75
+ messagesByDate[dateKey].push(message);
76
+ });
77
+
78
+ return Object.entries(messagesByDate).map(([title, messages]) => ({
79
+ title,
80
+ data: messages.sort(
81
+ (a, b) => b.created_at.getTime() - a.created_at.getTime()
82
+ ),
83
+ }));
84
+ }, [data]);
85
+
86
+ const resendMessage = useCallback(() => {
87
+ if (selectedMessage && customer?.external_id) {
88
+ const currentMessage = useAppStore.getState().data || [];
89
+ const messageIndex = currentMessage.findIndex(
90
+ (msg) => msg.content === selectedMessage
91
+ );
92
+
93
+ if (messageIndex === -1) {
94
+ return;
95
+ }
96
+
97
+ const updatedValue = [...currentMessage];
98
+ const existingValue = updatedValue[messageIndex];
99
+
100
+ if (existingValue) {
101
+ updatedValue[messageIndex] = {
102
+ ...existingValue,
103
+ approved: false,
104
+ error: false,
105
+ };
106
+
107
+ useAppStore.setState({
108
+ data: updatedValue,
109
+ });
110
+
111
+ sendCustomerMessage(customer.external_id, selectedMessage, {
112
+ fake: useAppStore.getState().fake,
113
+ })
114
+ .then((res: any) => {
115
+ const currentData = useAppStore.getState().data || [];
116
+ const dataIndex = currentData.findIndex(
117
+ (msg) => msg.content === selectedMessage
118
+ );
119
+
120
+ if (dataIndex !== -1) {
121
+ const updatedData = [...currentData];
122
+ const existingMessage = updatedData[dataIndex];
123
+
124
+ if (existingMessage) {
125
+ updatedData[dataIndex] = {
126
+ ...existingMessage,
127
+ id: res.message.id,
128
+ content: res.message.content,
129
+ user: res.message.user || existingMessage.user,
130
+ approved: true,
131
+ error: false,
132
+ created_at: new Date(res.message.created_at),
133
+ };
134
+
135
+ useAppStore.setState({
136
+ data: updatedData,
137
+ });
138
+ }
139
+ }
140
+
141
+ setSelectedMessage('');
142
+ })
143
+ .catch((error) => {
144
+ const currentData = useAppStore.getState().data || [];
145
+ const dataIndex = currentData.findIndex(
146
+ (msg) => msg.content === selectedMessage
147
+ );
148
+
149
+ if (dataIndex !== -1) {
150
+ const updatedMessage = [...currentData];
151
+ const existingData = updatedMessage[dataIndex];
152
+
153
+ if (existingData) {
154
+ updatedMessage[dataIndex] = {
155
+ ...existingData,
156
+ error: true,
157
+ };
158
+
159
+ useAppStore.setState({
160
+ data: updatedMessage,
161
+ });
162
+ }
163
+ }
164
+
165
+ console.error('Mesajı yeniden gönderme hatası:', error);
166
+ setSelectedMessage('');
167
+ });
168
+ }
169
+ }
170
+ }, [selectedMessage, customer]);
55
171
 
56
172
  const nextPage = useCallback(() => {
57
173
  if (nextPageStatus.current) {
@@ -105,34 +221,121 @@ export function ChatList() {
105
221
  }, [MESSAGES_PER_PAGE, customer?.external_id, page, setData]);
106
222
 
107
223
  const renderItem = useCallback(
108
- ({ item }: { item: AppConversationMessage }) => <MessageItem item={item} />,
224
+ ({ item }: { item: AppConversationMessage }) => {
225
+ return (
226
+ <MessageItem
227
+ item={item}
228
+ onShowPopup={() => {
229
+ setSelectedMessage(item.content);
230
+ setPopupVisible(true);
231
+ }}
232
+ />
233
+ );
234
+ },
109
235
  []
110
236
  );
111
237
 
238
+ const handleViewabilityChange = useCallback(
239
+ (info: { viewableItems: any[] }) => {
240
+ if (info.viewableItems && info.viewableItems.length > 0) {
241
+ // Find the first viewable item with a section
242
+ for (const viewableItem of info.viewableItems) {
243
+ if (viewableItem.section && viewableItem.section.title) {
244
+ const title = viewableItem.section.title;
245
+ if (title !== currentSection) {
246
+ setCurrentSection(title);
247
+ break;
248
+ }
249
+ }
250
+ }
251
+ }
252
+ },
253
+ [currentSection]
254
+ );
255
+
256
+ const viewabilityConfigCallbackPairs = useRef([
257
+ {
258
+ viewabilityConfig,
259
+ onViewableItemsChanged: handleViewabilityChange,
260
+ },
261
+ ]);
262
+
263
+ // We'll use onScroll instead of onMomentumScrollEnd for more responsive updates
112
264
  const handleScroll = useCallback(
113
265
  (event: any) => {
114
266
  const currentScrollY = event.nativeEvent.contentOffset.y;
115
267
  const contentHeight = event.nativeEvent.contentSize.height;
116
268
  const scrollViewHeight = event.nativeEvent.layoutMeasurement.height;
269
+
117
270
  setIsScrollingUp(currentScrollY > 100);
118
271
 
272
+ // Only proceed if we have sections and the list is scrolled enough
273
+ if (sections && sections.length > 0 && currentScrollY > 0) {
274
+ // Create visibility info for each section
275
+ let visibleSectionIndex = -1;
276
+ let maxVisibility = 0;
277
+
278
+ // For inverted lists, we need to iterate through sections to find which one is most visible
279
+ for (let i = 0; i < sections.length; i++) {
280
+ const section = sections[i];
281
+ if (!section || !section.data) continue;
282
+
283
+ const sectionStartPercent = i / sections.length;
284
+ const sectionEndPercent = (i + 1) / sections.length;
285
+
286
+ // Convert percentages to scroll positions
287
+ const sectionStartPosition = contentHeight * sectionStartPercent;
288
+ const sectionEndPosition = contentHeight * sectionEndPercent;
289
+
290
+ // Calculate how much of this section is visible
291
+ const visibleTop = Math.max(currentScrollY, sectionStartPosition);
292
+ const visibleBottom = Math.min(
293
+ currentScrollY + scrollViewHeight,
294
+ sectionEndPosition
295
+ );
296
+ const visibleHeight = Math.max(0, visibleBottom - visibleTop);
297
+
298
+ // Track which section has the most visibility
299
+ if (visibleHeight > maxVisibility) {
300
+ maxVisibility = visibleHeight;
301
+ visibleSectionIndex = i;
302
+ }
303
+ }
304
+
305
+ // If we found a visible section, update the header
306
+ if (visibleSectionIndex >= 0 && visibleSectionIndex < sections.length) {
307
+ const visibleSection = sections[visibleSectionIndex];
308
+ if (
309
+ visibleSection?.title &&
310
+ visibleSection.title !== currentSection
311
+ ) {
312
+ console.log(
313
+ `Most visible section: ${visibleSection.title} (index ${visibleSectionIndex})`
314
+ );
315
+ setCurrentSection(visibleSection.title);
316
+ }
317
+ }
318
+ }
319
+
320
+ // Check if we need to load more data
119
321
  if (currentScrollY >= contentHeight - scrollViewHeight - 200) {
120
322
  nextPage();
121
323
  }
122
324
  },
123
- [nextPage]
325
+ [nextPage, sections, currentSection]
124
326
  );
125
327
 
126
328
  const scrollToBottom = useCallback(
127
329
  (animated: boolean) => {
128
- if (ref.current && data?.length) {
129
- ref.current.scrollToOffset({
130
- offset: 0,
330
+ if (ref.current && sections.length) {
331
+ ref.current.scrollToLocation({
332
+ sectionIndex: 0,
333
+ itemIndex: 0,
131
334
  animated,
132
335
  });
133
336
  }
134
337
  },
135
- [data]
338
+ [sections]
136
339
  );
137
340
 
138
341
  useEffect(() => {
@@ -145,18 +348,18 @@ export function ChatList() {
145
348
  setData((prevData) => {
146
349
  const newMessages = newData?.page?.data ?? [];
147
350
  const existingIds = new Set(prevData?.map((msg) => msg.id));
148
- const uniqueNewMessages = newMessages.filter(
149
- (msg) => !existingIds.has(msg.id)
351
+ const uniqueNewMessages = newMessages?.filter(
352
+ (msg) => !existingIds?.has(msg.id)
150
353
  );
151
- const processedMessages = uniqueNewMessages.map((u) => ({
354
+ const processedMessages = uniqueNewMessages?.map((u) => ({
152
355
  ...u,
153
356
  created_at: new Date(u.created_at),
154
357
  approved: true,
155
358
  }));
156
359
  useAppStore.setState({
157
- firstMessage: processedMessages[0],
360
+ firstMessage: processedMessages?.[0],
158
361
  });
159
- if (processedMessages.length === 0) {
362
+ if (processedMessages?.length === 0) {
160
363
  setLoading(false);
161
364
  }
162
365
  return processedMessages;
@@ -165,10 +368,20 @@ export function ChatList() {
165
368
  .catch((e) => {
166
369
  setInitFailed(true);
167
370
  console.error(e);
371
+ })
372
+ .finally(() => {
373
+ setLoading(false);
168
374
  });
169
375
  }
170
376
  }, [MESSAGES_PER_PAGE, customer?.external_id, initFailed, setData]);
171
377
 
378
+ // Initialize the current section when sections are loaded
379
+ useEffect(() => {
380
+ if (sections.length > 0 && sections[0]?.title) {
381
+ setCurrentSection(sections[0].title);
382
+ }
383
+ }, [sections]);
384
+
172
385
  if (nexPageFailed) {
173
386
  return (
174
387
  <View
@@ -197,38 +410,92 @@ export function ChatList() {
197
410
  if (initFailed) {
198
411
  return <InitFailed setInitFailed={setInitFailed} />;
199
412
  }
413
+
200
414
  return (
201
- <Pressable
415
+ <View
202
416
  style={[styles.container, { backgroundColor: themeColors.background }]}
203
- onPress={Keyboard.dismiss}
204
417
  >
205
- <FlatList
206
- ref={ref}
207
- data={data}
208
- inverted
209
- renderItem={renderItem}
210
- contentContainerStyle={[
211
- styles.contentContainer,
212
- { backgroundColor: themeColors.background },
418
+ <TouchableOpacity style={[styles.iconContainer]} onPress={onBack}>
419
+ <Image
420
+ source={closeIcon}
421
+ style={[styles.closeIcon, { tintColor: themeColors.text }]}
422
+ />
423
+ </TouchableOpacity>
424
+ <View
425
+ style={[
426
+ styles.headerContainer,
427
+ {
428
+ backgroundColor: themeColors.background,
429
+ borderBottomColor: themeColors.lavender,
430
+ },
213
431
  ]}
214
- style={styles.list}
215
- ListEmptyComponent={
216
- !loading && (!data || data.length === 0) ? <EmptyList /> : null
217
- }
218
- ListFooterComponent={loading ? <LoadingItem /> : null}
219
- keyExtractor={(item) => item.id + '-' + item.created_at}
220
- removeClippedSubviews={true}
221
- maxToRenderPerBatch={10}
222
- windowSize={21}
223
- initialNumToRender={MESSAGES_PER_PAGE}
224
- getItemLayout={(_, index) => ({
225
- length: MESSAGE_MIN_HEIGHT + 10,
226
- offset: (MESSAGE_MIN_HEIGHT + 10) * index,
227
- index,
228
- })}
229
- onScroll={handleScroll}
230
- scrollEventThrottle={16}
231
- />
432
+ >
433
+ <AppText
434
+ localization="chat.empty"
435
+ style={[styles.header, { color: themeColors.text }]}
436
+ />
437
+ <View
438
+ style={[
439
+ styles.headerText,
440
+ { backgroundColor: themeColors.background },
441
+ ]}
442
+ >
443
+ <Image
444
+ source={headphonesIcon}
445
+ style={[styles.headphonesIcon, { tintColor: themeColors.text }]}
446
+ />
447
+ <AppText
448
+ localization="chat.empty"
449
+ style={[styles.liveChat, { color: themeColors.text }]}
450
+ />
451
+ <View style={[styles.dot, { backgroundColor: themeColors.green }]} />
452
+ </View>
453
+ </View>
454
+
455
+ <View style={styles.listContainer}>
456
+ <AppText
457
+ style={[
458
+ styles.fixedDateText,
459
+ { color: themeColors.slate, backgroundColor: themeColors.ghost },
460
+ ]}
461
+ >
462
+ {currentSection}
463
+ </AppText>
464
+
465
+ <SectionList
466
+ ref={ref}
467
+ sections={sections}
468
+ inverted
469
+ renderItem={renderItem}
470
+ contentContainerStyle={[
471
+ styles.contentContainer,
472
+ { backgroundColor: themeColors.background },
473
+ ]}
474
+ style={styles.list}
475
+ ListEmptyComponent={
476
+ !loading && (!data || data.length === 0) ? <EmptyList /> : null
477
+ }
478
+ ListFooterComponent={loading ? <LoadingItem /> : null}
479
+ keyExtractor={(item: AppConversationMessage) =>
480
+ item.id + '-' + item.created_at
481
+ }
482
+ removeClippedSubviews={true}
483
+ maxToRenderPerBatch={10}
484
+ windowSize={21}
485
+ initialNumToRender={MESSAGES_PER_PAGE}
486
+ getItemLayout={(_: any, index: number) => ({
487
+ length: MESSAGE_MIN_HEIGHT + 10,
488
+ offset: (MESSAGE_MIN_HEIGHT + 10) * index,
489
+ index,
490
+ })}
491
+ onScroll={handleScroll}
492
+ scrollEventThrottle={16}
493
+ viewabilityConfigCallbackPairs={
494
+ viewabilityConfigCallbackPairs.current
495
+ }
496
+ stickySectionHeadersEnabled={false}
497
+ />
498
+ </View>
232
499
  {isScrollingUp && (
233
500
  <TouchableOpacity
234
501
  style={[
@@ -243,21 +510,41 @@ export function ChatList() {
243
510
  />
244
511
  </TouchableOpacity>
245
512
  )}
246
- <MessageInput scrollToBottom={scrollToBottom} />
247
- </Pressable>
513
+ <MessageInput
514
+ scrollToBottom={scrollToBottom}
515
+ selectedMessage={selectedMessage}
516
+ />
517
+ <CustomPopup
518
+ isVisible={popupVisible}
519
+ onClose={() => setPopupVisible(false)}
520
+ title={'chat.list-failed-message.title'}
521
+ description={'chat.list-failed-message.description'}
522
+ onCancelButton={() => {
523
+ setPopupVisible(false);
524
+ }}
525
+ onResendButton={() => {
526
+ resendMessage();
527
+ setPopupVisible(false);
528
+ }}
529
+ buttonText={'chat.list-cancel' as keyof LocalizationKeys}
530
+ />
531
+ </View>
248
532
  );
249
533
  }
250
534
 
251
- const styles = StyleSheet.create({
535
+ const styles = ScaledSheet.create({
252
536
  container: {
253
537
  flex: 1,
538
+ paddingVertical: '20@vs',
539
+ position: 'relative',
254
540
  },
255
541
  list: {
256
542
  flex: 1,
257
543
  },
258
544
  contentContainer: {
259
- paddingVertical: 10,
260
- paddingHorizontal: 10,
545
+ paddingHorizontal: '10@s',
546
+ paddingVertical: '10@vs',
547
+ flexGrow: 1,
261
548
  },
262
549
  loadingItem: {
263
550
  height: 200,
@@ -266,10 +553,11 @@ const styles = StyleSheet.create({
266
553
  },
267
554
  scrollDownButton: {
268
555
  position: 'absolute',
269
- right: 20,
556
+ right: '20@s',
270
557
  bottom: '15%',
271
- borderRadius: 25,
272
- padding: 10,
558
+ borderRadius: '25@s',
559
+ paddingHorizontal: '10@s',
560
+ paddingVertical: '10@vs',
273
561
  shadowColor: '#000',
274
562
  shadowOffset: {
275
563
  width: 0,
@@ -280,23 +568,88 @@ const styles = StyleSheet.create({
280
568
  elevation: 5,
281
569
  },
282
570
  scrollUpIcon: {
283
- width: 24,
284
- height: 24,
571
+ width: '24@s',
572
+ height: '24@vs',
285
573
  },
286
574
  retryContainer: {
287
575
  flex: 1,
288
576
  alignItems: 'center',
289
577
  justifyContent: 'center',
290
- padding: 20,
578
+ paddingHorizontal: '20@s',
579
+ paddingVertical: '20@vs',
291
580
  },
292
581
  retryButton: {
293
582
  padding: 15,
294
- borderRadius: 8,
583
+ borderRadius: '8@s',
295
584
  alignItems: 'center',
296
585
  justifyContent: 'center',
297
586
  },
298
587
  retryText: {
299
- fontSize: 16,
588
+ fontSize: '16@vs',
300
589
  fontWeight: '500',
301
590
  },
591
+ headphonesIcon: {
592
+ width: '24@s',
593
+ height: '24@vs',
594
+ },
595
+ header: {
596
+ fontSize: '16@vs',
597
+ textAlign: 'center',
598
+ fontWeight: 600,
599
+ },
600
+ liveChat: {
601
+ fontSize: '16@vs',
602
+ textAlign: 'center',
603
+ fontWeight: 400,
604
+ },
605
+ headerContainer: {
606
+ alignItems: 'center',
607
+ gap: 16,
608
+ borderBottomWidth: 1,
609
+ paddingBottom: '16@vs',
610
+ },
611
+ iconContainer: {
612
+ paddingHorizontal: '20@s',
613
+ paddingVertical: '20@vs',
614
+ position: 'absolute',
615
+ zIndex: 9999,
616
+ },
617
+ closeIcon: {
618
+ width: '24@s',
619
+ height: '24@vs',
620
+ },
621
+ dot: {
622
+ width: '10@s',
623
+ height: '10@vs',
624
+ borderRadius: '8@s',
625
+ },
626
+ headerText: { flexDirection: 'row', alignItems: 'center', gap: 4 },
627
+ sectionHeader: {
628
+ fontSize: '14@vs',
629
+ fontWeight: '400',
630
+ borderRadius: '8@s',
631
+ paddingHorizontal: '8@s',
632
+ paddingVertical: '4@vs',
633
+ alignSelf: 'center',
634
+ marginVertical: '8@vs',
635
+ },
636
+ fixedDateText: {
637
+ fontSize: '14@vs',
638
+ fontWeight: '500',
639
+ borderRadius: '8@s',
640
+ paddingHorizontal: '10@s',
641
+ paddingVertical: '4@s',
642
+ position: 'absolute',
643
+ top: '10@vs',
644
+ zIndex: 10,
645
+ alignSelf: 'center',
646
+ },
647
+ listContainer: {
648
+ flex: 1,
649
+ position: 'relative',
650
+ },
651
+ sectionHeaderContainer: {
652
+ alignItems: 'center',
653
+ marginVertical: '8@vs',
654
+ },
302
655
  });