@messenger-box/platform-mobile 10.0.3-alpha.20 → 10.0.3-alpha.201
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/lib/components/messages-container-ui/BuildModeView.js +428 -0
- package/lib/components/messages-container-ui/BuildModeView.js.map +1 -0
- package/lib/components/messages-container-ui/MessagesContainerUI.js +55 -0
- package/lib/components/messages-container-ui/MessagesContainerUI.js.map +1 -0
- package/lib/components/messages-container-ui/PlanModeView.js +336 -0
- package/lib/components/messages-container-ui/PlanModeView.js.map +1 -0
- package/lib/compute.js +2 -3
- package/lib/compute.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/module.js.map +1 -1
- package/lib/queries/inboxQueries.js +62 -0
- package/lib/queries/inboxQueries.js.map +1 -0
- package/lib/routes.json +2 -3
- package/lib/screens/inbox/DialogMessages.js +8 -3
- package/lib/screens/inbox/DialogMessages.js.map +1 -1
- package/lib/screens/inbox/DialogThreadMessages.js +6 -11
- package/lib/screens/inbox/DialogThreadMessages.js.map +1 -1
- package/lib/screens/inbox/DialogThreads.js +58 -20
- package/lib/screens/inbox/DialogThreads.js.map +1 -1
- package/lib/screens/inbox/Inbox.js.map +1 -1
- package/lib/screens/inbox/components/CachedImage/consts.js +1 -1
- package/lib/screens/inbox/components/CachedImage/consts.js.map +1 -1
- package/lib/screens/inbox/components/CachedImage/index.js +125 -115
- package/lib/screens/inbox/components/CachedImage/index.js.map +1 -1
- package/lib/screens/inbox/components/DialogItem.js +160 -0
- package/lib/screens/inbox/components/DialogItem.js.map +1 -0
- package/lib/screens/inbox/components/GiftedChatInboxComponent.js +315 -0
- package/lib/screens/inbox/components/GiftedChatInboxComponent.js.map +1 -0
- package/lib/screens/inbox/components/SlackMessageContainer/ImageViewerModal.js +3 -1
- package/lib/screens/inbox/components/SlackMessageContainer/ImageViewerModal.js.map +1 -1
- package/lib/screens/inbox/components/SlackMessageContainer/PaymentMessage.js +194 -0
- package/lib/screens/inbox/components/SlackMessageContainer/PaymentMessage.js.map +1 -0
- package/lib/screens/inbox/components/SlackMessageContainer/SlackBubble.js +149 -36
- package/lib/screens/inbox/components/SlackMessageContainer/SlackBubble.js.map +1 -1
- package/lib/screens/inbox/components/SlackMessageContainer/SlackMessage.js +4 -5
- package/lib/screens/inbox/components/SlackMessageContainer/SlackMessage.js.map +1 -1
- package/lib/screens/inbox/components/SubscriptionHandler.js +22 -0
- package/lib/screens/inbox/components/SubscriptionHandler.js.map +1 -0
- package/lib/screens/inbox/components/ThreadsViewItem.js +67 -47
- package/lib/screens/inbox/components/ThreadsViewItem.js.map +1 -1
- package/lib/screens/inbox/config/config.js +4 -2
- package/lib/screens/inbox/config/config.js.map +1 -1
- package/lib/screens/inbox/containers/ConversationView.js +1099 -1094
- package/lib/screens/inbox/containers/ConversationView.js.map +1 -1
- package/lib/screens/inbox/containers/Dialogs.js +132 -534
- package/lib/screens/inbox/containers/Dialogs.js.map +1 -1
- package/lib/screens/inbox/containers/ThreadConversationView.js +876 -1357
- package/lib/screens/inbox/containers/ThreadConversationView.js.map +1 -1
- package/lib/screens/inbox/containers/ThreadsView.js +81 -54
- package/lib/screens/inbox/containers/ThreadsView.js.map +1 -1
- package/lib/screens/inbox/hooks/useInboxMessages.js +31 -0
- package/lib/screens/inbox/hooks/useInboxMessages.js.map +1 -0
- package/lib/screens/inbox/hooks/useSafeDialogThreadsMachine.js +108 -0
- package/lib/screens/inbox/hooks/useSafeDialogThreadsMachine.js.map +1 -0
- package/lib/screens/inbox/workflow/dialog-threads-xstate.js +151 -0
- package/lib/screens/inbox/workflow/dialog-threads-xstate.js.map +1 -0
- package/package.json +9 -7
- package/CHANGELOG.md +0 -164
- package/jest.config.js +0 -24
- package/lib/screens/inbox/components/DialogsListItem.js +0 -548
- package/lib/screens/inbox/components/DialogsListItem.js.map +0 -1
- package/lib/screens/inbox/components/ServiceDialogsListItem.js +0 -489
- package/lib/screens/inbox/components/ServiceDialogsListItem.js.map +0 -1
- package/lib/screens/inbox/components/workflow/dialogs-list-item-xstate.js +0 -175
- package/lib/screens/inbox/components/workflow/dialogs-list-item-xstate.js.map +0 -1
- package/lib/screens/inbox/components/workflow/service-dialogs-list-item-xstate.js +0 -191
- package/lib/screens/inbox/components/workflow/service-dialogs-list-item-xstate.js.map +0 -1
- package/lib/screens/inbox/containers/workflow/conversation-xstate.js +0 -380
- package/lib/screens/inbox/containers/workflow/conversation-xstate.js.map +0 -1
- package/lib/screens/inbox/containers/workflow/dialogs-xstate.js +0 -211
- package/lib/screens/inbox/containers/workflow/dialogs-xstate.js.map +0 -1
- package/lib/screens/inbox/containers/workflow/thread-conversation-xstate.js +0 -438
- package/lib/screens/inbox/containers/workflow/thread-conversation-xstate.js.map +0 -1
- package/rollup.config.mjs +0 -45
- package/src/components/index.ts +0 -0
- package/src/compute.ts +0 -63
- package/src/index.ts +0 -7
- package/src/module.ts +0 -10
- package/src/navigation/InboxNavigation.tsx +0 -102
- package/src/navigation/index.ts +0 -1
- package/src/screens/inbox/DialogMessages.tsx +0 -21
- package/src/screens/inbox/DialogThreadMessages.tsx +0 -97
- package/src/screens/inbox/DialogThreads.tsx +0 -129
- package/src/screens/inbox/Inbox.tsx +0 -17
- package/src/screens/inbox/components/CachedImage/consts.ts +0 -6
- package/src/screens/inbox/components/CachedImage/index.tsx +0 -223
- package/src/screens/inbox/components/DialogsHeader.tsx +0 -30
- package/src/screens/inbox/components/DialogsListItem.tsx +0 -819
- package/src/screens/inbox/components/ServiceDialogsListItem.tsx +0 -679
- package/src/screens/inbox/components/SlackMessageContainer/ImageViewerModal.tsx +0 -113
- package/src/screens/inbox/components/SlackMessageContainer/SlackBubble.tsx +0 -313
- package/src/screens/inbox/components/SlackMessageContainer/SlackMessage.tsx +0 -145
- package/src/screens/inbox/components/SlackMessageContainer/index.ts +0 -3
- package/src/screens/inbox/components/SupportServiceDialogsListItem.tsx +0 -301
- package/src/screens/inbox/components/ThreadsViewItem.tsx +0 -321
- package/src/screens/inbox/components/workflow/dialogs-list-item-xstate.ts +0 -145
- package/src/screens/inbox/components/workflow/service-dialogs-list-item-xstate.ts +0 -159
- package/src/screens/inbox/config/config.ts +0 -15
- package/src/screens/inbox/config/index.ts +0 -1
- package/src/screens/inbox/containers/ConversationView.tsx +0 -1782
- package/src/screens/inbox/containers/Dialogs.tsx +0 -794
- package/src/screens/inbox/containers/SupportServiceDialogs.tsx +0 -119
- package/src/screens/inbox/containers/ThreadConversationView.tsx +0 -2312
- package/src/screens/inbox/containers/ThreadsView.tsx +0 -305
- package/src/screens/inbox/containers/workflow/apollo/handleResult.ts +0 -20
- package/src/screens/inbox/containers/workflow/conversation-xstate.ts +0 -313
- package/src/screens/inbox/containers/workflow/dialogs-xstate.ts +0 -196
- package/src/screens/inbox/containers/workflow/thread-conversation-xstate.ts +0 -401
- package/src/screens/index.ts +0 -4
- package/tsconfig.json +0 -13
- package/webpack.config.js +0 -58
|
@@ -1,679 +0,0 @@
|
|
|
1
|
-
import React, { useMemo, useState, useCallback, useRef } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
Text,
|
|
4
|
-
Image,
|
|
5
|
-
Pressable,
|
|
6
|
-
HStack,
|
|
7
|
-
Box,
|
|
8
|
-
AvatarGroup,
|
|
9
|
-
Avatar,
|
|
10
|
-
AvatarFallbackText,
|
|
11
|
-
AvatarImage,
|
|
12
|
-
AvatarBadge,
|
|
13
|
-
View,
|
|
14
|
-
} from '@admin-layout/gluestack-ui-mobile';
|
|
15
|
-
import { format, isToday, isYesterday } from 'date-fns';
|
|
16
|
-
import { useFocusEffect } from '@react-navigation/native';
|
|
17
|
-
import { IChannel, IUserAccount } from 'common';
|
|
18
|
-
import {
|
|
19
|
-
useOnThreadCreatedUpdatedSubscription,
|
|
20
|
-
useOnThreadChatMessageAddedSubscription,
|
|
21
|
-
useThreadMessagesQuery,
|
|
22
|
-
OnThreadCreatedUpdatedDocument as THREAD_CHAT_ADDED,
|
|
23
|
-
OnThreadChatMessageAddedDocument as CHAT_MESSAGE_ADDED,
|
|
24
|
-
} from 'common/graphql';
|
|
25
|
-
import { startCase } from 'lodash-es';
|
|
26
|
-
import colors from 'tailwindcss/colors';
|
|
27
|
-
import { serviceDialogsListItemXstate, Actions, BaseState } from './workflow/service-dialogs-list-item-xstate';
|
|
28
|
-
|
|
29
|
-
// Helper function to safely create a Date object
|
|
30
|
-
const safeDate = (dateValue) => {
|
|
31
|
-
if (!dateValue) return null;
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
const date = new Date(dateValue);
|
|
35
|
-
// Check if the date is valid by seeing if it returns a valid time value
|
|
36
|
-
if (isNaN(date.getTime())) {
|
|
37
|
-
console.warn('Invalid date value detected:', dateValue);
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
return date;
|
|
41
|
-
} catch (error) {
|
|
42
|
-
console.warn('Error creating date from value:', dateValue, error);
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const createdAtText = (value: string) => {
|
|
48
|
-
if (!value) return '';
|
|
49
|
-
const date = safeDate(value);
|
|
50
|
-
if (!date) return '';
|
|
51
|
-
|
|
52
|
-
if (isToday(date)) return 'Today';
|
|
53
|
-
if (isYesterday(date)) return 'Yesterday';
|
|
54
|
-
return format(date, 'MMM dd, yyyy');
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export interface IDialogListChannel extends IChannel {
|
|
58
|
-
users: IUserAccount[];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export interface IDialogListItemProps {
|
|
62
|
-
currentUser?: any;
|
|
63
|
-
users?: any;
|
|
64
|
-
selectedChannelId?: any;
|
|
65
|
-
channel?: any;
|
|
66
|
-
onOpen: (id: any, title: any, postParentId?: any) => void;
|
|
67
|
-
refreshing: boolean;
|
|
68
|
-
role: any;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Create a safer version of useMachine to handle potential errors
|
|
72
|
-
function useSafeMachine(machine) {
|
|
73
|
-
// Define the state type
|
|
74
|
-
interface SafeStateType {
|
|
75
|
-
context: {
|
|
76
|
-
channelId: string | null;
|
|
77
|
-
currentUser: any;
|
|
78
|
-
threadMessages: any[];
|
|
79
|
-
loading: boolean;
|
|
80
|
-
error: string | null;
|
|
81
|
-
title: string;
|
|
82
|
-
role: string;
|
|
83
|
-
servicePostParentId: string | null;
|
|
84
|
-
lastMessage: any;
|
|
85
|
-
};
|
|
86
|
-
value: string;
|
|
87
|
-
matches?: (stateValue: string) => boolean;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Initialize state with default values to prevent undefined errors
|
|
91
|
-
const [state, setState] = useState<SafeStateType>({
|
|
92
|
-
context: {
|
|
93
|
-
channelId: null,
|
|
94
|
-
currentUser: null,
|
|
95
|
-
threadMessages: [],
|
|
96
|
-
loading: false,
|
|
97
|
-
error: null,
|
|
98
|
-
title: '',
|
|
99
|
-
role: '',
|
|
100
|
-
servicePostParentId: null,
|
|
101
|
-
lastMessage: null,
|
|
102
|
-
},
|
|
103
|
-
value: BaseState.Idle,
|
|
104
|
-
matches: (stateValue) => stateValue === BaseState.Idle,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
// Implement a send function that updates state safely
|
|
108
|
-
const send = useCallback((event) => {
|
|
109
|
-
try {
|
|
110
|
-
// Handle specific events manually
|
|
111
|
-
if (event.type === Actions.INITIAL_CONTEXT) {
|
|
112
|
-
setState((prev) => ({
|
|
113
|
-
...prev,
|
|
114
|
-
context: {
|
|
115
|
-
...prev.context,
|
|
116
|
-
channelId: event.data?.channelId || null,
|
|
117
|
-
currentUser: event.data?.currentUser || null,
|
|
118
|
-
role: event.data?.role || null,
|
|
119
|
-
loading: true,
|
|
120
|
-
},
|
|
121
|
-
value: BaseState.FetchingThreadMessages,
|
|
122
|
-
}));
|
|
123
|
-
} else if (event.type === Actions.UPDATE_THREAD_MESSAGES) {
|
|
124
|
-
setState((prev) => {
|
|
125
|
-
if (event.data?.threadMessages) {
|
|
126
|
-
// Handling bulk messages (initial load)
|
|
127
|
-
// If a direct last message was provided (from subscription), use it
|
|
128
|
-
if (event.data?.directLastMessage) {
|
|
129
|
-
console.log('Using direct last message from event:', {
|
|
130
|
-
id: event.data.directLastMessage.id,
|
|
131
|
-
message: event.data.directLastMessage.message?.substring(0, 20) + '...',
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
return {
|
|
135
|
-
...prev,
|
|
136
|
-
context: {
|
|
137
|
-
...prev.context,
|
|
138
|
-
threadMessages: event.data.threadMessages,
|
|
139
|
-
lastMessage: event.data.directLastMessage,
|
|
140
|
-
loading: false,
|
|
141
|
-
},
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return {
|
|
146
|
-
...prev,
|
|
147
|
-
context: {
|
|
148
|
-
...prev.context,
|
|
149
|
-
threadMessages: event.data.threadMessages,
|
|
150
|
-
lastMessage: computeLastMessage(event.data.threadMessages),
|
|
151
|
-
loading: false,
|
|
152
|
-
},
|
|
153
|
-
};
|
|
154
|
-
} else if (event.data?.message) {
|
|
155
|
-
// Handling single message (from subscription)
|
|
156
|
-
const newMessage = event.data.message;
|
|
157
|
-
const updatedMessages = [...prev.context.threadMessages, newMessage];
|
|
158
|
-
|
|
159
|
-
// Use the new message directly as the last message
|
|
160
|
-
// This ensures immediate update on subscription
|
|
161
|
-
console.log('Setting new message as lastMessage from subscription:', {
|
|
162
|
-
id: newMessage.id,
|
|
163
|
-
message: newMessage.message?.substring(0, 20) + '...',
|
|
164
|
-
date: newMessage.createdAt || newMessage.updatedAt,
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
...prev,
|
|
169
|
-
context: {
|
|
170
|
-
...prev.context,
|
|
171
|
-
threadMessages: updatedMessages,
|
|
172
|
-
lastMessage: newMessage, // Use the new message directly
|
|
173
|
-
loading: false,
|
|
174
|
-
},
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
return prev;
|
|
178
|
-
});
|
|
179
|
-
} else if (event.type === Actions.SET_TITLE) {
|
|
180
|
-
setState((prev) => ({
|
|
181
|
-
...prev,
|
|
182
|
-
context: {
|
|
183
|
-
...prev.context,
|
|
184
|
-
title: event.data?.title || '',
|
|
185
|
-
},
|
|
186
|
-
}));
|
|
187
|
-
} else if (event.type === Actions.SET_SERVICE_POST_PARENT_ID) {
|
|
188
|
-
setState((prev) => ({
|
|
189
|
-
...prev,
|
|
190
|
-
context: {
|
|
191
|
-
...prev.context,
|
|
192
|
-
servicePostParentId: event.data?.servicePostParentId || null,
|
|
193
|
-
},
|
|
194
|
-
}));
|
|
195
|
-
} else if (event.type === Actions.START_LOADING) {
|
|
196
|
-
setState((prev) => ({
|
|
197
|
-
...prev,
|
|
198
|
-
context: {
|
|
199
|
-
...prev.context,
|
|
200
|
-
loading: true,
|
|
201
|
-
},
|
|
202
|
-
}));
|
|
203
|
-
} else if (event.type === Actions.STOP_LOADING) {
|
|
204
|
-
setState((prev) => ({
|
|
205
|
-
...prev,
|
|
206
|
-
context: {
|
|
207
|
-
...prev.context,
|
|
208
|
-
loading: false,
|
|
209
|
-
},
|
|
210
|
-
}));
|
|
211
|
-
}
|
|
212
|
-
} catch (error) {
|
|
213
|
-
console.error('Error sending event to state machine:', error);
|
|
214
|
-
}
|
|
215
|
-
}, []);
|
|
216
|
-
|
|
217
|
-
// Helper function to compute the last message from a list of messages
|
|
218
|
-
const computeLastMessage = (threadMessages) => {
|
|
219
|
-
if (!threadMessages || !threadMessages.length) return null;
|
|
220
|
-
|
|
221
|
-
// Extract the post and replies from thread messages
|
|
222
|
-
let posts = [];
|
|
223
|
-
let replies = [];
|
|
224
|
-
|
|
225
|
-
// Helper function to check if a message is an image or contains only image content
|
|
226
|
-
const isTextMessage = (msg) => {
|
|
227
|
-
if (!msg) return false;
|
|
228
|
-
|
|
229
|
-
// If message is empty or only contains image-specific markers, ignore it
|
|
230
|
-
if (!msg.message || msg.message === '') return false;
|
|
231
|
-
|
|
232
|
-
// Check if message looks like an image URL or reference
|
|
233
|
-
const isImageMessage =
|
|
234
|
-
msg.message.includes('<img') ||
|
|
235
|
-
msg.message.includes('[Image]') ||
|
|
236
|
-
msg.message.includes('![') ||
|
|
237
|
-
(/\.(jpeg|jpg|gif|png|bmp|webp)/i.test(msg.message) &&
|
|
238
|
-
(msg.message.includes('http') || msg.message.includes('/images/')));
|
|
239
|
-
|
|
240
|
-
// Return true only for text messages (not image messages)
|
|
241
|
-
return !isImageMessage;
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
threadMessages.forEach((thread) => {
|
|
245
|
-
if (thread?.post && isTextMessage(thread.post)) {
|
|
246
|
-
posts.push(thread.post);
|
|
247
|
-
}
|
|
248
|
-
if (thread?.replies?.length) {
|
|
249
|
-
replies = [...replies, ...thread.replies.filter((r) => isTextMessage(r))];
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
// Combine and sort all messages
|
|
254
|
-
const allMessages = [...posts, ...replies];
|
|
255
|
-
if (!allMessages.length) return null;
|
|
256
|
-
|
|
257
|
-
// Return the most recent message - use safe date comparison
|
|
258
|
-
return allMessages.reduce((a, b) => {
|
|
259
|
-
const dateA = safeDate(a?.createdAt);
|
|
260
|
-
const dateB = safeDate(b?.createdAt);
|
|
261
|
-
|
|
262
|
-
// If either date is invalid, prefer the message with valid date
|
|
263
|
-
if (!dateA) return b;
|
|
264
|
-
if (!dateB) return a;
|
|
265
|
-
|
|
266
|
-
return dateA > dateB ? a : b;
|
|
267
|
-
}, allMessages[0]);
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
// Add a matches function that works with the current state value
|
|
271
|
-
const stateWithMatches = useMemo(() => {
|
|
272
|
-
return {
|
|
273
|
-
...state,
|
|
274
|
-
matches: (stateValue) => {
|
|
275
|
-
try {
|
|
276
|
-
return state.value === stateValue;
|
|
277
|
-
} catch (error) {
|
|
278
|
-
console.error(`Error in matches function:`, error);
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
281
|
-
},
|
|
282
|
-
};
|
|
283
|
-
}, [state]);
|
|
284
|
-
|
|
285
|
-
// Return as a tuple to match useMachine API
|
|
286
|
-
return [stateWithMatches, send] as const;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const ServiceChannelWithLastMessage = React.memo(({ channel, lastMessage, subscribeToNewMessages }: any) => {
|
|
290
|
-
React.useEffect(() => {
|
|
291
|
-
// Subscribe and store the unsubscribe function
|
|
292
|
-
const unsubscribe = subscribeToNewMessages();
|
|
293
|
-
return () => {
|
|
294
|
-
// Cleanup subscription on unmount
|
|
295
|
-
if (unsubscribe && typeof unsubscribe === 'function') {
|
|
296
|
-
unsubscribe();
|
|
297
|
-
}
|
|
298
|
-
};
|
|
299
|
-
}, [subscribeToNewMessages]);
|
|
300
|
-
|
|
301
|
-
// Debug output for component rendering
|
|
302
|
-
React.useEffect(() => {
|
|
303
|
-
console.log(`ServiceChannelWithLastMessage rendered for channel ${channel?.id}:`, {
|
|
304
|
-
hasLastMessage: !!lastMessage,
|
|
305
|
-
messageId: lastMessage?.id,
|
|
306
|
-
messageText: lastMessage?.message?.substring(0, 20) + (lastMessage?.message?.length > 20 ? '...' : ''),
|
|
307
|
-
date: lastMessage?.createdAt ? safeDate(lastMessage.createdAt)?.toISOString() : 'none',
|
|
308
|
-
});
|
|
309
|
-
}, [lastMessage, channel?.id]);
|
|
310
|
-
|
|
311
|
-
const count = 30;
|
|
312
|
-
const title = channel?.title.slice(0, count) + (channel?.title.length > count ? '...' : '');
|
|
313
|
-
|
|
314
|
-
// Define message display text
|
|
315
|
-
let displayMessage = 'No messages yet';
|
|
316
|
-
|
|
317
|
-
if (lastMessage) {
|
|
318
|
-
// Check for file attachments
|
|
319
|
-
const hasFileAttachments = lastMessage.files?.data?.length > 0;
|
|
320
|
-
|
|
321
|
-
// Check if the message appears to be an image
|
|
322
|
-
const isImageMessage =
|
|
323
|
-
lastMessage.message?.includes('<img') ||
|
|
324
|
-
lastMessage.message?.includes('[Image]') ||
|
|
325
|
-
lastMessage.message?.includes('![') ||
|
|
326
|
-
(/\.(jpeg|jpg|gif|png|bmp|webp)/i.test(lastMessage.message || '') &&
|
|
327
|
-
((lastMessage.message || '').includes('http') || (lastMessage.message || '').includes('/images/')));
|
|
328
|
-
|
|
329
|
-
if (hasFileAttachments) {
|
|
330
|
-
displayMessage = '📎 File attachment';
|
|
331
|
-
} else if (isImageMessage) {
|
|
332
|
-
displayMessage = '[Image]';
|
|
333
|
-
} else if (lastMessage.message && lastMessage.message.trim() !== '') {
|
|
334
|
-
displayMessage = lastMessage.message;
|
|
335
|
-
} else {
|
|
336
|
-
displayMessage = '(Empty message)';
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
return (
|
|
341
|
-
<HStack space={'sm'} className="flex-1 justify-between">
|
|
342
|
-
<Box className="flex-[0.8]">
|
|
343
|
-
<Text color={colors.gray[600]} className="text-base text-wrap flex-wrap font-semibold">
|
|
344
|
-
{title}
|
|
345
|
-
</Text>
|
|
346
|
-
<Text color={colors.gray[600]} numberOfLines={1}>
|
|
347
|
-
{displayMessage}
|
|
348
|
-
</Text>
|
|
349
|
-
</Box>
|
|
350
|
-
|
|
351
|
-
<Box className="flex-[0.2]">
|
|
352
|
-
<Text color={colors.gray[500]}>{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}</Text>
|
|
353
|
-
</Box>
|
|
354
|
-
</HStack>
|
|
355
|
-
);
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* TODO:
|
|
360
|
-
* - Get Reservation info: reservation date, status
|
|
361
|
-
* - Add ability to get property information: name, logo
|
|
362
|
-
*/
|
|
363
|
-
export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = function DialogsListItem({
|
|
364
|
-
currentUser,
|
|
365
|
-
// users,
|
|
366
|
-
selectedChannelId,
|
|
367
|
-
channel,
|
|
368
|
-
onOpen,
|
|
369
|
-
refreshing,
|
|
370
|
-
role,
|
|
371
|
-
}) {
|
|
372
|
-
// Create a ref to track if component is mounted
|
|
373
|
-
const isMountedRef = useRef(true);
|
|
374
|
-
// Define safe access functions
|
|
375
|
-
const [state, send] = useSafeMachine(serviceDialogsListItemXstate);
|
|
376
|
-
|
|
377
|
-
const safeContextProperty = useCallback(
|
|
378
|
-
(property, defaultValue = null) => {
|
|
379
|
-
try {
|
|
380
|
-
return state?.context?.[property] ?? defaultValue;
|
|
381
|
-
} catch (error) {
|
|
382
|
-
console.error(`Error accessing state.context.${property}:`, error);
|
|
383
|
-
return defaultValue;
|
|
384
|
-
}
|
|
385
|
-
},
|
|
386
|
-
[state],
|
|
387
|
-
);
|
|
388
|
-
|
|
389
|
-
const safeSend = useCallback(
|
|
390
|
-
(event) => {
|
|
391
|
-
try {
|
|
392
|
-
send(event);
|
|
393
|
-
} catch (error) {
|
|
394
|
-
console.error('Error sending event to state machine:', error, event);
|
|
395
|
-
}
|
|
396
|
-
},
|
|
397
|
-
[send],
|
|
398
|
-
);
|
|
399
|
-
|
|
400
|
-
// Query for thread messages
|
|
401
|
-
const {
|
|
402
|
-
data: threadMessages,
|
|
403
|
-
loading: threadMessageLoading,
|
|
404
|
-
error,
|
|
405
|
-
refetch: refetchThreadMessages,
|
|
406
|
-
subscribeToMore,
|
|
407
|
-
} = useThreadMessagesQuery({
|
|
408
|
-
variables: {
|
|
409
|
-
channelId: channel?.id?.toString(),
|
|
410
|
-
role,
|
|
411
|
-
limit: 2,
|
|
412
|
-
},
|
|
413
|
-
fetchPolicy: 'cache-and-network',
|
|
414
|
-
onCompleted: (data) => {
|
|
415
|
-
console.log(
|
|
416
|
-
`Completed thread messages query for channel ${channel?.id}:`,
|
|
417
|
-
data?.threadMessages?.data?.length ? 'Has messages' : 'No messages',
|
|
418
|
-
);
|
|
419
|
-
},
|
|
420
|
-
onError: (error) => {
|
|
421
|
-
console.error(`Error fetching thread messages for channel ${channel?.id}:`, error);
|
|
422
|
-
},
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
// Update state when channel or currentUser changes
|
|
426
|
-
React.useEffect(() => {
|
|
427
|
-
if (channel?.id) {
|
|
428
|
-
safeSend({
|
|
429
|
-
type: Actions.INITIAL_CONTEXT,
|
|
430
|
-
data: {
|
|
431
|
-
channelId: channel?.id?.toString(),
|
|
432
|
-
currentUser,
|
|
433
|
-
role,
|
|
434
|
-
},
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
safeSend({
|
|
438
|
-
type: Actions.SET_TITLE,
|
|
439
|
-
data: {
|
|
440
|
-
title: channel?.title || '',
|
|
441
|
-
},
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
return () => {
|
|
446
|
-
isMountedRef.current = false;
|
|
447
|
-
};
|
|
448
|
-
}, [channel?.id, currentUser, role, safeSend]);
|
|
449
|
-
|
|
450
|
-
// Update state when thread messages are loaded or refreshed
|
|
451
|
-
React.useEffect(() => {
|
|
452
|
-
if (threadMessages && isMountedRef.current) {
|
|
453
|
-
console.log(
|
|
454
|
-
`Updating thread messages for channel ${channel?.id}, count:`,
|
|
455
|
-
threadMessages?.threadMessages?.data?.length || 0,
|
|
456
|
-
);
|
|
457
|
-
|
|
458
|
-
safeSend({
|
|
459
|
-
type: Actions.UPDATE_THREAD_MESSAGES,
|
|
460
|
-
data: {
|
|
461
|
-
threadMessages: threadMessages?.threadMessages?.data || [],
|
|
462
|
-
},
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
}, [threadMessages, refreshing, channel?.id, safeSend]);
|
|
466
|
-
|
|
467
|
-
// Use computed values from state
|
|
468
|
-
const lastMessage = safeContextProperty('lastMessage', null);
|
|
469
|
-
const servicePostParentId = safeContextProperty('servicePostParentId', null);
|
|
470
|
-
|
|
471
|
-
// Update servicePostParentId when lastMessage changes
|
|
472
|
-
React.useEffect(() => {
|
|
473
|
-
if (lastMessage) {
|
|
474
|
-
const sParentId = lastMessage?.parentId ? lastMessage?.parentId : lastMessage?.id;
|
|
475
|
-
safeSend({
|
|
476
|
-
type: Actions.SET_SERVICE_POST_PARENT_ID,
|
|
477
|
-
data: {
|
|
478
|
-
servicePostParentId: sParentId,
|
|
479
|
-
},
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
}, [lastMessage, safeSend]);
|
|
483
|
-
|
|
484
|
-
const creatorAndMembersId = React.useMemo(() => {
|
|
485
|
-
if (!channel?.members) return null;
|
|
486
|
-
const membersIds: any =
|
|
487
|
-
channel?.members
|
|
488
|
-
?.filter((m: any) => m !== null && m?.user?.id !== currentUser?.id)
|
|
489
|
-
?.map((mu: any) => mu?.user?.id) ?? [];
|
|
490
|
-
const creatorId: any = channel?.creator?.id;
|
|
491
|
-
const mergedIds: any = [].concat(membersIds, creatorId) ?? [];
|
|
492
|
-
return mergedIds?.filter((m: any, pos: any) => mergedIds?.indexOf(m) === pos) ?? [];
|
|
493
|
-
}, [channel, currentUser]);
|
|
494
|
-
|
|
495
|
-
const postParentId = React.useMemo(() => {
|
|
496
|
-
if (!creatorAndMembersId?.length) return null;
|
|
497
|
-
|
|
498
|
-
return creatorAndMembersId?.length && creatorAndMembersId?.includes(currentUser?.id)
|
|
499
|
-
? null
|
|
500
|
-
: lastMessage?.parentId
|
|
501
|
-
? lastMessage?.parentId
|
|
502
|
-
: lastMessage?.id ?? 0;
|
|
503
|
-
}, [creatorAndMembersId, lastMessage, currentUser]);
|
|
504
|
-
|
|
505
|
-
// Function to force refresh the component state
|
|
506
|
-
const refreshThreadState = useCallback(() => {
|
|
507
|
-
if (channel?.id && isMountedRef.current) {
|
|
508
|
-
console.log('Forcing thread state refresh for channel:', channel?.id);
|
|
509
|
-
// First ensure we're in a loading state to trigger UI updates
|
|
510
|
-
safeSend({ type: Actions.START_LOADING });
|
|
511
|
-
|
|
512
|
-
// Refetch messages from server
|
|
513
|
-
refetchThreadMessages({
|
|
514
|
-
channelId: channel?.id?.toString(),
|
|
515
|
-
role,
|
|
516
|
-
limit: 2,
|
|
517
|
-
})
|
|
518
|
-
.then((result) => {
|
|
519
|
-
// Update the state with fresh data
|
|
520
|
-
if (result.data?.threadMessages?.data && isMountedRef.current) {
|
|
521
|
-
console.log(
|
|
522
|
-
`Refreshed ${result.data.threadMessages.data.length} thread messages for channel ${channel?.id}`,
|
|
523
|
-
);
|
|
524
|
-
|
|
525
|
-
safeSend({
|
|
526
|
-
type: Actions.UPDATE_THREAD_MESSAGES,
|
|
527
|
-
data: { threadMessages: result.data.threadMessages.data },
|
|
528
|
-
});
|
|
529
|
-
}
|
|
530
|
-
safeSend({ type: Actions.STOP_LOADING });
|
|
531
|
-
})
|
|
532
|
-
.catch((err) => {
|
|
533
|
-
console.error('Error refreshing thread state:', err);
|
|
534
|
-
safeSend({ type: Actions.STOP_LOADING });
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
}, [channel?.id, refetchThreadMessages, safeSend, isMountedRef, role]);
|
|
538
|
-
|
|
539
|
-
// Refetch messages when screen is focused or refreshing
|
|
540
|
-
useFocusEffect(
|
|
541
|
-
React.useCallback(() => {
|
|
542
|
-
if (!channel?.id) return;
|
|
543
|
-
|
|
544
|
-
console.log('ServiceDialogsListItem focused for channel:', channel?.id);
|
|
545
|
-
|
|
546
|
-
// Show loading state
|
|
547
|
-
safeSend({ type: Actions.START_LOADING });
|
|
548
|
-
|
|
549
|
-
// Use a direct refetch with network-only policy to force fresh data
|
|
550
|
-
const fetchFreshData = async () => {
|
|
551
|
-
try {
|
|
552
|
-
// Force a network-only fetch
|
|
553
|
-
const result = await refetchThreadMessages({
|
|
554
|
-
channelId: channel?.id?.toString(),
|
|
555
|
-
role,
|
|
556
|
-
limit: 2,
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
// Log the refreshed data
|
|
560
|
-
console.log(
|
|
561
|
-
`FOCUS EFFECT: Refetched ${
|
|
562
|
-
result?.data?.threadMessages?.data?.length || 0
|
|
563
|
-
} messages for channel ${channel?.id}`,
|
|
564
|
-
);
|
|
565
|
-
|
|
566
|
-
if (result?.data?.threadMessages?.data && isMountedRef.current) {
|
|
567
|
-
// Update state with fresh data
|
|
568
|
-
safeSend({
|
|
569
|
-
type: Actions.UPDATE_THREAD_MESSAGES,
|
|
570
|
-
data: { threadMessages: result.data.threadMessages.data },
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
} catch (error) {
|
|
574
|
-
console.error('Error refetching thread messages on focus:', error);
|
|
575
|
-
} finally {
|
|
576
|
-
if (isMountedRef.current) {
|
|
577
|
-
safeSend({ type: Actions.STOP_LOADING });
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
};
|
|
581
|
-
|
|
582
|
-
// Execute fetch
|
|
583
|
-
fetchFreshData();
|
|
584
|
-
|
|
585
|
-
return () => {
|
|
586
|
-
// Cleanup function when unfocused
|
|
587
|
-
};
|
|
588
|
-
}, [refreshing, channel?.id, refetchThreadMessages, safeSend, role, isMountedRef]),
|
|
589
|
-
);
|
|
590
|
-
|
|
591
|
-
return (
|
|
592
|
-
<Pressable
|
|
593
|
-
onPress={() => channel?.id !== selectedChannelId && onOpen(channel?.id, channel?.title, postParentId)}
|
|
594
|
-
className="flex-1 rounded-md border-gray-200 dark:border-gray-600 dark:bg-gray-700"
|
|
595
|
-
style={{ borderBottomWidth: 1, borderColor: '#e5e7eb', marginVertical: 0 }}
|
|
596
|
-
>
|
|
597
|
-
<HStack space={'md'} className="flex-1 w-[100%] px-2 py-3 items-center">
|
|
598
|
-
<Box className="flex-[0.1] items-start pl-3">
|
|
599
|
-
<Avatar
|
|
600
|
-
key={'service-channels-key-' + channel?.id}
|
|
601
|
-
size={'sm'}
|
|
602
|
-
className="bg-transparent top-0 right-0 z-[1]"
|
|
603
|
-
>
|
|
604
|
-
<AvatarFallbackText> {startCase(channel?.creator?.username?.charAt(0))}</AvatarFallbackText>
|
|
605
|
-
<AvatarImage
|
|
606
|
-
alt="user image"
|
|
607
|
-
style={{ borderRadius: 6, borderWidth: 2, borderColor: '#fff' }}
|
|
608
|
-
source={{
|
|
609
|
-
uri: channel?.creator?.picture,
|
|
610
|
-
}}
|
|
611
|
-
/>
|
|
612
|
-
</Avatar>
|
|
613
|
-
</Box>
|
|
614
|
-
<Box className="flex-1">
|
|
615
|
-
<ServiceChannelWithLastMessage
|
|
616
|
-
channel={channel}
|
|
617
|
-
lastMessage={lastMessage}
|
|
618
|
-
subscribeToNewMessages={() =>
|
|
619
|
-
subscribeToMore({
|
|
620
|
-
document: THREAD_CHAT_ADDED,
|
|
621
|
-
variables: {
|
|
622
|
-
channelId: channel?.id?.toString(),
|
|
623
|
-
postParentId: postParentId ? servicePostParentId : null,
|
|
624
|
-
},
|
|
625
|
-
updateQuery: (prev, { subscriptionData }: any) => {
|
|
626
|
-
if (!subscriptionData.data) return prev;
|
|
627
|
-
|
|
628
|
-
const newPostThreadData: any = subscriptionData?.data?.threadCreatedUpdated?.data;
|
|
629
|
-
const newMessage: any = subscriptionData?.data?.threadCreatedUpdated?.lastMessage;
|
|
630
|
-
|
|
631
|
-
console.log('New thread message subscription update:', {
|
|
632
|
-
channelId: channel?.id,
|
|
633
|
-
threadId: newPostThreadData?.id,
|
|
634
|
-
hasMessage: !!newMessage,
|
|
635
|
-
message: newMessage?.message?.substring(0, 20) + '...',
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
const data =
|
|
639
|
-
prev?.threadMessages?.data?.map((t: any) =>
|
|
640
|
-
t.id === newPostThreadData?.id
|
|
641
|
-
? {
|
|
642
|
-
...t,
|
|
643
|
-
replies: [...t?.replies, newMessage],
|
|
644
|
-
replyCount: newPostThreadData?.replyCount,
|
|
645
|
-
lastReplyAt: newPostThreadData?.lastReplyAt,
|
|
646
|
-
updatedAt: newPostThreadData?.updatedAt,
|
|
647
|
-
}
|
|
648
|
-
: t,
|
|
649
|
-
) ?? [];
|
|
650
|
-
|
|
651
|
-
// Use the safe state update function
|
|
652
|
-
if (isMountedRef.current) {
|
|
653
|
-
safeSend({
|
|
654
|
-
type: Actions.UPDATE_THREAD_MESSAGES,
|
|
655
|
-
data: {
|
|
656
|
-
threadMessages: data?.length > 0 ? data : [newPostThreadData],
|
|
657
|
-
directLastMessage: newMessage, // Directly set the new message
|
|
658
|
-
},
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
return Object.assign({}, prev, {
|
|
663
|
-
threadMessages: {
|
|
664
|
-
...prev?.threadMessages,
|
|
665
|
-
totalCount: newPostThreadData?.totalCount ?? 0,
|
|
666
|
-
data: data?.length > 0 ? data : [newPostThreadData],
|
|
667
|
-
},
|
|
668
|
-
});
|
|
669
|
-
},
|
|
670
|
-
})
|
|
671
|
-
}
|
|
672
|
-
/>
|
|
673
|
-
</Box>
|
|
674
|
-
</HStack>
|
|
675
|
-
</Pressable>
|
|
676
|
-
);
|
|
677
|
-
};
|
|
678
|
-
|
|
679
|
-
export const ServiceDialogsListItem = React.memo(ServiceDialogsListItemComponent);
|