@messenger-box/platform-mobile 10.0.3-alpha.7 → 10.0.3-alpha.74
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/CHANGELOG.md +120 -0
- package/lib/compute.js +2 -3
- package/lib/compute.js.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/queries/inboxQueries.js +65 -0
- package/lib/queries/inboxQueries.js.map +1 -0
- package/lib/routes.json +2 -3
- package/lib/screens/inbox/DialogMessages.js +1 -1
- package/lib/screens/inbox/DialogMessages.js.map +1 -1
- package/lib/screens/inbox/DialogThreadMessages.js +4 -8
- package/lib/screens/inbox/DialogThreadMessages.js.map +1 -1
- package/lib/screens/inbox/DialogThreads.js +57 -12
- package/lib/screens/inbox/DialogThreads.js.map +1 -1
- package/lib/screens/inbox/Inbox.js +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 +168 -46
- package/lib/screens/inbox/components/CachedImage/index.js.map +1 -1
- package/lib/screens/inbox/components/DialogItem.js +169 -0
- package/lib/screens/inbox/components/DialogItem.js.map +1 -0
- package/lib/screens/inbox/components/GiftedChatInboxComponent.js +313 -0
- package/lib/screens/inbox/components/GiftedChatInboxComponent.js.map +1 -0
- package/lib/screens/inbox/components/SlackMessageContainer/SlackBubble.js +147 -31
- package/lib/screens/inbox/components/SlackMessageContainer/SlackBubble.js.map +1 -1
- package/lib/screens/inbox/components/SlackMessageContainer/SlackMessage.js +6 -1
- package/lib/screens/inbox/components/SlackMessageContainer/SlackMessage.js.map +1 -1
- package/lib/screens/inbox/components/SubscriptionHandler.js +24 -0
- package/lib/screens/inbox/components/SubscriptionHandler.js.map +1 -0
- package/lib/screens/inbox/components/ThreadsViewItem.js +66 -55
- package/lib/screens/inbox/components/ThreadsViewItem.js.map +1 -1
- package/lib/screens/inbox/config/config.js +2 -2
- package/lib/screens/inbox/config/config.js.map +1 -1
- package/lib/screens/inbox/containers/ConversationView.js +1111 -434
- package/lib/screens/inbox/containers/ConversationView.js.map +1 -1
- package/lib/screens/inbox/containers/Dialogs.js +193 -80
- package/lib/screens/inbox/containers/Dialogs.js.map +1 -1
- package/lib/screens/inbox/containers/ThreadConversationView.js +725 -216
- package/lib/screens/inbox/containers/ThreadConversationView.js.map +1 -1
- package/lib/screens/inbox/containers/ThreadsView.js +83 -50
- 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 +4 -4
- package/src/compute.ts +5 -6
- package/src/index.ts +2 -0
- package/src/navigation/InboxNavigation.tsx +3 -3
- package/src/queries/inboxQueries.ts +299 -0
- package/src/queries/index.d.ts +2 -0
- package/src/queries/index.ts +1 -0
- package/src/screens/inbox/DialogMessages.tsx +1 -1
- package/src/screens/inbox/DialogThreadMessages.tsx +7 -14
- package/src/screens/inbox/DialogThreads.tsx +55 -61
- package/src/screens/inbox/Inbox.tsx +1 -1
- package/src/screens/inbox/components/Actionsheet.tsx +30 -0
- package/src/screens/inbox/components/CachedImage/consts.ts +4 -3
- package/src/screens/inbox/components/CachedImage/index.tsx +232 -61
- package/src/screens/inbox/components/DialogItem.tsx +306 -0
- package/src/screens/inbox/components/DialogsHeader.tsx +6 -13
- package/src/screens/inbox/components/DialogsListItem.tsx +262 -198
- package/src/screens/inbox/components/ExpandableInput.tsx +460 -0
- package/src/screens/inbox/components/ExpandableInputActionSheet.tsx +518 -0
- package/src/screens/inbox/components/GiftedChatInboxComponent.tsx +411 -0
- package/src/screens/inbox/components/ServiceDialogsListItem.tsx +337 -194
- package/src/screens/inbox/components/SlackInput.tsx +23 -0
- package/src/screens/inbox/components/SlackMessageContainer/SlackBubble.tsx +233 -23
- package/src/screens/inbox/components/SlackMessageContainer/SlackMessage.tsx +1 -1
- package/src/screens/inbox/components/SmartLoader.tsx +61 -0
- package/src/screens/inbox/components/SubscriptionHandler.tsx +41 -0
- package/src/screens/inbox/components/SupportServiceDialogsListItem.tsx +53 -55
- package/src/screens/inbox/components/ThreadsViewItem.tsx +178 -285
- package/src/screens/inbox/components/workflow/dialogs-list-item-xstate.ts +145 -0
- package/src/screens/inbox/components/workflow/service-dialogs-list-item-xstate.ts +159 -0
- package/src/screens/inbox/config/config.ts +2 -2
- package/src/screens/inbox/containers/ConversationView.tsx +1843 -702
- package/src/screens/inbox/containers/ConversationView.tsx.bk +1467 -0
- package/src/screens/inbox/containers/Dialogs.tsx +402 -204
- package/src/screens/inbox/containers/SupportServiceDialogs.tsx +4 -4
- package/src/screens/inbox/containers/ThreadConversationView.tsx +1350 -319
- package/src/screens/inbox/containers/ThreadsView.tsx +105 -193
- package/src/screens/inbox/containers/workflow/apollo/handleResult.ts +20 -0
- package/src/screens/inbox/containers/workflow/conversation-xstate.ts +313 -0
- package/src/screens/inbox/containers/workflow/dialogs-xstate.ts +196 -0
- package/src/screens/inbox/containers/workflow/thread-conversation-xstate.ts +401 -0
- package/src/screens/inbox/hooks/useInboxMessages.ts +34 -0
- package/src/screens/inbox/hooks/useSafeDialogThreadsMachine.ts +136 -0
- package/src/screens/inbox/index.ts +37 -0
- package/src/screens/inbox/machines/threadsMachine.ts +147 -0
- package/src/screens/inbox/workflow/dialog-threads-xstate.ts +163 -0
- package/tsconfig.json +11 -54
- package/lib/screens/inbox/components/DialogsListItem.js +0 -171
- package/lib/screens/inbox/components/DialogsListItem.js.map +0 -1
- package/lib/screens/inbox/components/ServiceDialogsListItem.js +0 -171
- package/lib/screens/inbox/components/ServiceDialogsListItem.js.map +0 -1
|
@@ -1,35 +1,47 @@
|
|
|
1
|
-
import React, { useMemo } from 'react';
|
|
1
|
+
import React, { useMemo, useState, useCallback, useRef } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Text,
|
|
4
|
-
Image,
|
|
5
4
|
Pressable,
|
|
6
5
|
HStack,
|
|
7
6
|
Box,
|
|
8
|
-
AvatarGroup,
|
|
9
7
|
Avatar,
|
|
10
8
|
AvatarFallbackText,
|
|
11
9
|
AvatarImage,
|
|
12
|
-
AvatarBadge,
|
|
13
|
-
View,
|
|
14
10
|
} from '@admin-layout/gluestack-ui-mobile';
|
|
15
11
|
import { format, isToday, isYesterday } from 'date-fns';
|
|
16
12
|
import { useFocusEffect } from '@react-navigation/native';
|
|
17
13
|
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/lib/generated/generated.js';
|
|
14
|
+
import { THREAD_CREATED_UPDATED, CHAT_MESSAGE_ADDED, useThreadMessagesQuery } from '../../../queries/inboxQueries';
|
|
25
15
|
import { startCase } from 'lodash-es';
|
|
16
|
+
import colors from 'tailwindcss/colors';
|
|
17
|
+
import { SubscriptionHandler } from './SubscriptionHandler';
|
|
18
|
+
|
|
19
|
+
// Helper function to safely create a Date object
|
|
20
|
+
const safeDate = (dateValue) => {
|
|
21
|
+
if (!dateValue) return null;
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const date = new Date(dateValue);
|
|
25
|
+
// Check if the date is valid by seeing if it returns a valid time value
|
|
26
|
+
if (isNaN(date.getTime())) {
|
|
27
|
+
console.warn('Invalid date value detected:', dateValue);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return date;
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.warn('Error creating date from value:', dateValue, error);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
26
36
|
|
|
27
37
|
const createdAtText = (value: string) => {
|
|
28
38
|
if (!value) return '';
|
|
29
|
-
|
|
39
|
+
const date = safeDate(value);
|
|
40
|
+
if (!date) return '';
|
|
41
|
+
|
|
30
42
|
if (isToday(date)) return 'Today';
|
|
31
43
|
if (isYesterday(date)) return 'Yesterday';
|
|
32
|
-
return format(
|
|
44
|
+
return format(date, 'MMM dd, yyyy');
|
|
33
45
|
};
|
|
34
46
|
|
|
35
47
|
export interface IDialogListChannel extends IChannel {
|
|
@@ -44,8 +56,120 @@ export interface IDialogListItemProps {
|
|
|
44
56
|
onOpen: (id: any, title: any, postParentId?: any) => void;
|
|
45
57
|
refreshing: boolean;
|
|
46
58
|
role: any;
|
|
59
|
+
visible?: boolean;
|
|
47
60
|
}
|
|
48
61
|
|
|
62
|
+
// Helper function to compute the last message from a list of messages
|
|
63
|
+
const computeLastMessage = (threadMessages) => {
|
|
64
|
+
if (!threadMessages || !threadMessages.length) return null;
|
|
65
|
+
|
|
66
|
+
// Extract the post and replies from thread messages
|
|
67
|
+
let posts = [];
|
|
68
|
+
let replies = [];
|
|
69
|
+
|
|
70
|
+
// Helper function to check if a message is an image or contains only image content
|
|
71
|
+
const isTextMessage = (msg) => {
|
|
72
|
+
if (!msg) return false;
|
|
73
|
+
|
|
74
|
+
// If message is empty or only contains image-specific markers, ignore it
|
|
75
|
+
if (!msg.message || msg.message === '') return false;
|
|
76
|
+
|
|
77
|
+
// Check if message looks like an image URL or reference
|
|
78
|
+
const isImageMessage =
|
|
79
|
+
msg.message.includes('<img') ||
|
|
80
|
+
msg.message.includes('[Image]') ||
|
|
81
|
+
msg.message.includes('![') ||
|
|
82
|
+
(/\.(jpeg|jpg|gif|png|bmp|webp)/i.test(msg.message) &&
|
|
83
|
+
(msg.message.includes('http') || msg.message.includes('/images/')));
|
|
84
|
+
|
|
85
|
+
// Return true only for text messages (not image messages)
|
|
86
|
+
return !isImageMessage;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
threadMessages.forEach((thread) => {
|
|
90
|
+
if (thread?.post && isTextMessage(thread.post)) {
|
|
91
|
+
posts.push(thread.post);
|
|
92
|
+
}
|
|
93
|
+
if (thread?.replies?.length) {
|
|
94
|
+
replies = [...replies, ...thread.replies.filter((r) => isTextMessage(r))];
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Combine and sort all messages
|
|
99
|
+
const allMessages = [...posts, ...replies];
|
|
100
|
+
if (!allMessages.length) return null;
|
|
101
|
+
|
|
102
|
+
// Return the most recent message - use safe date comparison
|
|
103
|
+
return allMessages.reduce((a, b) => {
|
|
104
|
+
const dateA = safeDate(a?.createdAt);
|
|
105
|
+
const dateB = safeDate(b?.createdAt);
|
|
106
|
+
|
|
107
|
+
// If either date is invalid, prefer the message with valid date
|
|
108
|
+
if (!dateA) return b;
|
|
109
|
+
if (!dateB) return a;
|
|
110
|
+
|
|
111
|
+
return dateA > dateB ? a : b;
|
|
112
|
+
}, allMessages[0]);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const ServiceChannelWithLastMessage = React.memo(({ channel, lastMessage }: { channel: any; lastMessage: any }) => {
|
|
116
|
+
// Debug output for component rendering
|
|
117
|
+
React.useEffect(() => {
|
|
118
|
+
console.log(`ServiceChannelWithLastMessage rendered for channel ${channel?.id}:`, {
|
|
119
|
+
hasLastMessage: !!lastMessage,
|
|
120
|
+
messageId: lastMessage?.id,
|
|
121
|
+
messageText: lastMessage?.message?.substring(0, 20) + (lastMessage?.message?.length > 20 ? '...' : ''),
|
|
122
|
+
date: lastMessage?.createdAt ? safeDate(lastMessage.createdAt)?.toISOString() : 'none',
|
|
123
|
+
});
|
|
124
|
+
}, [lastMessage, channel?.id]);
|
|
125
|
+
|
|
126
|
+
const count = 30;
|
|
127
|
+
const title = channel?.title.slice(0, count) + (channel?.title.length > count ? '...' : '');
|
|
128
|
+
|
|
129
|
+
// Define message display text
|
|
130
|
+
let displayMessage = 'No messages yet';
|
|
131
|
+
|
|
132
|
+
if (lastMessage) {
|
|
133
|
+
// Check for file attachments
|
|
134
|
+
const hasFileAttachments = lastMessage.files?.data?.length > 0;
|
|
135
|
+
|
|
136
|
+
// Check if the message appears to be an image
|
|
137
|
+
const isImageMessage =
|
|
138
|
+
lastMessage.message?.includes('<img') ||
|
|
139
|
+
lastMessage.message?.includes('[Image]') ||
|
|
140
|
+
lastMessage.message?.includes('![') ||
|
|
141
|
+
(/\.(jpeg|jpg|gif|png|bmp|webp)/i.test(lastMessage.message || '') &&
|
|
142
|
+
((lastMessage.message || '').includes('http') || (lastMessage.message || '').includes('/images/')));
|
|
143
|
+
|
|
144
|
+
if (hasFileAttachments) {
|
|
145
|
+
displayMessage = '📎 File attachment';
|
|
146
|
+
} else if (isImageMessage) {
|
|
147
|
+
displayMessage = '[Image]';
|
|
148
|
+
} else if (lastMessage.message && lastMessage.message.trim() !== '') {
|
|
149
|
+
displayMessage = lastMessage.message;
|
|
150
|
+
} else {
|
|
151
|
+
displayMessage = '(Empty message)';
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<HStack space={'sm'} className="flex-1 justify-between">
|
|
157
|
+
<Box className="flex-[0.8]">
|
|
158
|
+
<Text color={colors.gray[600]} className="text-base text-wrap flex-wrap font-semibold">
|
|
159
|
+
{title}
|
|
160
|
+
</Text>
|
|
161
|
+
<Text color={colors.gray[600]} numberOfLines={1}>
|
|
162
|
+
{displayMessage}
|
|
163
|
+
</Text>
|
|
164
|
+
</Box>
|
|
165
|
+
|
|
166
|
+
<Box className="flex-[0.2]">
|
|
167
|
+
<Text color={colors.gray[500]}>{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}</Text>
|
|
168
|
+
</Box>
|
|
169
|
+
</HStack>
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
|
|
49
173
|
/**
|
|
50
174
|
* TODO:
|
|
51
175
|
* - Get Reservation info: reservation date, status
|
|
@@ -59,10 +183,17 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
|
|
|
59
183
|
onOpen,
|
|
60
184
|
refreshing,
|
|
61
185
|
role,
|
|
186
|
+
visible = true,
|
|
62
187
|
}) {
|
|
63
|
-
const
|
|
188
|
+
const isMountedRef = useRef(true);
|
|
189
|
+
const [subscriptionsTimestamp, setSubscriptionsTimestamp] = useState(Date.now());
|
|
190
|
+
|
|
191
|
+
// Only run queries/subscriptions if visible
|
|
192
|
+
const shouldQuery = !!channel?.id && visible;
|
|
193
|
+
|
|
194
|
+
// Query for thread messages (minimal fields if possible)
|
|
64
195
|
const {
|
|
65
|
-
data:
|
|
196
|
+
data: threadMessagesData,
|
|
66
197
|
loading: threadMessageLoading,
|
|
67
198
|
error,
|
|
68
199
|
refetch: refetchThreadMessages,
|
|
@@ -73,56 +204,41 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
|
|
|
73
204
|
role,
|
|
74
205
|
limit: 2,
|
|
75
206
|
},
|
|
207
|
+
skip: !shouldQuery, // Only run if visible
|
|
76
208
|
fetchPolicy: 'cache-and-network',
|
|
209
|
+
onCompleted: (data) => {
|
|
210
|
+
if (!shouldQuery) return;
|
|
211
|
+
},
|
|
212
|
+
onError: (error) => {
|
|
213
|
+
if (!shouldQuery) return;
|
|
214
|
+
console.error(`Error fetching thread messages for channel ${channel?.id}:`, error);
|
|
215
|
+
},
|
|
77
216
|
});
|
|
78
217
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
// Do something when the screen is focused
|
|
82
|
-
// refetchMessages({ channelId: channel?.id?.toString(), limit: 25 });
|
|
83
|
-
refetchThreadMessages({ channelId: channel?.id?.toString(), role, limit: 2 });
|
|
84
|
-
return () => {
|
|
85
|
-
// Do something when the screen is unfocused
|
|
86
|
-
// Useful for cleanup functions
|
|
87
|
-
};
|
|
88
|
-
}, [refreshing]),
|
|
89
|
-
);
|
|
218
|
+
// Extract threadMessages data for easier access
|
|
219
|
+
const threadMessages = useMemo(() => threadMessagesData?.threadMessages?.data || [], [threadMessagesData]);
|
|
90
220
|
|
|
221
|
+
// Calculate lastMessage directly from threadMessages
|
|
91
222
|
const lastMessage = useMemo(() => {
|
|
92
|
-
if (!
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const { data }: any = threadMessages.threadMessages;
|
|
96
|
-
const replies =
|
|
97
|
-
data
|
|
98
|
-
?.map((t: any) => t?.replies)
|
|
99
|
-
?.flat(1)
|
|
100
|
-
?.filter((p: any) => p?.message !== '') ?? [];
|
|
101
|
-
if (replies?.length) {
|
|
102
|
-
return replies[0];
|
|
103
|
-
// return replies[replies.length - 1];
|
|
104
|
-
} else {
|
|
105
|
-
const post =
|
|
106
|
-
data?.find((t: any) => {
|
|
107
|
-
return t?.post?.message !== '';
|
|
108
|
-
}) ??
|
|
109
|
-
data?.find((t: any) => {
|
|
110
|
-
return t?.post;
|
|
111
|
-
}) ??
|
|
112
|
-
null;
|
|
113
|
-
return post ? post?.post : null;
|
|
114
|
-
}
|
|
115
|
-
}, [threadMessages]);
|
|
223
|
+
if (!shouldQuery) return null;
|
|
224
|
+
return computeLastMessage(threadMessages);
|
|
225
|
+
}, [shouldQuery, threadMessages]);
|
|
116
226
|
|
|
227
|
+
// Calculate servicePostParentId based on lastMessage
|
|
228
|
+
const servicePostParentId = useMemo(() => {
|
|
229
|
+
if (!shouldQuery || !lastMessage) return null;
|
|
230
|
+
return lastMessage?.parentId ? lastMessage?.parentId : lastMessage?.id;
|
|
231
|
+
}, [shouldQuery, lastMessage]);
|
|
232
|
+
|
|
233
|
+
// Handle component unmount
|
|
117
234
|
React.useEffect(() => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}, [lastMessage]);
|
|
235
|
+
return () => {
|
|
236
|
+
isMountedRef.current = false;
|
|
237
|
+
};
|
|
238
|
+
}, []);
|
|
123
239
|
|
|
124
|
-
const creatorAndMembersId =
|
|
125
|
-
if (!channel?.members) return null;
|
|
240
|
+
const creatorAndMembersId = useMemo(() => {
|
|
241
|
+
if (!shouldQuery || !channel?.members) return null;
|
|
126
242
|
const membersIds: any =
|
|
127
243
|
channel?.members
|
|
128
244
|
?.filter((m: any) => m !== null && m?.user?.id !== currentUser?.id)
|
|
@@ -130,67 +246,64 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
|
|
|
130
246
|
const creatorId: any = channel?.creator?.id;
|
|
131
247
|
const mergedIds: any = [].concat(membersIds, creatorId) ?? [];
|
|
132
248
|
return mergedIds?.filter((m: any, pos: any) => mergedIds?.indexOf(m) === pos) ?? [];
|
|
133
|
-
}, [channel]);
|
|
134
|
-
|
|
135
|
-
const postParentId = React.useMemo(() => {
|
|
136
|
-
if (!creatorAndMembersId?.length) return null;
|
|
249
|
+
}, [shouldQuery, channel, currentUser]);
|
|
137
250
|
|
|
251
|
+
const postParentId = useMemo(() => {
|
|
252
|
+
if (!shouldQuery || !creatorAndMembersId?.length) return null;
|
|
138
253
|
return creatorAndMembersId?.length && creatorAndMembersId?.includes(currentUser?.id)
|
|
139
254
|
? null
|
|
140
255
|
: lastMessage?.parentId
|
|
141
256
|
? lastMessage?.parentId
|
|
142
257
|
: lastMessage?.id ?? 0;
|
|
143
|
-
}, [creatorAndMembersId, lastMessage]);
|
|
144
|
-
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
258
|
+
}, [shouldQuery, creatorAndMembersId, lastMessage, currentUser]);
|
|
259
|
+
|
|
260
|
+
// Function to force refresh the component state
|
|
261
|
+
const refreshThreadState = useCallback(() => {
|
|
262
|
+
if (shouldQuery && channel?.id && isMountedRef.current) {
|
|
263
|
+
refetchThreadMessages({
|
|
264
|
+
channelId: channel?.id?.toString(),
|
|
265
|
+
role,
|
|
266
|
+
limit: 2,
|
|
267
|
+
}).catch((err) => {
|
|
268
|
+
console.error('Error refreshing thread state:', err);
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}, [shouldQuery, channel?.id, refetchThreadMessages, role]);
|
|
272
|
+
|
|
273
|
+
// Reset subscriptions when the component gains focus
|
|
274
|
+
useFocusEffect(
|
|
275
|
+
React.useCallback(() => {
|
|
276
|
+
if (!shouldQuery || !channel?.id) return;
|
|
277
|
+
setSubscriptionsTimestamp(Date.now());
|
|
278
|
+
const fetchFreshData = async () => {
|
|
279
|
+
try {
|
|
280
|
+
await refetchThreadMessages({
|
|
281
|
+
channelId: channel?.id?.toString(),
|
|
282
|
+
role,
|
|
283
|
+
limit: 2,
|
|
284
|
+
});
|
|
285
|
+
} catch (error) {
|
|
286
|
+
console.error('Error refetching thread messages on focus:', error);
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
fetchFreshData();
|
|
290
|
+
return () => {};
|
|
291
|
+
}, [shouldQuery, refreshing, channel?.id, refetchThreadMessages, role]),
|
|
292
|
+
);
|
|
162
293
|
|
|
163
294
|
return (
|
|
164
295
|
<Pressable
|
|
165
|
-
onPress={() => channel?.id !== selectedChannelId && onOpen(channel?.id, channel?.title, postParentId)}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
flex={1}
|
|
170
|
-
$dark-borderColor="$coolGray600"
|
|
171
|
-
$dark-backgroundColor="$trueGray700"
|
|
172
|
-
$light-backgroundColor="$trueGray50"
|
|
173
|
-
$light-borderColor="$trueGray200"
|
|
296
|
+
// onPress={() => channel?.id !== selectedChannelId && onOpen(channel?.id, channel?.title, postParentId)}
|
|
297
|
+
onPress={() => onOpen(channel?.id, channel?.title, postParentId)}
|
|
298
|
+
className="flex-1 rounded-md border-gray-200 dark:border-gray-600 dark:bg-gray-700"
|
|
299
|
+
style={{ borderBottomWidth: 1, borderColor: '#e5e7eb', marginVertical: 0 }}
|
|
174
300
|
>
|
|
175
|
-
<HStack
|
|
176
|
-
|
|
177
|
-
// pl={3}
|
|
178
|
-
py={'$3'}
|
|
179
|
-
space={'sm'}
|
|
180
|
-
w={'100%'}
|
|
181
|
-
flex={1}
|
|
182
|
-
// direction={'row'}
|
|
183
|
-
justifyContent={'space-between'}
|
|
184
|
-
alignItems={'center'}
|
|
185
|
-
>
|
|
186
|
-
<Box flex={0.1} alignItems={'flex-start'} pl={'$3'}>
|
|
301
|
+
<HStack space={'md'} className="flex-1 w-[100%] px-2 py-3 items-center">
|
|
302
|
+
<Box className="flex-[0.1] items-start pl-3">
|
|
187
303
|
<Avatar
|
|
188
304
|
key={'service-channels-key-' + channel?.id}
|
|
189
|
-
bg={'transparent'}
|
|
190
305
|
size={'sm'}
|
|
191
|
-
top
|
|
192
|
-
right={'$0'}
|
|
193
|
-
zIndex={1}
|
|
306
|
+
className="bg-transparent top-0 right-0 z-[1]"
|
|
194
307
|
>
|
|
195
308
|
<AvatarFallbackText> {startCase(channel?.creator?.username?.charAt(0))}</AvatarFallbackText>
|
|
196
309
|
<AvatarImage
|
|
@@ -202,102 +315,132 @@ export const ServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = f
|
|
|
202
315
|
/>
|
|
203
316
|
</Avatar>
|
|
204
317
|
</Box>
|
|
205
|
-
<Box flex
|
|
318
|
+
<Box className="flex-1">
|
|
206
319
|
<ServiceChannelWithLastMessage
|
|
320
|
+
key={`channel-${channel?.id}-${subscriptionsTimestamp}`}
|
|
207
321
|
channel={channel}
|
|
208
322
|
lastMessage={lastMessage}
|
|
209
|
-
subscribeToNewMessages={() =>
|
|
210
|
-
subscribeToMore({
|
|
211
|
-
document: THREAD_CHAT_ADDED,
|
|
212
|
-
variables: {
|
|
213
|
-
channelId: channel?.id?.toString(),
|
|
214
|
-
postParentId: postParentId ? servicePostParentId : null,
|
|
215
|
-
},
|
|
216
|
-
updateQuery: (prev, { subscriptionData }: any) => {
|
|
217
|
-
if (!subscriptionData.data) return prev;
|
|
218
|
-
|
|
219
|
-
const newPostThreadData: any = subscriptionData?.data?.threadCreatedUpdated?.data;
|
|
220
|
-
const newMessage: any = subscriptionData?.data?.threadCreatedUpdated?.lastMessage;
|
|
221
|
-
const data =
|
|
222
|
-
prev?.threadMessages?.data?.map((t: any) =>
|
|
223
|
-
t.id === newPostThreadData?.id
|
|
224
|
-
? {
|
|
225
|
-
...t,
|
|
226
|
-
replies: [...t?.replies, newMessage],
|
|
227
|
-
replyCount: newPostThreadData?.replyCount,
|
|
228
|
-
lastReplyAt: newPostThreadData?.lastReplyAt,
|
|
229
|
-
updatedAt: newPostThreadData?.updatedAt,
|
|
230
|
-
}
|
|
231
|
-
: t,
|
|
232
|
-
) ?? [];
|
|
233
|
-
|
|
234
|
-
return Object.assign({}, prev, {
|
|
235
|
-
threadMessages: {
|
|
236
|
-
...prev?.threadMessages,
|
|
237
|
-
totalCount: newPostThreadData?.totalCount ?? 0,
|
|
238
|
-
data: data?.length > 0 ? data : [newPostThreadData],
|
|
239
|
-
// totalCount: prev?.threadMessages?.totalCount + 1,
|
|
240
|
-
//data: data,
|
|
241
|
-
},
|
|
242
|
-
});
|
|
243
|
-
},
|
|
244
|
-
})
|
|
245
|
-
}
|
|
246
323
|
/>
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
324
|
+
|
|
325
|
+
{/* Subscription handlers for real-time updates */}
|
|
326
|
+
{shouldQuery && (
|
|
327
|
+
<>
|
|
328
|
+
<SubscriptionHandler
|
|
329
|
+
subscribeToMore={subscribeToMore}
|
|
330
|
+
document={THREAD_CREATED_UPDATED}
|
|
331
|
+
variables={{
|
|
332
|
+
channelId: channel?.id?.toString(),
|
|
333
|
+
postParentId: postParentId || servicePostParentId || null,
|
|
334
|
+
}}
|
|
335
|
+
updateQuery={(prev, { subscriptionData }: any) => {
|
|
336
|
+
if (!subscriptionData?.data) {
|
|
337
|
+
return prev;
|
|
338
|
+
}
|
|
339
|
+
try {
|
|
340
|
+
const newPostThreadData: any =
|
|
341
|
+
subscriptionData?.data?.threadCreatedUpdated?.data;
|
|
342
|
+
const newMessage: any =
|
|
343
|
+
subscriptionData?.data?.threadCreatedUpdated?.lastMessage;
|
|
344
|
+
if (!newPostThreadData || !newMessage) {
|
|
345
|
+
return prev;
|
|
346
|
+
}
|
|
347
|
+
const prevThreads = prev?.threadMessages?.data || [];
|
|
348
|
+
const threadExists = prevThreads.some(
|
|
349
|
+
(t: any) => t.id === newPostThreadData?.id,
|
|
350
|
+
);
|
|
351
|
+
let updatedThreads;
|
|
352
|
+
if (threadExists) {
|
|
353
|
+
updatedThreads = prevThreads.map((t: any) =>
|
|
354
|
+
t.id === newPostThreadData?.id
|
|
355
|
+
? {
|
|
356
|
+
...t,
|
|
357
|
+
replies: [...(t?.replies || []), newMessage],
|
|
358
|
+
replyCount: newPostThreadData?.replyCount,
|
|
359
|
+
lastReplyAt: newPostThreadData?.lastReplyAt,
|
|
360
|
+
updatedAt: newPostThreadData?.updatedAt,
|
|
361
|
+
}
|
|
362
|
+
: t,
|
|
363
|
+
);
|
|
364
|
+
} else {
|
|
365
|
+
updatedThreads = [...prevThreads, newPostThreadData];
|
|
366
|
+
}
|
|
367
|
+
return {
|
|
368
|
+
...prev,
|
|
369
|
+
threadMessages: {
|
|
370
|
+
...prev?.threadMessages,
|
|
371
|
+
totalCount:
|
|
372
|
+
newPostThreadData?.totalCount ??
|
|
373
|
+
prev?.threadMessages?.totalCount ??
|
|
374
|
+
0,
|
|
375
|
+
data: updatedThreads,
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
} catch (error) {
|
|
379
|
+
console.error(`Thread subscription error for channel ${channel?.id}:`, error);
|
|
380
|
+
return prev;
|
|
381
|
+
}
|
|
382
|
+
}}
|
|
383
|
+
/>
|
|
384
|
+
<SubscriptionHandler
|
|
385
|
+
subscribeToMore={subscribeToMore}
|
|
386
|
+
document={CHAT_MESSAGE_ADDED}
|
|
387
|
+
variables={{
|
|
388
|
+
channelId: channel?.id?.toString(),
|
|
389
|
+
postParentId: postParentId || servicePostParentId || null,
|
|
390
|
+
}}
|
|
391
|
+
updateQuery={(prev, { subscriptionData }: any) => {
|
|
392
|
+
if (!subscriptionData?.data) {
|
|
393
|
+
return prev;
|
|
394
|
+
}
|
|
395
|
+
try {
|
|
396
|
+
const newMessage = subscriptionData?.data?.chatMessageAdded;
|
|
397
|
+
if (!newMessage) {
|
|
398
|
+
return prev;
|
|
399
|
+
}
|
|
400
|
+
const prevThreads = prev?.threadMessages?.data || [];
|
|
401
|
+
const threadIndex = prevThreads.findIndex((t: any) => {
|
|
402
|
+
if (newMessage.parentId && t.post?.id === newMessage.parentId) {
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
if (t.replies && t.replies.some((r: any) => r.id === newMessage.parentId)) {
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
408
|
+
return false;
|
|
409
|
+
});
|
|
410
|
+
if (threadIndex === -1) {
|
|
411
|
+
return prev;
|
|
412
|
+
}
|
|
413
|
+
const updatedThreads = [...prevThreads];
|
|
414
|
+
const thread = { ...updatedThreads[threadIndex] };
|
|
415
|
+
thread.replies = [...(thread.replies || []), newMessage];
|
|
416
|
+
const currentReplyCount =
|
|
417
|
+
typeof thread.replyCount === 'number' ? thread.replyCount : 0;
|
|
418
|
+
thread.replyCount = currentReplyCount + 1;
|
|
419
|
+
thread.lastReplyAt = newMessage.createdAt;
|
|
420
|
+
thread.updatedAt = newMessage.createdAt;
|
|
421
|
+
updatedThreads[threadIndex] = thread;
|
|
422
|
+
return {
|
|
423
|
+
...prev,
|
|
424
|
+
threadMessages: {
|
|
425
|
+
...prev?.threadMessages,
|
|
426
|
+
data: updatedThreads,
|
|
427
|
+
},
|
|
428
|
+
};
|
|
429
|
+
} catch (error) {
|
|
430
|
+
console.error(
|
|
431
|
+
`Chat message subscription error for channel ${channel?.id}:`,
|
|
432
|
+
error,
|
|
433
|
+
);
|
|
434
|
+
return prev;
|
|
435
|
+
}
|
|
436
|
+
}}
|
|
437
|
+
/>
|
|
438
|
+
</>
|
|
439
|
+
)}
|
|
262
440
|
</Box>
|
|
263
|
-
{/* <Text flex={0.2} color="gray.500">
|
|
264
|
-
{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}
|
|
265
|
-
</Text> */}
|
|
266
441
|
</HStack>
|
|
267
442
|
</Pressable>
|
|
268
443
|
);
|
|
269
444
|
};
|
|
270
445
|
|
|
271
|
-
const ServiceChannelWithLastMessage = React.memo(({ channel, lastMessage, subscribeToNewMessages }: any) => {
|
|
272
|
-
React.useEffect(() => subscribeToNewMessages(), []);
|
|
273
|
-
return (
|
|
274
|
-
<HStack space={'sm'} flex={1} justifyContent={'center'} alignItems={'center'}>
|
|
275
|
-
<Box flex={0.8}>
|
|
276
|
-
<Text color="$trueGray600" fontSize="$lg" flexWrap={'wrap'} fontWeight="$semibold">
|
|
277
|
-
{channel?.title}
|
|
278
|
-
</Text>
|
|
279
|
-
<Text color="$trueGray600" numberOfLines={1}>
|
|
280
|
-
{/* {creatorAndMembersId?.length && creatorAndMembersId?.includes(currentUser?.id)
|
|
281
|
-
? lastMessageCreatorAndMembers?.message ?? ''
|
|
282
|
-
: lastMessage?.message ?? ''} */}
|
|
283
|
-
{lastMessage?.message ?? ''}
|
|
284
|
-
</Text>
|
|
285
|
-
</Box>
|
|
286
|
-
|
|
287
|
-
<Box flex={0.2}>
|
|
288
|
-
{/* {creatorAndMembersId?.length && creatorAndMembersId?.includes(currentUser?.id) ? (
|
|
289
|
-
<Text color="gray.500">
|
|
290
|
-
{lastMessageCreatorAndMembers
|
|
291
|
-
? createdAtText(lastMessageCreatorAndMembers?.createdAt)
|
|
292
|
-
: ''}
|
|
293
|
-
</Text>
|
|
294
|
-
) : (
|
|
295
|
-
<Text color="gray.500">{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}</Text>
|
|
296
|
-
)} */}
|
|
297
|
-
<Text color="$trueGray500">{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}</Text>
|
|
298
|
-
</Box>
|
|
299
|
-
</HStack>
|
|
300
|
-
);
|
|
301
|
-
});
|
|
302
|
-
|
|
303
446
|
export const ServiceDialogsListItem = React.memo(ServiceDialogsListItemComponent);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TextInput, TextInputProps } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export const SlackInput = React.forwardRef<TextInput, TextInputProps>((props, ref) => {
|
|
5
|
+
return (
|
|
6
|
+
<TextInput
|
|
7
|
+
ref={ref}
|
|
8
|
+
{...props}
|
|
9
|
+
style={[
|
|
10
|
+
{
|
|
11
|
+
flex: 1,
|
|
12
|
+
fontSize: 16,
|
|
13
|
+
padding: 10,
|
|
14
|
+
borderRadius: 8,
|
|
15
|
+
backgroundColor: '#f8f8f8',
|
|
16
|
+
borderColor: '#e0e0e0',
|
|
17
|
+
borderWidth: 1,
|
|
18
|
+
},
|
|
19
|
+
props.style,
|
|
20
|
+
]}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
});
|