@messenger-box/platform-mobile 10.0.3-alpha.23 → 10.0.3-alpha.232
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 +9 -11
- 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 -96
- 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 +2 -4
- 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 +1093 -1090
- package/lib/screens/inbox/containers/ConversationView.js.map +1 -1
- package/lib/screens/inbox/containers/Dialogs.js +130 -577
- package/lib/screens/inbox/containers/Dialogs.js.map +1 -1
- package/lib/screens/inbox/containers/ThreadConversationView.js +864 -1408
- package/lib/screens/inbox/containers/ThreadConversationView.js.map +1 -1
- package/lib/screens/inbox/containers/ThreadsView.js +9 -15
- 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 +1 -1
- package/lib/screens/inbox/hooks/useSafeDialogThreadsMachine.js.map +1 -1
- package/lib/screens/inbox/workflow/dialog-threads-xstate.js.map +1 -1
- package/package.json +10 -8
- package/CHANGELOG.md +0 -172
- 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 -125
- 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/SmartLoader.tsx +0 -61
- package/src/screens/inbox/components/SupportServiceDialogsListItem.tsx +0 -301
- package/src/screens/inbox/components/ThreadsViewItem.tsx +0 -233
- 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 -1784
- package/src/screens/inbox/containers/Dialogs.tsx +0 -829
- package/src/screens/inbox/containers/SupportServiceDialogs.tsx +0 -119
- package/src/screens/inbox/containers/ThreadConversationView.tsx +0 -2295
- package/src/screens/inbox/containers/ThreadsView.tsx +0 -224
- 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/inbox/hooks/useSafeDialogThreadsMachine.ts +0 -136
- package/src/screens/inbox/index.ts +0 -37
- package/src/screens/inbox/machines/threadsMachine.ts +0 -147
- package/src/screens/inbox/workflow/dialog-threads-xstate.ts +0 -163
- package/src/screens/index.ts +0 -4
- package/tsconfig.json +0 -13
- package/webpack.config.js +0 -58
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
import React, { useMemo } 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
|
-
useThreadMessagesQuery,
|
|
20
|
-
useMessagesQuery,
|
|
21
|
-
useUserAccountQuery,
|
|
22
|
-
useOnThreadCreatedUpdatedSubscription,
|
|
23
|
-
useOnThreadChatMessageAddedSubscription,
|
|
24
|
-
OnThreadCreatedUpdatedDocument as THREAD_CHAT_ADDED,
|
|
25
|
-
OnThreadChatMessageAddedDocument as CHAT_MESSAGE_ADDED,
|
|
26
|
-
} from 'common/graphql';
|
|
27
|
-
import { startCase } from 'lodash-es';
|
|
28
|
-
import colors from 'tailwindcss/colors';
|
|
29
|
-
|
|
30
|
-
const createdAtText = (value: string) => {
|
|
31
|
-
if (!value) return '';
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
// Validate the date before processing
|
|
35
|
-
const timestamp = new Date(value).getTime();
|
|
36
|
-
if (isNaN(timestamp)) {
|
|
37
|
-
console.warn(`Invalid date value in createdAtText: ${value}`);
|
|
38
|
-
return 'Unknown date';
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
let date = new Date(value);
|
|
42
|
-
if (isToday(date)) return 'Today';
|
|
43
|
-
if (isYesterday(date)) return 'Yesterday';
|
|
44
|
-
return format(date, 'MMM dd, yyyy');
|
|
45
|
-
} catch (error) {
|
|
46
|
-
console.error(`Error processing date in createdAtText: ${value}`, error);
|
|
47
|
-
return 'Unknown date';
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export interface IDialogListChannel extends IChannel {
|
|
52
|
-
users: IUserAccount[];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface IDialogListItemProps {
|
|
56
|
-
currentUser?: any;
|
|
57
|
-
users?: any;
|
|
58
|
-
selectedChannelId?: any;
|
|
59
|
-
channel?: any;
|
|
60
|
-
onOpen: (id: any, title: any, postParentId?: any) => void;
|
|
61
|
-
refreshing: boolean;
|
|
62
|
-
role: any;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* TODO:
|
|
67
|
-
* - Get Reservation info: reservation date, status
|
|
68
|
-
* - Add ability to get property information: name, logo
|
|
69
|
-
*/
|
|
70
|
-
export const SupportServiceDialogsListItemComponent: React.FC<IDialogListItemProps> = function DialogsListItem({
|
|
71
|
-
currentUser,
|
|
72
|
-
// users,
|
|
73
|
-
selectedChannelId,
|
|
74
|
-
channel,
|
|
75
|
-
onOpen,
|
|
76
|
-
refreshing,
|
|
77
|
-
role,
|
|
78
|
-
}) {
|
|
79
|
-
const [servicePostParentId, setServicePostParentId] = React.useState(null);
|
|
80
|
-
const {
|
|
81
|
-
data: threadMessages,
|
|
82
|
-
loading: threadMessageLoading,
|
|
83
|
-
error,
|
|
84
|
-
refetch: refetchThreadMessages,
|
|
85
|
-
subscribeToMore,
|
|
86
|
-
} = useThreadMessagesQuery({
|
|
87
|
-
variables: {
|
|
88
|
-
channelId: channel?.id?.toString(),
|
|
89
|
-
role,
|
|
90
|
-
limit: 2,
|
|
91
|
-
},
|
|
92
|
-
fetchPolicy: 'cache-and-network',
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
useFocusEffect(
|
|
96
|
-
React.useCallback(() => {
|
|
97
|
-
// Do something when the screen is focused
|
|
98
|
-
// refetchMessages({ channelId: channel?.id?.toString(), limit: 25 });
|
|
99
|
-
refetchThreadMessages({ channelId: channel?.id?.toString(), role, limit: 2 });
|
|
100
|
-
return () => {
|
|
101
|
-
// Do something when the screen is unfocused
|
|
102
|
-
// Useful for cleanup functions
|
|
103
|
-
};
|
|
104
|
-
}, [refreshing]),
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
const lastMessage = useMemo(() => {
|
|
108
|
-
if (!threadMessages?.threadMessages?.data?.length) {
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
111
|
-
const { data }: any = threadMessages.threadMessages;
|
|
112
|
-
const replies =
|
|
113
|
-
data
|
|
114
|
-
?.map((t: any) => t?.replies)
|
|
115
|
-
?.flat(1)
|
|
116
|
-
?.filter((p: any) => p?.message !== '') ?? [];
|
|
117
|
-
if (replies?.length) {
|
|
118
|
-
return replies[0];
|
|
119
|
-
// return replies[replies.length - 1];
|
|
120
|
-
} else {
|
|
121
|
-
const post =
|
|
122
|
-
data?.find((t: any) => {
|
|
123
|
-
return t?.post?.message !== '';
|
|
124
|
-
}) ??
|
|
125
|
-
data?.find((t: any) => {
|
|
126
|
-
return t?.post;
|
|
127
|
-
}) ??
|
|
128
|
-
null;
|
|
129
|
-
return post ? post?.post : null;
|
|
130
|
-
}
|
|
131
|
-
}, [threadMessages]);
|
|
132
|
-
|
|
133
|
-
React.useEffect(() => {
|
|
134
|
-
if (lastMessage) {
|
|
135
|
-
const sParentId = lastMessage?.parentId ? lastMessage?.parentId : lastMessage?.id;
|
|
136
|
-
setServicePostParentId(sParentId);
|
|
137
|
-
}
|
|
138
|
-
}, [lastMessage]);
|
|
139
|
-
|
|
140
|
-
const creatorAndMembersId = React.useMemo(() => {
|
|
141
|
-
if (!channel?.members) return null;
|
|
142
|
-
const membersIds: any =
|
|
143
|
-
channel?.members
|
|
144
|
-
?.filter((m: any) => m !== null && m?.user?.id !== currentUser?.id)
|
|
145
|
-
?.map((mu: any) => mu?.user?.id) ?? [];
|
|
146
|
-
const creatorId: any = channel?.creator?.id;
|
|
147
|
-
const mergedIds: any = [].concat(membersIds, creatorId) ?? [];
|
|
148
|
-
return mergedIds?.filter((m: any, pos: any) => mergedIds?.indexOf(m) === pos) ?? [];
|
|
149
|
-
}, [channel]);
|
|
150
|
-
|
|
151
|
-
const postParentId = React.useMemo(() => {
|
|
152
|
-
if (!creatorAndMembersId?.length) return null;
|
|
153
|
-
|
|
154
|
-
return creatorAndMembersId?.length && creatorAndMembersId?.includes(currentUser?.id)
|
|
155
|
-
? null
|
|
156
|
-
: lastMessage?.parentId
|
|
157
|
-
? lastMessage?.parentId
|
|
158
|
-
: lastMessage?.id ?? 0;
|
|
159
|
-
}, [creatorAndMembersId, lastMessage]);
|
|
160
|
-
|
|
161
|
-
// const { data: threadCreatedUpdated } = useOnThreadCreatedUpdatedSubscription({
|
|
162
|
-
// variables: {
|
|
163
|
-
// channelId: channel?.id?.toString(),
|
|
164
|
-
// postParentId:
|
|
165
|
-
// postParentId == null
|
|
166
|
-
// ? null
|
|
167
|
-
// : lastMessage?.parentId
|
|
168
|
-
// ? lastMessage?.parentId ?? null
|
|
169
|
-
// : lastMessage?.id ?? null,
|
|
170
|
-
// },
|
|
171
|
-
// });
|
|
172
|
-
|
|
173
|
-
// React.useEffect(() => {
|
|
174
|
-
// if (threadCreatedUpdated?.threadCreatedUpdated?.data) {
|
|
175
|
-
// refetchThreadMessages({ channelId: channel?.id?.toString(), role, limit: 2 });
|
|
176
|
-
// }
|
|
177
|
-
// }, [threadCreatedUpdated]);
|
|
178
|
-
|
|
179
|
-
return (
|
|
180
|
-
<Pressable
|
|
181
|
-
onPress={() => channel?.id !== selectedChannelId && onOpen(channel?.id, channel?.title, postParentId)}
|
|
182
|
-
className="flex-1 border rounded-md border-gray-200 dark:border-gray-600 dark:bg-gray-700"
|
|
183
|
-
>
|
|
184
|
-
<HStack space={'sm'} className="flex-1 w-[100%] py-3 justify-between items-center">
|
|
185
|
-
<Box className="flex-[0.1] items-start pl-3">
|
|
186
|
-
<Avatar
|
|
187
|
-
key={'support-service-channels-key-' + channel?.id}
|
|
188
|
-
size={'sm'}
|
|
189
|
-
className="bg-transparent top-0 right-0 z-[1]"
|
|
190
|
-
>
|
|
191
|
-
<AvatarFallbackText> {startCase(channel?.creator?.username?.charAt(0))}</AvatarFallbackText>
|
|
192
|
-
<AvatarImage
|
|
193
|
-
alt="user image"
|
|
194
|
-
style={{ borderRadius: 6, borderWidth: 2, borderColor: '#fff' }}
|
|
195
|
-
source={{
|
|
196
|
-
uri: channel?.creator?.picture,
|
|
197
|
-
}}
|
|
198
|
-
/>
|
|
199
|
-
</Avatar>
|
|
200
|
-
</Box>
|
|
201
|
-
<Box className="flex-[0.9]">
|
|
202
|
-
<ServiceChannelWithLastMessage
|
|
203
|
-
channel={channel}
|
|
204
|
-
lastMessage={lastMessage}
|
|
205
|
-
subscribeToNewMessages={() =>
|
|
206
|
-
subscribeToMore({
|
|
207
|
-
document: THREAD_CHAT_ADDED,
|
|
208
|
-
variables: {
|
|
209
|
-
channelId: channel?.id?.toString(),
|
|
210
|
-
postParentId: postParentId ? servicePostParentId : null,
|
|
211
|
-
},
|
|
212
|
-
updateQuery: (prev, { subscriptionData }: any) => {
|
|
213
|
-
if (!subscriptionData.data) return prev;
|
|
214
|
-
|
|
215
|
-
const newPostThreadData: any = subscriptionData?.data?.threadCreatedUpdated?.data;
|
|
216
|
-
const newMessage: any = subscriptionData?.data?.threadCreatedUpdated?.lastMessage;
|
|
217
|
-
const data = prev?.threadMessages?.data?.map((t: any) =>
|
|
218
|
-
t.id === newPostThreadData?.id
|
|
219
|
-
? {
|
|
220
|
-
...t,
|
|
221
|
-
replies: [...t?.replies, newMessage],
|
|
222
|
-
replyCount: newPostThreadData?.replyCount,
|
|
223
|
-
lastReplyAt: newPostThreadData?.lastReplyAt,
|
|
224
|
-
updatedAt: newPostThreadData?.updatedAt,
|
|
225
|
-
}
|
|
226
|
-
: t,
|
|
227
|
-
);
|
|
228
|
-
|
|
229
|
-
return Object.assign({}, prev, {
|
|
230
|
-
threadMessages: {
|
|
231
|
-
...prev?.threadMessages,
|
|
232
|
-
// totalCount: prev?.threadMessages?.totalCount + 1,
|
|
233
|
-
data: data,
|
|
234
|
-
},
|
|
235
|
-
});
|
|
236
|
-
},
|
|
237
|
-
})
|
|
238
|
-
}
|
|
239
|
-
/>
|
|
240
|
-
{/* <Text
|
|
241
|
-
flex={1}
|
|
242
|
-
color="gray.600"
|
|
243
|
-
p={0}
|
|
244
|
-
m={0}
|
|
245
|
-
w={'100%'}
|
|
246
|
-
justifyContent={''}
|
|
247
|
-
fontSize="lg"
|
|
248
|
-
fontWeight="semibold"
|
|
249
|
-
>
|
|
250
|
-
{title}
|
|
251
|
-
</Text> */}
|
|
252
|
-
{/* <Text flex={0.1} color="gray.600">
|
|
253
|
-
{lastMessage?.message ?? ''}
|
|
254
|
-
</Text> */}
|
|
255
|
-
</Box>
|
|
256
|
-
{/* <Text flex={0.2} color="gray.500">
|
|
257
|
-
{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}
|
|
258
|
-
</Text> */}
|
|
259
|
-
</HStack>
|
|
260
|
-
</Pressable>
|
|
261
|
-
);
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
const ServiceChannelWithLastMessage = React.memo(({ channel, lastMessage, subscribeToNewMessages }: any) => {
|
|
265
|
-
React.useEffect(() => subscribeToNewMessages(), []);
|
|
266
|
-
|
|
267
|
-
// Define message display text
|
|
268
|
-
let displayMessage = 'No messages yet';
|
|
269
|
-
|
|
270
|
-
if (lastMessage) {
|
|
271
|
-
// Check for file attachments
|
|
272
|
-
const hasFileAttachments = lastMessage.files?.data?.length > 0;
|
|
273
|
-
|
|
274
|
-
if (hasFileAttachments) {
|
|
275
|
-
displayMessage = '📎 File attachment';
|
|
276
|
-
} else if (lastMessage.message && lastMessage.message.trim() !== '') {
|
|
277
|
-
displayMessage = lastMessage.message;
|
|
278
|
-
} else {
|
|
279
|
-
displayMessage = '(Empty message)';
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return (
|
|
284
|
-
<HStack space={'sm'} className="flex-1 justify-center items-center">
|
|
285
|
-
<Box className="flex-[0.8]">
|
|
286
|
-
<Text color={colors.gray[600]} className="text-lg flex-wrap font-semibold">
|
|
287
|
-
{channel?.title}
|
|
288
|
-
</Text>
|
|
289
|
-
<Text color={colors.gray[600]} numberOfLines={1}>
|
|
290
|
-
{displayMessage}
|
|
291
|
-
</Text>
|
|
292
|
-
</Box>
|
|
293
|
-
|
|
294
|
-
<Box className="flex-[0.2]">
|
|
295
|
-
<Text color={colors.gray[500]}>{lastMessage ? createdAtText(lastMessage?.createdAt) : ''}</Text>
|
|
296
|
-
</Box>
|
|
297
|
-
</HStack>
|
|
298
|
-
);
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
export const SupportServiceDialogsListItem = React.memo(SupportServiceDialogsListItemComponent);
|
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
import React, { useMemo } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
VStack,
|
|
4
|
-
Text,
|
|
5
|
-
Pressable,
|
|
6
|
-
HStack,
|
|
7
|
-
Box,
|
|
8
|
-
Avatar,
|
|
9
|
-
AvatarFallbackText,
|
|
10
|
-
AvatarImage,
|
|
11
|
-
Button,
|
|
12
|
-
ButtonText,
|
|
13
|
-
Icon,
|
|
14
|
-
Badge,
|
|
15
|
-
BadgeText,
|
|
16
|
-
Link,
|
|
17
|
-
LinkText,
|
|
18
|
-
} from '@admin-layout/gluestack-ui-mobile';
|
|
19
|
-
import { format, isToday, isYesterday } from 'date-fns';
|
|
20
|
-
import { useFocusEffect } from '@react-navigation/native';
|
|
21
|
-
import { IChannel, IUserAccount } from 'common';
|
|
22
|
-
import { startCase } from 'lodash-es';
|
|
23
|
-
import colors from 'tailwindcss/colors';
|
|
24
|
-
import { useSelector } from 'react-redux';
|
|
25
|
-
import { userSelector } from '@adminide-stack/user-auth0-client';
|
|
26
|
-
|
|
27
|
-
const timeFormat = (value: string) => {
|
|
28
|
-
if (!value) return '';
|
|
29
|
-
let date = new Date(value);
|
|
30
|
-
if (isToday(date)) return format(date, 'h:mm a').toUpperCase();
|
|
31
|
-
return format(date, 'MMM do').toUpperCase();
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export interface IDialogListChannel extends IChannel {
|
|
35
|
-
users: IUserAccount[];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface ThreadViewItemProps {
|
|
39
|
-
id: string;
|
|
40
|
-
post: any;
|
|
41
|
-
channel: any;
|
|
42
|
-
replies: any[];
|
|
43
|
-
onPress: (id: any, title: any, postParentId?: any) => void;
|
|
44
|
-
channelId?: string;
|
|
45
|
-
channelsDetail?: any;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* TODO:
|
|
50
|
-
* - Get Reservation info: reservation date, status
|
|
51
|
-
* - Add ability to get property information: name, logo
|
|
52
|
-
*/
|
|
53
|
-
export const ThreadViewItemComponent: React.FC<ThreadViewItemProps> = function ThreadViewItem({
|
|
54
|
-
id,
|
|
55
|
-
post,
|
|
56
|
-
channel,
|
|
57
|
-
replies,
|
|
58
|
-
onPress,
|
|
59
|
-
channelId,
|
|
60
|
-
channelsDetail,
|
|
61
|
-
}) {
|
|
62
|
-
const currentUser = useSelector(userSelector);
|
|
63
|
-
|
|
64
|
-
// Prepare thread replies for display
|
|
65
|
-
const threadReplies = useMemo(() => {
|
|
66
|
-
return replies || [];
|
|
67
|
-
}, [replies]);
|
|
68
|
-
|
|
69
|
-
if (!threadReplies || threadReplies.length === 0) {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Get the last reply for the thread preview
|
|
74
|
-
const lastReply = threadReplies[0]; // Most recent reply should be first in the array
|
|
75
|
-
// Get total number of replies
|
|
76
|
-
const totalReplies = threadReplies.length;
|
|
77
|
-
|
|
78
|
-
// Get the first user in the thread as the primary user
|
|
79
|
-
const primaryUser = channel?.users?.[0] || lastReply?.author;
|
|
80
|
-
const channelName = channel?.title || `${primaryUser?.givenName || ''} ${primaryUser?.familyName || ''}`;
|
|
81
|
-
|
|
82
|
-
// Determine if the current user is in the thread
|
|
83
|
-
const userIsInThread = channel?.users?.some((user: any) => user.id === currentUser?.id);
|
|
84
|
-
|
|
85
|
-
return (
|
|
86
|
-
<Pressable
|
|
87
|
-
onPress={() => onPress(channel?.id, 'Thread', post?.id)}
|
|
88
|
-
style={{
|
|
89
|
-
marginVertical: 5,
|
|
90
|
-
backgroundColor: 'white',
|
|
91
|
-
borderRadius: 10,
|
|
92
|
-
paddingVertical: 5,
|
|
93
|
-
elevation: 1,
|
|
94
|
-
shadowColor: '#000',
|
|
95
|
-
shadowOffset: { width: 0, height: 1 },
|
|
96
|
-
shadowOpacity: 0.1,
|
|
97
|
-
shadowRadius: 2,
|
|
98
|
-
}}
|
|
99
|
-
>
|
|
100
|
-
<Box className="mb-3">
|
|
101
|
-
<VStack space="md">
|
|
102
|
-
{/* Thread header with green dot and users */}
|
|
103
|
-
{/* <HStack space="sm" className="px-4 items-center">
|
|
104
|
-
<Box
|
|
105
|
-
className="bg-green-500 rounded-full"
|
|
106
|
-
style={{ width: 8, height: 8 }}
|
|
107
|
-
/>
|
|
108
|
-
<Text className="text-base text-gray-900">{channelName}</Text>
|
|
109
|
-
</HStack> */}
|
|
110
|
-
|
|
111
|
-
{/* Thread members */}
|
|
112
|
-
<Text className="text-sm font-medium text-gray-600 px-4 pt-2">
|
|
113
|
-
{channel?.users?.map((user: any) => user?.givenName || user?.username)?.join(', ')}
|
|
114
|
-
</Text>
|
|
115
|
-
|
|
116
|
-
{/* Thread messages preview - show up to 3 most recent messages */}
|
|
117
|
-
{threadReplies.slice(0, 3).map((reply: any, index: number) => {
|
|
118
|
-
// Group consecutive messages from the same user
|
|
119
|
-
const previousReply = index > 0 ? threadReplies.slice(0, 3)[index - 1] : null;
|
|
120
|
-
const isConsecutiveReply = previousReply && previousReply.author?.id === reply.author?.id;
|
|
121
|
-
|
|
122
|
-
return (
|
|
123
|
-
<HStack key={reply.id || index} space="md" className="px-4 py-1">
|
|
124
|
-
{!isConsecutiveReply ? (
|
|
125
|
-
<Avatar
|
|
126
|
-
size="md"
|
|
127
|
-
style={{
|
|
128
|
-
width: 40,
|
|
129
|
-
height: 40,
|
|
130
|
-
backgroundColor:
|
|
131
|
-
reply?.author?.id === currentUser?.id ? '#2EB67D' : '#E8A54A',
|
|
132
|
-
}}
|
|
133
|
-
>
|
|
134
|
-
<AvatarFallbackText>
|
|
135
|
-
{startCase(reply?.author?.username?.charAt(0) || 'U')}
|
|
136
|
-
</AvatarFallbackText>
|
|
137
|
-
{reply?.author?.picture && (
|
|
138
|
-
<AvatarImage
|
|
139
|
-
alt={reply?.author?.username || 'User'}
|
|
140
|
-
source={{
|
|
141
|
-
uri: reply?.author?.picture,
|
|
142
|
-
}}
|
|
143
|
-
/>
|
|
144
|
-
)}
|
|
145
|
-
</Avatar>
|
|
146
|
-
) : (
|
|
147
|
-
<Box style={{ width: 40 }} />
|
|
148
|
-
)}
|
|
149
|
-
|
|
150
|
-
<VStack space="xs" className="flex-1">
|
|
151
|
-
{!isConsecutiveReply && (
|
|
152
|
-
<HStack space="sm" className="items-center">
|
|
153
|
-
<Text className="font-bold text-gray-900">
|
|
154
|
-
{reply?.author?.givenName || reply?.author?.username || 'User'}
|
|
155
|
-
</Text>
|
|
156
|
-
<Text className="text-xs text-gray-500">
|
|
157
|
-
{timeFormat(reply?.createdAt)}
|
|
158
|
-
</Text>
|
|
159
|
-
</HStack>
|
|
160
|
-
)}
|
|
161
|
-
|
|
162
|
-
{reply?.message && (
|
|
163
|
-
<Text color={colors.gray[700]} numberOfLines={2} className="text-base">
|
|
164
|
-
{reply?.message}
|
|
165
|
-
{reply?.edited && <Text className="text-xs text-gray-500"> (edited)</Text>}
|
|
166
|
-
</Text>
|
|
167
|
-
)}
|
|
168
|
-
|
|
169
|
-
{reply.email && <Text className="text-blue-500">{reply.email}</Text>}
|
|
170
|
-
|
|
171
|
-
{reply?.files?.data?.length > 0 && (
|
|
172
|
-
<HStack space="sm" className="my-1">
|
|
173
|
-
{reply?.files?.data?.map((file: any, fileIndex: number) => (
|
|
174
|
-
<Box
|
|
175
|
-
key={fileIndex}
|
|
176
|
-
className="overflow-hidden"
|
|
177
|
-
style={{ width: 80, height: 80 }}
|
|
178
|
-
>
|
|
179
|
-
<AvatarImage
|
|
180
|
-
alt="attachment"
|
|
181
|
-
className="rounded-none border-none"
|
|
182
|
-
style={{
|
|
183
|
-
width: '100%',
|
|
184
|
-
height: '100%',
|
|
185
|
-
}}
|
|
186
|
-
source={{
|
|
187
|
-
uri: file?.url,
|
|
188
|
-
}}
|
|
189
|
-
/>
|
|
190
|
-
</Box>
|
|
191
|
-
))}
|
|
192
|
-
</HStack>
|
|
193
|
-
)}
|
|
194
|
-
</VStack>
|
|
195
|
-
</HStack>
|
|
196
|
-
);
|
|
197
|
-
})}
|
|
198
|
-
|
|
199
|
-
{/* Show more replies indicator */}
|
|
200
|
-
{totalReplies > 3 && (
|
|
201
|
-
<HStack className="px-4 items-center" space="sm">
|
|
202
|
-
<Box style={{ width: 40 }} />
|
|
203
|
-
<Link onPress={() => onPress(channel?.id, 'Thread', post?.id)}>
|
|
204
|
-
<LinkText className="text-blue-600 mt-1">
|
|
205
|
-
{totalReplies - 3} more {totalReplies - 3 === 1 ? 'reply' : 'replies'}
|
|
206
|
-
</LinkText>
|
|
207
|
-
</Link>
|
|
208
|
-
</HStack>
|
|
209
|
-
)}
|
|
210
|
-
|
|
211
|
-
{/* Reply button */}
|
|
212
|
-
<Box className="px-4 pb-2">
|
|
213
|
-
<Button
|
|
214
|
-
size="sm"
|
|
215
|
-
className="self-start rounded-full"
|
|
216
|
-
variant="outline"
|
|
217
|
-
style={{
|
|
218
|
-
borderColor: '#E2E8F0',
|
|
219
|
-
paddingHorizontal: 16,
|
|
220
|
-
paddingVertical: 6,
|
|
221
|
-
}}
|
|
222
|
-
onPress={() => onPress(channel?.id, 'Thread', post?.id)}
|
|
223
|
-
>
|
|
224
|
-
<ButtonText style={{ fontSize: 14, color: colors.gray[800] }}>Reply</ButtonText>
|
|
225
|
-
</Button>
|
|
226
|
-
</Box>
|
|
227
|
-
</VStack>
|
|
228
|
-
</Box>
|
|
229
|
-
</Pressable>
|
|
230
|
-
);
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
export const ThreadViewItem = React.memo(ThreadViewItemComponent);
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { assign, setup } from 'xstate';
|
|
2
|
-
import { merge } from 'lodash-es';
|
|
3
|
-
|
|
4
|
-
export const enum Actions {
|
|
5
|
-
INITIAL_CONTEXT = 'INITIAL_CONTEXT',
|
|
6
|
-
ERROR_HANDLED = 'ERROR_HANDLED',
|
|
7
|
-
FETCH_MESSAGES = 'FETCH_MESSAGES',
|
|
8
|
-
UPDATE_MESSAGES = 'UPDATE_MESSAGES',
|
|
9
|
-
SUBSCRIBE_TO_MESSAGES = 'SUBSCRIBE_TO_MESSAGES',
|
|
10
|
-
SET_TITLE = 'SET_TITLE',
|
|
11
|
-
START_LOADING = 'START_LOADING',
|
|
12
|
-
STOP_LOADING = 'STOP_LOADING',
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const enum BaseState {
|
|
16
|
-
Idle = 'idle',
|
|
17
|
-
Error = 'error',
|
|
18
|
-
FetchingMessages = 'fetchingMessages',
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const dialogsListItemXstate = setup({
|
|
22
|
-
types: {
|
|
23
|
-
context: {} as {
|
|
24
|
-
channelId: string | null;
|
|
25
|
-
currentUser: any;
|
|
26
|
-
messages: any[];
|
|
27
|
-
loading: boolean;
|
|
28
|
-
error: string | null;
|
|
29
|
-
title: string;
|
|
30
|
-
channelMembers: any[];
|
|
31
|
-
lastMessage: any;
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
actions: {
|
|
35
|
-
errorState: assign(({ context, event }) => {
|
|
36
|
-
return {
|
|
37
|
-
...context,
|
|
38
|
-
error: event.data?.message || 'An error occurred',
|
|
39
|
-
loading: false,
|
|
40
|
-
};
|
|
41
|
-
}),
|
|
42
|
-
setInitialContext: assign(({ context, event }) => {
|
|
43
|
-
return merge({
|
|
44
|
-
...context,
|
|
45
|
-
channelId: event.data?.channelId || null,
|
|
46
|
-
currentUser: event.data?.currentUser || null,
|
|
47
|
-
loading: true,
|
|
48
|
-
});
|
|
49
|
-
}),
|
|
50
|
-
setMessages: assign(({ context, event }) => {
|
|
51
|
-
return {
|
|
52
|
-
...context,
|
|
53
|
-
messages: event.data?.messages || [],
|
|
54
|
-
loading: false,
|
|
55
|
-
};
|
|
56
|
-
}),
|
|
57
|
-
updateMessages: assign(({ context, event }) => {
|
|
58
|
-
const newMessage = event.data?.message;
|
|
59
|
-
if (!newMessage) return context;
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
...context,
|
|
63
|
-
messages: [...context.messages, newMessage],
|
|
64
|
-
};
|
|
65
|
-
}),
|
|
66
|
-
setTitle: assign(({ context, event }) => {
|
|
67
|
-
return {
|
|
68
|
-
...context,
|
|
69
|
-
title: event.data?.title || '',
|
|
70
|
-
};
|
|
71
|
-
}),
|
|
72
|
-
startLoading: assign(({ context }) => {
|
|
73
|
-
return {
|
|
74
|
-
...context,
|
|
75
|
-
loading: true,
|
|
76
|
-
};
|
|
77
|
-
}),
|
|
78
|
-
stopLoading: assign(({ context }) => {
|
|
79
|
-
return {
|
|
80
|
-
...context,
|
|
81
|
-
loading: false,
|
|
82
|
-
};
|
|
83
|
-
}),
|
|
84
|
-
},
|
|
85
|
-
}).createMachine({
|
|
86
|
-
id: 'dialogs-list-item',
|
|
87
|
-
initial: BaseState.Idle,
|
|
88
|
-
context: {
|
|
89
|
-
channelId: null,
|
|
90
|
-
currentUser: null,
|
|
91
|
-
messages: [],
|
|
92
|
-
loading: false,
|
|
93
|
-
error: null,
|
|
94
|
-
title: '',
|
|
95
|
-
channelMembers: [],
|
|
96
|
-
lastMessage: null,
|
|
97
|
-
},
|
|
98
|
-
states: {
|
|
99
|
-
[BaseState.Idle]: {
|
|
100
|
-
on: {
|
|
101
|
-
[Actions.INITIAL_CONTEXT]: {
|
|
102
|
-
target: BaseState.FetchingMessages,
|
|
103
|
-
actions: ['setInitialContext'],
|
|
104
|
-
},
|
|
105
|
-
[Actions.UPDATE_MESSAGES]: {
|
|
106
|
-
target: BaseState.Idle,
|
|
107
|
-
actions: ['updateMessages'],
|
|
108
|
-
},
|
|
109
|
-
[Actions.SET_TITLE]: {
|
|
110
|
-
target: BaseState.Idle,
|
|
111
|
-
actions: ['setTitle'],
|
|
112
|
-
},
|
|
113
|
-
[Actions.START_LOADING]: {
|
|
114
|
-
target: BaseState.Idle,
|
|
115
|
-
actions: ['startLoading'],
|
|
116
|
-
},
|
|
117
|
-
[Actions.STOP_LOADING]: {
|
|
118
|
-
target: BaseState.Idle,
|
|
119
|
-
actions: ['stopLoading'],
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
[BaseState.Error]: {
|
|
124
|
-
entry: ['errorState'],
|
|
125
|
-
on: {
|
|
126
|
-
[Actions.ERROR_HANDLED]: {
|
|
127
|
-
target: BaseState.Idle,
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
[BaseState.FetchingMessages]: {
|
|
132
|
-
invoke: {
|
|
133
|
-
src: 'fetchMessages',
|
|
134
|
-
input: ({ context, event }) => ({ context, event }),
|
|
135
|
-
onDone: {
|
|
136
|
-
target: BaseState.Idle,
|
|
137
|
-
actions: ['setMessages'],
|
|
138
|
-
},
|
|
139
|
-
onError: {
|
|
140
|
-
target: BaseState.Error,
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
},
|
|
144
|
-
},
|
|
145
|
-
} as any);
|