@messenger-box/platform-mobile 10.0.3-alpha.5 → 10.0.3-alpha.50
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 +92 -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
|
@@ -56,45 +56,255 @@ export default class Bubble extends React.Component<any> {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
renderMessageImage() {
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
const { currentMessage, containerStyle, wrapperStyle, ...messageImageProps } = this.props;
|
|
60
|
+
|
|
61
|
+
// Check if we have multiple images
|
|
62
|
+
if (currentMessage.images && Array.isArray(currentMessage.images) && currentMessage.images.length > 0) {
|
|
63
|
+
// Filter out any invalid or empty image URLs first
|
|
64
|
+
const validImages = currentMessage.images.filter((url) => url && typeof url === 'string');
|
|
65
|
+
|
|
66
|
+
// If no valid images after filtering, don't render anything
|
|
67
|
+
if (validImages.length === 0) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Render multiple images in a scrollable horizontal row
|
|
72
|
+
return (
|
|
73
|
+
<View style={{ marginVertical: 0 }}>
|
|
74
|
+
<View
|
|
75
|
+
style={{
|
|
76
|
+
flexDirection: 'row',
|
|
77
|
+
flexWrap: 'wrap',
|
|
78
|
+
gap: 8,
|
|
79
|
+
}}
|
|
80
|
+
>
|
|
81
|
+
{validImages.map((imageUrl, index) => {
|
|
82
|
+
// Check if this is a local image
|
|
83
|
+
const isLocalImage =
|
|
84
|
+
imageUrl.startsWith('file:') ||
|
|
85
|
+
imageUrl.startsWith('data:') ||
|
|
86
|
+
imageUrl.startsWith('content:') ||
|
|
87
|
+
(!imageUrl.startsWith('http://') && !imageUrl.startsWith('https://'));
|
|
88
|
+
|
|
89
|
+
const imageSize =
|
|
90
|
+
validImages.length === 1
|
|
91
|
+
? { width: windowWidth * 0.6, height: windowWidth * 0.4 }
|
|
92
|
+
: { width: windowWidth * 0.3, height: windowWidth * 0.3 };
|
|
93
|
+
|
|
94
|
+
// Show upload indicator if this message is uploading
|
|
95
|
+
const isUploading = currentMessage.isUploading;
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<TouchableHighlight
|
|
99
|
+
key={`image-${index}-${currentMessage._id}`}
|
|
100
|
+
underlayColor={'transparent'}
|
|
101
|
+
onPress={() => {
|
|
102
|
+
// Don't allow viewing while uploading
|
|
103
|
+
if (isUploading) return;
|
|
104
|
+
|
|
105
|
+
// Create a message-like object with this image for the viewer
|
|
106
|
+
const imageObject = {
|
|
107
|
+
_id: `${currentMessage._id}-img-${index}`,
|
|
108
|
+
image: imageUrl,
|
|
109
|
+
};
|
|
110
|
+
this.props.setImageViewer(imageObject, true);
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
<View
|
|
114
|
+
style={{
|
|
115
|
+
...imageSize,
|
|
116
|
+
maxHeight: 200,
|
|
117
|
+
borderRadius: 8,
|
|
118
|
+
overflow: 'hidden',
|
|
119
|
+
borderWidth: 1,
|
|
120
|
+
borderColor: '#e0e0e0',
|
|
121
|
+
backgroundColor: '#f7f7f7',
|
|
122
|
+
}}
|
|
123
|
+
>
|
|
124
|
+
{isLocalImage ? (
|
|
125
|
+
// For local images, use direct Image component with no loading state
|
|
126
|
+
<View style={{ width: '100%', height: '100%' }}>
|
|
127
|
+
<MessageImage
|
|
128
|
+
{...messageImageProps}
|
|
129
|
+
currentMessage={{ ...currentMessage, image: imageUrl }}
|
|
130
|
+
imageStyle={{
|
|
131
|
+
width: '100%',
|
|
132
|
+
height: '100%',
|
|
133
|
+
borderRadius: 8,
|
|
134
|
+
}}
|
|
135
|
+
/>
|
|
136
|
+
|
|
137
|
+
{/* Show upload indicator if needed */}
|
|
138
|
+
{isUploading && (
|
|
139
|
+
<View
|
|
140
|
+
style={{
|
|
141
|
+
position: 'absolute',
|
|
142
|
+
top: 0,
|
|
143
|
+
left: 0,
|
|
144
|
+
right: 0,
|
|
145
|
+
bottom: 0,
|
|
146
|
+
backgroundColor: 'rgba(0,0,0,0.2)',
|
|
147
|
+
justifyContent: 'center',
|
|
148
|
+
alignItems: 'center',
|
|
149
|
+
}}
|
|
150
|
+
>
|
|
151
|
+
<Text style={{ color: 'white', fontWeight: 'bold' }}>
|
|
152
|
+
Uploading...
|
|
153
|
+
</Text>
|
|
154
|
+
</View>
|
|
155
|
+
)}
|
|
156
|
+
</View>
|
|
157
|
+
) : (
|
|
158
|
+
// For remote images, use CachedImage with loading placeholder
|
|
159
|
+
<CachedImage
|
|
160
|
+
style={[styles.slackImage, { width: '100%', height: '100%' }]}
|
|
161
|
+
cacheKey={`${currentMessage._id}-img-${index}-slack-bubble-imageKey`}
|
|
162
|
+
source={{
|
|
163
|
+
uri: imageUrl,
|
|
164
|
+
expiresIn: 86400,
|
|
165
|
+
}}
|
|
166
|
+
resizeMode={'cover'}
|
|
167
|
+
alt={'image'}
|
|
168
|
+
placeholderContent={
|
|
169
|
+
<View
|
|
170
|
+
style={[
|
|
171
|
+
styles.slackImage,
|
|
172
|
+
{
|
|
173
|
+
width: '100%',
|
|
174
|
+
height: '100%',
|
|
175
|
+
backgroundColor: '#e1e1e1',
|
|
176
|
+
justifyContent: 'center',
|
|
177
|
+
alignItems: 'center',
|
|
178
|
+
borderRadius: 8,
|
|
179
|
+
},
|
|
180
|
+
]}
|
|
181
|
+
>
|
|
182
|
+
<Text>{isUploading ? 'Uploading...' : 'Loading...'}</Text>
|
|
183
|
+
</View>
|
|
184
|
+
}
|
|
185
|
+
/>
|
|
186
|
+
)}
|
|
187
|
+
</View>
|
|
188
|
+
</TouchableHighlight>
|
|
189
|
+
);
|
|
190
|
+
})}
|
|
191
|
+
</View>
|
|
192
|
+
</View>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Fall back to single image rendering if no images array
|
|
197
|
+
if (currentMessage.image) {
|
|
61
198
|
if (this.props.renderMessageImage) {
|
|
62
199
|
return this.props.renderMessageImage(messageImageProps);
|
|
63
200
|
}
|
|
64
|
-
const { image, _id } =
|
|
201
|
+
const { image, _id } = currentMessage;
|
|
202
|
+
|
|
203
|
+
// Add validation for image URL
|
|
204
|
+
if (!image || typeof image !== 'string') {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
65
207
|
|
|
208
|
+
// Check if this is a local image (from device camera/library)
|
|
209
|
+
const isLocalImage =
|
|
210
|
+
image.startsWith('file:') ||
|
|
211
|
+
image.startsWith('data:') ||
|
|
212
|
+
image.startsWith('content:') ||
|
|
213
|
+
// Check if the image is a pendingUpload by checking if it doesn't start with http/https
|
|
214
|
+
(!image.startsWith('http://') && !image.startsWith('https://'));
|
|
215
|
+
|
|
216
|
+
// Check uploading state
|
|
217
|
+
const isUploading = currentMessage.isUploading;
|
|
218
|
+
|
|
219
|
+
// Class components can't use hooks like useMemo
|
|
220
|
+
// Directly render the CachedImage instead
|
|
66
221
|
return (
|
|
67
222
|
<TouchableHighlight
|
|
68
|
-
// underlayColor={'#c0c0c0'}
|
|
69
223
|
underlayColor={'transparent'}
|
|
70
|
-
style={{ width: '100%' }}
|
|
71
|
-
onPress={() =>
|
|
224
|
+
style={{ width: '100%', marginVertical: 0 }}
|
|
225
|
+
onPress={() => {
|
|
226
|
+
// Don't allow viewing while uploading
|
|
227
|
+
if (isUploading) return;
|
|
228
|
+
this.props.setImageViewer(currentMessage, true);
|
|
229
|
+
}}
|
|
72
230
|
>
|
|
73
231
|
<View
|
|
74
232
|
style={{
|
|
75
|
-
width: windowWidth
|
|
76
|
-
height:
|
|
233
|
+
width: windowWidth * 0.6, // 60% of screen width
|
|
234
|
+
height: windowWidth * 0.4, // Maintain aspect ratio
|
|
235
|
+
maxHeight: 200,
|
|
236
|
+
borderRadius: 8,
|
|
237
|
+
overflow: 'hidden',
|
|
238
|
+
borderWidth: 1,
|
|
239
|
+
borderColor: '#e0e0e0',
|
|
240
|
+
backgroundColor: '#f7f7f7',
|
|
77
241
|
}}
|
|
78
242
|
>
|
|
79
|
-
{
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
243
|
+
{isLocalImage ? (
|
|
244
|
+
// For local images, use direct Image component with no loading state
|
|
245
|
+
<View style={{ width: '100%', height: '100%', position: 'relative' }}>
|
|
246
|
+
<MessageImage
|
|
247
|
+
{...messageImageProps}
|
|
248
|
+
imageStyle={{
|
|
249
|
+
width: '100%',
|
|
250
|
+
height: '100%',
|
|
251
|
+
borderRadius: 8,
|
|
252
|
+
}}
|
|
253
|
+
/>
|
|
254
|
+
|
|
255
|
+
{/* Show upload indicator if needed */}
|
|
256
|
+
{isUploading && (
|
|
257
|
+
<View
|
|
258
|
+
style={{
|
|
259
|
+
position: 'absolute',
|
|
260
|
+
top: 0,
|
|
261
|
+
left: 0,
|
|
262
|
+
right: 0,
|
|
263
|
+
bottom: 0,
|
|
264
|
+
backgroundColor: 'rgba(0,0,0,0.2)',
|
|
265
|
+
justifyContent: 'center',
|
|
266
|
+
alignItems: 'center',
|
|
267
|
+
}}
|
|
268
|
+
>
|
|
269
|
+
<Text style={{ color: 'white', fontWeight: 'bold' }}>Uploading...</Text>
|
|
270
|
+
</View>
|
|
271
|
+
)}
|
|
272
|
+
</View>
|
|
273
|
+
) : (
|
|
274
|
+
// For remote images, use CachedImage with loading placeholder
|
|
275
|
+
<CachedImage
|
|
276
|
+
style={[styles.slackImage, { width: '100%', height: '100%' }]}
|
|
277
|
+
cacheKey={`${_id}-slack-bubble-imageKey`}
|
|
278
|
+
source={{
|
|
279
|
+
uri: image,
|
|
280
|
+
expiresIn: 86400,
|
|
281
|
+
}}
|
|
282
|
+
resizeMode={'cover'}
|
|
283
|
+
alt={'image'}
|
|
284
|
+
placeholderContent={
|
|
285
|
+
<View
|
|
286
|
+
style={[
|
|
287
|
+
styles.slackImage,
|
|
288
|
+
{
|
|
289
|
+
width: '100%',
|
|
290
|
+
height: '100%',
|
|
291
|
+
backgroundColor: '#e1e1e1',
|
|
292
|
+
justifyContent: 'center',
|
|
293
|
+
alignItems: 'center',
|
|
294
|
+
borderRadius: 8,
|
|
295
|
+
},
|
|
296
|
+
]}
|
|
297
|
+
>
|
|
298
|
+
<Text>{isUploading ? 'Uploading...' : 'Loading...'}</Text>
|
|
299
|
+
</View>
|
|
300
|
+
}
|
|
301
|
+
/>
|
|
302
|
+
)}
|
|
94
303
|
</View>
|
|
95
304
|
</TouchableHighlight>
|
|
96
305
|
);
|
|
97
306
|
}
|
|
307
|
+
|
|
98
308
|
return null;
|
|
99
309
|
}
|
|
100
310
|
|
|
@@ -74,7 +74,7 @@ export default class Message extends React.Component<any> {
|
|
|
74
74
|
const avatarProps: any = this.getInnerComponentProps();
|
|
75
75
|
|
|
76
76
|
return (
|
|
77
|
-
<Avatar size={'sm'} bg
|
|
77
|
+
<Avatar size={'sm'} className="bg-transparent rounded-none mr-2">
|
|
78
78
|
<AvatarFallbackText>
|
|
79
79
|
{isSameUserAndSameDay ? '' : avatarProps?.currentMessage?.user?.name[0]}
|
|
80
80
|
</AvatarFallbackText>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { Box, Spinner, Text, VStack, Button, ButtonText } from '@admin-layout/gluestack-ui-mobile';
|
|
3
|
+
import colors from 'tailwindcss/colors';
|
|
4
|
+
|
|
5
|
+
interface SmartLoaderProps {
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
timeoutMs?: number;
|
|
8
|
+
message?: string;
|
|
9
|
+
onRetry?: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* SmartLoader component that automatically shows a timeout message
|
|
14
|
+
* if loading takes too long, preventing infinite loading states
|
|
15
|
+
*/
|
|
16
|
+
export const SmartLoader: React.FC<SmartLoaderProps> = ({
|
|
17
|
+
isLoading,
|
|
18
|
+
timeoutMs = 20000, // Default 20 seconds
|
|
19
|
+
message = 'Taking longer than expected...',
|
|
20
|
+
onRetry,
|
|
21
|
+
}) => {
|
|
22
|
+
const [showTimeout, setShowTimeout] = useState(false);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!isLoading) {
|
|
26
|
+
setShowTimeout(false);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const timeoutId = setTimeout(() => {
|
|
31
|
+
if (isLoading) {
|
|
32
|
+
setShowTimeout(true);
|
|
33
|
+
}
|
|
34
|
+
}, timeoutMs);
|
|
35
|
+
|
|
36
|
+
return () => clearTimeout(timeoutId);
|
|
37
|
+
}, [isLoading, timeoutMs]);
|
|
38
|
+
|
|
39
|
+
if (!isLoading) return null;
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Box className="flex-1 bg-gray-100 dark:bg-gray-800 items-center justify-center">
|
|
43
|
+
<VStack space="md" className="items-center">
|
|
44
|
+
<Spinner color={colors.blue[500]} size="large" />
|
|
45
|
+
|
|
46
|
+
{showTimeout && (
|
|
47
|
+
<VStack space="sm" className="p-4 items-center">
|
|
48
|
+
<Text className="text-gray-700 dark:text-gray-300 text-center">{message}</Text>
|
|
49
|
+
{onRetry && (
|
|
50
|
+
<Button onPress={onRetry} className="bg-blue-500 mt-3 rounded-full">
|
|
51
|
+
<ButtonText>Try Again</ButtonText>
|
|
52
|
+
</Button>
|
|
53
|
+
)}
|
|
54
|
+
</VStack>
|
|
55
|
+
)}
|
|
56
|
+
</VStack>
|
|
57
|
+
</Box>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export default SmartLoader;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared SubscriptionHandler for Apollo subscribeToMore
|
|
5
|
+
*
|
|
6
|
+
* @param subscribeToMore - Apollo subscribeToMore function
|
|
7
|
+
* @param document - GraphQL subscription document
|
|
8
|
+
* @param variables - Variables for the subscription
|
|
9
|
+
* @param updateQuery - Apollo updateQuery function
|
|
10
|
+
* @param onError - Optional error handler
|
|
11
|
+
* @param enabled - If false, disables the subscription
|
|
12
|
+
*/
|
|
13
|
+
export function SubscriptionHandler({
|
|
14
|
+
subscribeToMore,
|
|
15
|
+
document,
|
|
16
|
+
variables,
|
|
17
|
+
updateQuery,
|
|
18
|
+
onError,
|
|
19
|
+
enabled = true,
|
|
20
|
+
}: {
|
|
21
|
+
subscribeToMore: Function;
|
|
22
|
+
document: any;
|
|
23
|
+
variables: Record<string, any>;
|
|
24
|
+
updateQuery: (prev: any, { subscriptionData }: any) => any;
|
|
25
|
+
onError?: (error: any) => void;
|
|
26
|
+
enabled?: boolean;
|
|
27
|
+
}) {
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!enabled) return;
|
|
30
|
+
const unsubscribe = subscribeToMore({
|
|
31
|
+
document,
|
|
32
|
+
variables,
|
|
33
|
+
updateQuery,
|
|
34
|
+
onError,
|
|
35
|
+
});
|
|
36
|
+
return () => {
|
|
37
|
+
if (unsubscribe && typeof unsubscribe === 'function') unsubscribe();
|
|
38
|
+
};
|
|
39
|
+
}, [subscribeToMore, document, JSON.stringify(variables), updateQuery, onError, enabled]);
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
@@ -16,22 +16,35 @@ import { format, isToday, isYesterday } from 'date-fns';
|
|
|
16
16
|
import { useFocusEffect } from '@react-navigation/native';
|
|
17
17
|
import { IChannel, IUserAccount } from 'common';
|
|
18
18
|
import {
|
|
19
|
+
THREAD_CREATED_UPDATED,
|
|
20
|
+
CHAT_MESSAGE_ADDED,
|
|
19
21
|
useThreadMessagesQuery,
|
|
22
|
+
useThreadCreatedUpdatedSubscription,
|
|
23
|
+
useThreadChatMessageAddedSubscription,
|
|
20
24
|
useMessagesQuery,
|
|
21
|
-
|
|
22
|
-
useOnThreadCreatedUpdatedSubscription,
|
|
23
|
-
useOnThreadChatMessageAddedSubscription,
|
|
24
|
-
OnThreadCreatedUpdatedDocument as THREAD_CHAT_ADDED,
|
|
25
|
-
OnThreadChatMessageAddedDocument as CHAT_MESSAGE_ADDED,
|
|
26
|
-
} from 'common/lib/generated/generated.js';
|
|
25
|
+
} from '../../../queries/inboxQueries';
|
|
27
26
|
import { startCase } from 'lodash-es';
|
|
27
|
+
import colors from 'tailwindcss/colors';
|
|
28
28
|
|
|
29
29
|
const createdAtText = (value: string) => {
|
|
30
30
|
if (!value) return '';
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
// Validate the date before processing
|
|
34
|
+
const timestamp = new Date(value).getTime();
|
|
35
|
+
if (isNaN(timestamp)) {
|
|
36
|
+
console.warn(`Invalid date value in createdAtText: ${value}`);
|
|
37
|
+
return 'Unknown date';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let date = new Date(value);
|
|
41
|
+
if (isToday(date)) return 'Today';
|
|
42
|
+
if (isYesterday(date)) return 'Yesterday';
|
|
43
|
+
return format(date, 'MMM dd, yyyy');
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(`Error processing date in createdAtText: ${value}`, error);
|
|
46
|
+
return 'Unknown date';
|
|
47
|
+
}
|
|
35
48
|
};
|
|
36
49
|
|
|
37
50
|
export interface IDialogListChannel extends IChannel {
|
|
@@ -165,34 +178,14 @@ export const SupportServiceDialogsListItemComponent: React.FC<IDialogListItemPro
|
|
|
165
178
|
return (
|
|
166
179
|
<Pressable
|
|
167
180
|
onPress={() => channel?.id !== selectedChannelId && onOpen(channel?.id, channel?.title, postParentId)}
|
|
168
|
-
|
|
169
|
-
borderRadius={'$md'}
|
|
170
|
-
borderColor={'$trueGray200'}
|
|
171
|
-
flex={1}
|
|
172
|
-
$dark-borderColor="$coolGray600"
|
|
173
|
-
$dark-backgroundColor="$trueGray700"
|
|
174
|
-
$light-backgroundColor="$trueGray50"
|
|
175
|
-
$light-borderColor="$trueGray200"
|
|
181
|
+
className="flex-1 border rounded-md border-gray-200 dark:border-gray-600 dark:bg-gray-700"
|
|
176
182
|
>
|
|
177
|
-
<HStack
|
|
178
|
-
|
|
179
|
-
// pl={3}
|
|
180
|
-
py={'$3'}
|
|
181
|
-
space={'sm'}
|
|
182
|
-
w={'100%'}
|
|
183
|
-
flex={1}
|
|
184
|
-
// direction={'row'}
|
|
185
|
-
justifyContent={'space-between'}
|
|
186
|
-
alignItems={'center'}
|
|
187
|
-
>
|
|
188
|
-
<Box flex={0.1} alignItems={'flex-start'} pl={'$3'}>
|
|
183
|
+
<HStack space={'sm'} className="flex-1 w-[100%] py-3 justify-between items-center">
|
|
184
|
+
<Box className="flex-[0.1] items-start pl-3">
|
|
189
185
|
<Avatar
|
|
190
186
|
key={'support-service-channels-key-' + channel?.id}
|
|
191
|
-
bg={'transparent'}
|
|
192
187
|
size={'sm'}
|
|
193
|
-
top
|
|
194
|
-
right={'$0'}
|
|
195
|
-
zIndex={1}
|
|
188
|
+
className="bg-transparent top-0 right-0 z-[1]"
|
|
196
189
|
>
|
|
197
190
|
<AvatarFallbackText> {startCase(channel?.creator?.username?.charAt(0))}</AvatarFallbackText>
|
|
198
191
|
<AvatarImage
|
|
@@ -204,13 +197,13 @@ export const SupportServiceDialogsListItemComponent: React.FC<IDialogListItemPro
|
|
|
204
197
|
/>
|
|
205
198
|
</Avatar>
|
|
206
199
|
</Box>
|
|
207
|
-
<Box flex
|
|
200
|
+
<Box className="flex-[0.9]">
|
|
208
201
|
<ServiceChannelWithLastMessage
|
|
209
202
|
channel={channel}
|
|
210
203
|
lastMessage={lastMessage}
|
|
211
204
|
subscribeToNewMessages={() =>
|
|
212
205
|
subscribeToMore({
|
|
213
|
-
document:
|
|
206
|
+
document: THREAD_CREATED_UPDATED,
|
|
214
207
|
variables: {
|
|
215
208
|
channelId: channel?.id?.toString(),
|
|
216
209
|
postParentId: postParentId ? servicePostParentId : null,
|
|
@@ -269,31 +262,36 @@ export const SupportServiceDialogsListItemComponent: React.FC<IDialogListItemPro
|
|
|
269
262
|
|
|
270
263
|
const ServiceChannelWithLastMessage = React.memo(({ channel, lastMessage, subscribeToNewMessages }: any) => {
|
|
271
264
|
React.useEffect(() => subscribeToNewMessages(), []);
|
|
265
|
+
|
|
266
|
+
// Define message display text
|
|
267
|
+
let displayMessage = 'No messages yet';
|
|
268
|
+
|
|
269
|
+
if (lastMessage) {
|
|
270
|
+
// Check for file attachments
|
|
271
|
+
const hasFileAttachments = lastMessage.files?.data?.length > 0;
|
|
272
|
+
|
|
273
|
+
if (hasFileAttachments) {
|
|
274
|
+
displayMessage = '📎 File attachment';
|
|
275
|
+
} else if (lastMessage.message && lastMessage.message.trim() !== '') {
|
|
276
|
+
displayMessage = lastMessage.message;
|
|
277
|
+
} else {
|
|
278
|
+
displayMessage = '(Empty message)';
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
272
282
|
return (
|
|
273
|
-
<HStack space={'sm'} flex
|
|
274
|
-
<Box flex
|
|
275
|
-
<Text color=
|
|
283
|
+
<HStack space={'sm'} className="flex-1 justify-center items-center">
|
|
284
|
+
<Box className="flex-[0.8]">
|
|
285
|
+
<Text color={colors.gray[600]} className="text-lg flex-wrap font-semibold">
|
|
276
286
|
{channel?.title}
|
|
277
287
|
</Text>
|
|
278
|
-
<Text color=
|
|
279
|
-
{
|
|
280
|
-
? lastMessageCreatorAndMembers?.message ?? ''
|
|
281
|
-
: lastMessage?.message ?? ''} */}
|
|
282
|
-
{lastMessage?.message ?? ''}
|
|
288
|
+
<Text color={colors.gray[600]} numberOfLines={1}>
|
|
289
|
+
{displayMessage}
|
|
283
290
|
</Text>
|
|
284
291
|
</Box>
|
|
285
292
|
|
|
286
|
-
<Box flex
|
|
287
|
-
|
|
288
|
-
<Text color="gray.500">
|
|
289
|
-
{lastMessageCreatorAndMembers
|
|
290
|
-
? createdAtText(lastMessageCreatorAndMembers?.createdAt)
|
|
291
|
-
: ''}
|
|
292
|
-
</Text>
|
|
293
|
-
) : (
|
|
294
|
-
<Text color="gray.500">{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}</Text>
|
|
295
|
-
)} */}
|
|
296
|
-
<Text color="$trueGray500">{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}</Text>
|
|
293
|
+
<Box className="flex-[0.2]">
|
|
294
|
+
<Text color={colors.gray[500]}>{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}</Text>
|
|
297
295
|
</Box>
|
|
298
296
|
</HStack>
|
|
299
297
|
);
|