@messenger-box/tailwind-ui-inbox 10.0.3-alpha.67 → 10.0.3-alpha.69
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 +4 -0
- package/lib/components/InboxMessage/ConversationItem.d.ts +10 -7
- package/lib/components/InboxMessage/ConversationItem.d.ts.map +1 -1
- package/lib/components/InboxMessage/ConversationItem.js +58 -77
- package/lib/components/InboxMessage/ConversationItem.js.map +1 -1
- package/lib/components/InboxMessage/LeftSidebar.d.ts +2 -1
- package/lib/components/InboxMessage/LeftSidebar.d.ts.map +1 -1
- package/lib/components/InboxMessage/LeftSidebar.js +15 -8
- package/lib/components/InboxMessage/LeftSidebar.js.map +1 -1
- package/lib/components/InboxMessage/MessageInput.d.ts.map +1 -1
- package/lib/components/InboxMessage/MessageInput.js +15 -1
- package/lib/components/InboxMessage/MessageInput.js.map +1 -1
- package/lib/components/InboxMessage/SubscriptionHandler.d.ts +19 -0
- package/lib/components/InboxMessage/SubscriptionHandler.d.ts.map +1 -0
- package/lib/components/InboxMessage/SubscriptionHandler.js +41 -0
- package/lib/components/InboxMessage/SubscriptionHandler.js.map +1 -0
- package/lib/container/Inbox.d.ts.map +1 -1
- package/lib/container/Inbox.js +49 -28
- package/lib/container/Inbox.js.map +1 -1
- package/lib/container/InboxWithLoader.d.ts +10 -3
- package/lib/container/InboxWithLoader.d.ts.map +1 -1
- package/lib/container/InboxWithLoader.js +81 -30
- package/lib/container/InboxWithLoader.js.map +1 -1
- package/lib/container/ServiceInbox.js +1 -1
- package/lib/container/ServiceInbox.js.map +1 -1
- package/lib/container/ThreadMessages.js +1 -1
- package/lib/container/ThreadMessages.js.map +1 -1
- package/lib/container/ThreadMessagesInbox.js +1 -1
- package/lib/container/ThreadMessagesInbox.js.map +1 -1
- package/lib/container/Threads.js +1 -1
- package/lib/container/Threads.js.map +1 -1
- package/package.json +4 -4
- package/src/components/InboxMessage/ConversationItem.tsx +188 -186
- package/src/components/InboxMessage/LeftSidebar.tsx +20 -11
- package/src/components/InboxMessage/MessageInput.tsx +16 -1
- package/src/components/InboxMessage/SubscriptionHandler.tsx +55 -0
- package/src/container/Inbox.tsx +53 -35
- package/src/container/InboxWithLoader.tsx +104 -38
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@messenger-box/tailwind-ui-inbox",
|
|
3
|
-
"version": "10.0.3-alpha.
|
|
3
|
+
"version": "10.0.3-alpha.69",
|
|
4
4
|
"description": "Inbox UI components built with TailwindCSS",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "CDMBase LLC",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"watch": "npm run build:lib:watch"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@messenger-box/core": "10.0.3-alpha.
|
|
25
|
-
"@messenger-box/platform-client": "10.0.3-alpha.
|
|
24
|
+
"@messenger-box/core": "10.0.3-alpha.69",
|
|
25
|
+
"@messenger-box/platform-client": "10.0.3-alpha.69",
|
|
26
26
|
"date-fns": "^4.1.0",
|
|
27
27
|
"date-fns-tz": "^3.2.0",
|
|
28
28
|
"emoji-mart": "^3.0.1",
|
|
@@ -53,5 +53,5 @@
|
|
|
53
53
|
"typescript": {
|
|
54
54
|
"definition": "lib/index.d.ts"
|
|
55
55
|
},
|
|
56
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "011fbbc8fbbc97043d73323c02c1ac22b2a01bfb"
|
|
57
57
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useMessagesQuery, OnChatMessageAddedDocument as CHAT_MESSAGE_ADDED } from 'common/graphql';
|
|
2
2
|
import { format, isToday, isYesterday } from 'date-fns';
|
|
3
|
-
import React, { useMemo, useEffect, useState } from 'react';
|
|
3
|
+
import React, { useMemo, useEffect, useState, useCallback } from 'react';
|
|
4
|
+
import { SubscriptionHandler } from './SubscriptionHandler';
|
|
4
5
|
|
|
5
6
|
const createdAtText = (value) => {
|
|
6
7
|
if (!value) return '';
|
|
@@ -20,177 +21,186 @@ const Skeleton = ({ className = '' }) => {
|
|
|
20
21
|
);
|
|
21
22
|
};
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
showBorder
|
|
25
|
-
currentUser
|
|
26
|
-
filter
|
|
27
|
-
channel
|
|
28
|
-
handleSelectChannel
|
|
29
|
-
users
|
|
30
|
-
selectedChannelId
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// data: messagesQuery,
|
|
34
|
-
// loading: messageLoading,
|
|
35
|
-
// } = useMessagesQuery({
|
|
36
|
-
// variables: {
|
|
37
|
-
// channelId: channel.id?.toString(),
|
|
38
|
-
// limit: 1,
|
|
39
|
-
// },
|
|
40
|
-
// fetchPolicy: 'cache-and-network',
|
|
41
|
-
// });
|
|
42
|
-
const [messages, setMessages] = useState([]);
|
|
43
|
-
const {
|
|
44
|
-
data: messagesQuery,
|
|
45
|
-
loading: messageLoading,
|
|
46
|
-
refetch: refetchMessages,
|
|
47
|
-
subscribeToMore,
|
|
48
|
-
} = useMessagesQuery({
|
|
49
|
-
variables: {
|
|
50
|
-
channelId: channel?.id?.toString(),
|
|
51
|
-
parentId: null,
|
|
52
|
-
limit: 10,
|
|
53
|
-
// sort: {
|
|
54
|
-
// key: 'updatedAt',
|
|
55
|
-
// value: SortEnum.Desc,
|
|
56
|
-
// },
|
|
57
|
-
},
|
|
58
|
-
fetchPolicy: 'cache-and-network',
|
|
59
|
-
refetchWritePolicy: 'merge',
|
|
60
|
-
});
|
|
24
|
+
interface ConversationItemProps {
|
|
25
|
+
showBorder: boolean;
|
|
26
|
+
currentUser: any;
|
|
27
|
+
filter: string;
|
|
28
|
+
channel: any;
|
|
29
|
+
handleSelectChannel: (channelId: string) => void;
|
|
30
|
+
users: any[];
|
|
31
|
+
selectedChannelId: string;
|
|
32
|
+
messagesQuery: any;
|
|
33
|
+
}
|
|
61
34
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
35
|
+
export const ConversationItem = React.memo(
|
|
36
|
+
({
|
|
37
|
+
showBorder,
|
|
38
|
+
currentUser,
|
|
39
|
+
filter,
|
|
40
|
+
channel,
|
|
41
|
+
handleSelectChannel,
|
|
42
|
+
users,
|
|
43
|
+
selectedChannelId,
|
|
44
|
+
messagesQuery: messagesQueryProp,
|
|
45
|
+
}: ConversationItemProps) => {
|
|
46
|
+
// const {
|
|
47
|
+
// data: messagesQuery,
|
|
48
|
+
// loading: messageLoading,
|
|
49
|
+
// } = useMessagesQuery({
|
|
50
|
+
// variables: {
|
|
51
|
+
// channelId: channel.id?.toString(),
|
|
52
|
+
// limit: 1,
|
|
53
|
+
// },
|
|
54
|
+
// fetchPolicy: 'cache-and-network',
|
|
55
|
+
// });
|
|
56
|
+
const [messages, setMessages] = useState([]);
|
|
57
|
+
const {
|
|
58
|
+
data: messagesQuery,
|
|
59
|
+
loading: messageLoading,
|
|
60
|
+
refetch: refetchMessages,
|
|
61
|
+
subscribeToMore,
|
|
62
|
+
} = useMessagesQuery({
|
|
63
|
+
variables: {
|
|
64
|
+
channelId: channel?.id?.toString(),
|
|
65
|
+
parentId: null,
|
|
66
|
+
limit: 10,
|
|
67
|
+
// sort: {
|
|
68
|
+
// key: 'updatedAt',
|
|
69
|
+
// value: SortEnum.Desc,
|
|
70
|
+
// },
|
|
71
|
+
},
|
|
72
|
+
fetchPolicy: 'cache-and-network',
|
|
73
|
+
refetchWritePolicy: 'merge',
|
|
81
74
|
});
|
|
82
|
-
}, []);
|
|
83
75
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
76
|
+
// const {
|
|
77
|
+
// data: newMessage,
|
|
78
|
+
// loading: newMsgLoading,
|
|
79
|
+
// error: newMsgError,
|
|
80
|
+
// }: any = useOnChatMessageAddedSubscription({
|
|
81
|
+
// variables: {
|
|
82
|
+
// channelId: channel?.id?.toString(),
|
|
83
|
+
// },
|
|
84
|
+
// });
|
|
85
|
+
|
|
86
|
+
React.useEffect(() => {
|
|
87
|
+
if (channel?.id) {
|
|
88
|
+
refetchMessages({
|
|
89
|
+
channelId: channel?.id?.toString(),
|
|
90
|
+
parentId: null,
|
|
91
|
+
limit: 10,
|
|
92
|
+
// sort: {
|
|
93
|
+
// key: 'updatedAt',
|
|
94
|
+
// value: SortEnum.Desc,
|
|
95
|
+
// },
|
|
96
|
+
});
|
|
88
97
|
}
|
|
98
|
+
}, [channel?.id, refetchMessages]);
|
|
89
99
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// key: 'updatedAt',
|
|
96
|
-
// value: SortEnum.Desc,
|
|
97
|
-
// },
|
|
98
|
-
// });
|
|
99
|
-
}
|
|
100
|
-
}, [messagesQuery]);
|
|
100
|
+
React.useEffect(() => {
|
|
101
|
+
if (messagesQuery?.messages?.data?.length) {
|
|
102
|
+
setMessages(messagesQuery.messages.data);
|
|
103
|
+
}
|
|
104
|
+
}, [messagesQuery?.messages?.data]);
|
|
101
105
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
const chatUser = useMemo(
|
|
107
|
+
() =>
|
|
108
|
+
users?.find(({ id }) => {
|
|
109
|
+
const isNotCurrentUser = id !== currentUser?.id;
|
|
110
|
+
if (isNotCurrentUser) {
|
|
111
|
+
return channel?.members?.find(({ user }) => user.id === id);
|
|
112
|
+
}
|
|
113
|
+
if (channel?.members?.length === 1 && channel?.members?.[0]?.user?.id === currentUser?.id) {
|
|
114
|
+
return currentUser;
|
|
115
|
+
}
|
|
116
|
+
return isNotCurrentUser;
|
|
117
|
+
}),
|
|
118
|
+
[users, currentUser, channel],
|
|
119
|
+
);
|
|
113
120
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
121
|
+
// Last Message
|
|
122
|
+
// const lastMessage = useMemo(() => {
|
|
123
|
+
// if (!messagesQuery?.messages?.data?.length) {
|
|
124
|
+
// return null;
|
|
125
|
+
// }
|
|
126
|
+
// const { data } = messagesQuery.messages;
|
|
127
|
+
// return data[data.length - 1];
|
|
128
|
+
// }, [messagesQuery]);
|
|
129
|
+
const lastMessage = useMemo(() => {
|
|
130
|
+
if (!messages?.length) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
const data = messages;
|
|
134
|
+
const filteredData: any = data?.filter((p: any) => p?.message !== '');
|
|
128
135
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
//return filteredData[0];
|
|
137
|
+
let filteredLastMessage =
|
|
138
|
+
filteredData && filteredData?.length
|
|
139
|
+
? filteredData?.reduce((a, b) => {
|
|
140
|
+
return new Date(a?.updatedAt) > new Date(b?.updatedAt) ? a : b;
|
|
141
|
+
}, []) ?? null
|
|
142
|
+
: null;
|
|
143
|
+
return filteredLastMessage;
|
|
144
|
+
// //return data[data.length - 1];
|
|
145
|
+
}, [messages]);
|
|
139
146
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
147
|
+
const channelType = useMemo(() => {
|
|
148
|
+
return channel?.type;
|
|
149
|
+
}, [channel]);
|
|
143
150
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
151
|
+
if (
|
|
152
|
+
filter &&
|
|
153
|
+
!chatUser?.username?.toLowerCase().includes(filter.toLowerCase()) &&
|
|
154
|
+
!lastMessage?.message?.toLowerCase().includes(filter.toLowerCase())
|
|
155
|
+
) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
150
158
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
159
|
+
return (
|
|
160
|
+
<div
|
|
161
|
+
key={`conv_channel_${channel.id}`}
|
|
162
|
+
className={`cursor-pointer flex items-center p-3 border-b ${
|
|
163
|
+
showBorder ? 'border-gray-300' : 'border-transparent'
|
|
164
|
+
} ${
|
|
165
|
+
channel.id == selectedChannelId
|
|
166
|
+
? 'bg-gray-300 dark:bg-gray-500'
|
|
167
|
+
: 'hover:bg-gray-50 dark:hover:bg-gray-700'
|
|
168
|
+
}`}
|
|
169
|
+
onClick={() => channel.id !== selectedChannelId && handleSelectChannel(channel.id)}
|
|
170
|
+
>
|
|
171
|
+
<img
|
|
172
|
+
className="w-10 h-10 rounded-full bg-gray-400 object-cover flex-shrink-0"
|
|
173
|
+
src={chatUser?.picture || '/default-avatar.svg'}
|
|
174
|
+
alt={chatUser?.givenName || 'User avatar'}
|
|
175
|
+
onError={(e) => {
|
|
176
|
+
// Prevent infinite loop by checking if we're already showing the fallback
|
|
177
|
+
if (e.currentTarget.src.includes('default-avatar.svg')) {
|
|
178
|
+
// If SVG also fails, use a data URL fallback
|
|
179
|
+
e.currentTarget.src =
|
|
180
|
+
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8Y2lyY2xlIGN4PSIyMCIgY3k9IjIwIiByPSIyMCIgZmlsbD0iI0U1RTdFQiIvPgogIDxjaXJjbGUgY3g9IjIwIiBjeT0iMTYiIHI9IjYiIGZpbGw9IiM5Q0EzQUYiLz4KICA8cGF0aCBkPSJNOCAzMmMwLTYuNjI3IDUuMzczLTEyIDEyLTEyczEyIDUuMzczIDEyIDEyIiBmaWxsPSIjOUNBM0FGIi8+Cjwvc3ZnPgo=';
|
|
181
|
+
} else {
|
|
182
|
+
e.currentTarget.src = '/default-avatar.svg';
|
|
183
|
+
}
|
|
184
|
+
}}
|
|
185
|
+
/>
|
|
186
|
+
<div className="ml-2 flex-grow min-w-10 max-w-96">
|
|
187
|
+
{messageLoading && <Skeleton className="w-full h-16" />}
|
|
188
|
+
{!messageLoading && (
|
|
189
|
+
<>
|
|
190
|
+
<LastMessageComponent
|
|
191
|
+
lastMessage={lastMessage}
|
|
192
|
+
channelType={channelType}
|
|
193
|
+
chatUser={chatUser}
|
|
194
|
+
/>
|
|
195
|
+
<SubscriptionHandler
|
|
196
|
+
subscribeToMore={subscribeToMore}
|
|
197
|
+
document={CHAT_MESSAGE_ADDED}
|
|
198
|
+
variables={{ channelId: channel?.id?.toString() }}
|
|
199
|
+
enabled={!!channel?.id && !!subscribeToMore}
|
|
200
|
+
updateQuery={(prev, { subscriptionData }: any) => {
|
|
192
201
|
if (!subscriptionData.data) return prev;
|
|
193
202
|
const newMessage: any = subscriptionData?.data?.chatMessageAdded;
|
|
203
|
+
console.log('ConversationItem: New message received via subscription:', newMessage);
|
|
194
204
|
const previousData = prev?.messages?.data
|
|
195
205
|
? [...prev.messages.data, newMessage]
|
|
196
206
|
: [];
|
|
@@ -204,34 +214,26 @@ export const ConversationItem = ({
|
|
|
204
214
|
},
|
|
205
215
|
};
|
|
206
216
|
return merged;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
// </Text>
|
|
216
|
-
// <Text fontSize="12px" color="gray.500">
|
|
217
|
-
// {lastMessage ? createdAtText(lastMessage?.createdAt) : ''}
|
|
218
|
-
// </Text>
|
|
219
|
-
// </Box>
|
|
220
|
-
// <Text fontSize="14px" color="gray.600" fontWeight={'bold'} mt="5px">
|
|
221
|
-
// {chatUser?.givenName + ' ' + chatUser?.familyName}
|
|
222
|
-
// </Text>
|
|
223
|
-
// <Text fontSize="14px" isTruncated w="80%" mt="5px" color="gray.600">
|
|
224
|
-
// {lastMessage?.message}
|
|
225
|
-
// </Text>
|
|
226
|
-
// </Box>
|
|
227
|
-
)}
|
|
217
|
+
}}
|
|
218
|
+
onError={(error) => {
|
|
219
|
+
console.error('ConversationItem: Subscription error:', error);
|
|
220
|
+
}}
|
|
221
|
+
/>
|
|
222
|
+
</>
|
|
223
|
+
)}
|
|
224
|
+
</div>
|
|
228
225
|
</div>
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
226
|
+
);
|
|
227
|
+
},
|
|
228
|
+
);
|
|
232
229
|
|
|
233
|
-
|
|
234
|
-
|
|
230
|
+
interface LastMessageComponentProps {
|
|
231
|
+
lastMessage: any;
|
|
232
|
+
channelType: string;
|
|
233
|
+
chatUser: any;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const LastMessageComponent = React.memo(({ lastMessage, channelType, chatUser }: LastMessageComponentProps) => {
|
|
235
237
|
return (
|
|
236
238
|
<div className="flex flex-col w-full">
|
|
237
239
|
<div className="w-full flex justify-between items-center">
|
|
@@ -250,4 +252,4 @@ const LastMessageComponent = ({ subscribeToNewMessages, lastMessage, channelType
|
|
|
250
252
|
</p>
|
|
251
253
|
</div>
|
|
252
254
|
);
|
|
253
|
-
};
|
|
255
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GoSettings } from '@react-icons/all-files/go/GoSettings.js';
|
|
2
2
|
import { GoSearch } from '@react-icons/all-files/go/GoSearch.js';
|
|
3
|
-
import React, { useState, useEffect } from 'react';
|
|
3
|
+
import React, { useState, useEffect, useMemo } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
import { orderBy, uniqBy } from 'lodash-es';
|
|
6
6
|
import { ConversationItem } from './ConversationItem';
|
|
@@ -19,9 +19,10 @@ type LeftSidebarProps = {
|
|
|
19
19
|
getChannelsRefetch: any;
|
|
20
20
|
supportServices?: any;
|
|
21
21
|
role?: any;
|
|
22
|
+
messagesQuery?: any;
|
|
22
23
|
};
|
|
23
24
|
|
|
24
|
-
export const LeftSidebar = (props: LeftSidebarProps) => {
|
|
25
|
+
export const LeftSidebar = React.memo((props: LeftSidebarProps) => {
|
|
25
26
|
const {
|
|
26
27
|
currentUser,
|
|
27
28
|
handleSelectChannel,
|
|
@@ -31,10 +32,21 @@ export const LeftSidebar = (props: LeftSidebarProps) => {
|
|
|
31
32
|
selectedChannelId,
|
|
32
33
|
supportServices,
|
|
33
34
|
role,
|
|
35
|
+
messagesQuery,
|
|
34
36
|
} = props;
|
|
35
37
|
const [keyword, setKeyword] = useState('');
|
|
36
38
|
const { t } = useTranslation('translations');
|
|
37
39
|
|
|
40
|
+
// Memoize the sorted channels to prevent unnecessary re-renders
|
|
41
|
+
const sortedChannels = useMemo(() => {
|
|
42
|
+
if (!userChannels?.length) return [];
|
|
43
|
+
return orderBy(
|
|
44
|
+
uniqBy([...userChannels], ({ id }) => id),
|
|
45
|
+
['updatedAt'],
|
|
46
|
+
['desc'],
|
|
47
|
+
);
|
|
48
|
+
}, [userChannels]);
|
|
49
|
+
|
|
38
50
|
if (userChannelsLoading) {
|
|
39
51
|
return (
|
|
40
52
|
<div className="space-y-4">
|
|
@@ -56,12 +68,8 @@ export const LeftSidebar = (props: LeftSidebarProps) => {
|
|
|
56
68
|
<>
|
|
57
69
|
{supportServices ? supportServices : <></>}
|
|
58
70
|
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
uniqBy([...userChannels], ({ id }) => id),
|
|
62
|
-
['updatedAt'],
|
|
63
|
-
['desc'],
|
|
64
|
-
)?.map((channel, index) =>
|
|
71
|
+
{sortedChannels.length > 0 ? (
|
|
72
|
+
sortedChannels.map((channel, index) =>
|
|
65
73
|
channel?.type === RoomType.Service ? (
|
|
66
74
|
<ServiceConversationItem
|
|
67
75
|
key={`service_channel_${channel.id}`}
|
|
@@ -71,7 +79,7 @@ export const LeftSidebar = (props: LeftSidebarProps) => {
|
|
|
71
79
|
handleSelectChannel={handleSelectChannel}
|
|
72
80
|
users={users}
|
|
73
81
|
selectedChannelId={selectedChannelId}
|
|
74
|
-
showBorder={index !=
|
|
82
|
+
showBorder={index != sortedChannels.length - 1}
|
|
75
83
|
role={role || null}
|
|
76
84
|
/>
|
|
77
85
|
) : (
|
|
@@ -83,7 +91,8 @@ export const LeftSidebar = (props: LeftSidebarProps) => {
|
|
|
83
91
|
handleSelectChannel={handleSelectChannel}
|
|
84
92
|
users={users}
|
|
85
93
|
selectedChannelId={selectedChannelId}
|
|
86
|
-
showBorder={index !=
|
|
94
|
+
showBorder={index != sortedChannels.length - 1}
|
|
95
|
+
messagesQuery={messagesQuery}
|
|
87
96
|
/>
|
|
88
97
|
),
|
|
89
98
|
)
|
|
@@ -98,7 +107,7 @@ export const LeftSidebar = (props: LeftSidebarProps) => {
|
|
|
98
107
|
</div>
|
|
99
108
|
</div>
|
|
100
109
|
);
|
|
101
|
-
};
|
|
110
|
+
});
|
|
102
111
|
|
|
103
112
|
const SearchInput = ({ keyword, setKeyword }: any) => {
|
|
104
113
|
const { t } = useTranslation('translations');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useMemo, useState } from 'react';
|
|
1
|
+
import React, { useCallback, useMemo, useState, useRef, useEffect } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { config } from '../../config';
|
|
4
4
|
import { UploadImageButton } from './UploadImageButton';
|
|
@@ -17,8 +17,16 @@ export const MessageInput = ({ handleSend: handleSendProp, placeholder }: Messag
|
|
|
17
17
|
const [showToast, setShowToast] = useState(false);
|
|
18
18
|
const [toastMessage, setToastMessage] = useState('');
|
|
19
19
|
const [isFocused, setIsFocused] = useState(false);
|
|
20
|
+
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
20
21
|
const { t } = useTranslation('translations');
|
|
21
22
|
|
|
23
|
+
// Auto-focus the textarea when component mounts
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (textareaRef.current) {
|
|
26
|
+
textareaRef.current.focus();
|
|
27
|
+
}
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
22
30
|
const showToastMessage = useCallback((message: string) => {
|
|
23
31
|
setToastMessage(message);
|
|
24
32
|
setShowToast(true);
|
|
@@ -33,6 +41,12 @@ export const MessageInput = ({ handleSend: handleSendProp, placeholder }: Messag
|
|
|
33
41
|
.then(() => {
|
|
34
42
|
setMessage('');
|
|
35
43
|
setFiles([]);
|
|
44
|
+
// Auto-focus the textarea after sending a message
|
|
45
|
+
setTimeout(() => {
|
|
46
|
+
if (textareaRef.current) {
|
|
47
|
+
textareaRef.current.focus();
|
|
48
|
+
}
|
|
49
|
+
}, 100);
|
|
36
50
|
})
|
|
37
51
|
.finally(() => setSending(false));
|
|
38
52
|
}, [files, handleSendProp, message]);
|
|
@@ -114,6 +128,7 @@ export const MessageInput = ({ handleSend: handleSendProp, placeholder }: Messag
|
|
|
114
128
|
|
|
115
129
|
{/* Textarea */}
|
|
116
130
|
<textarea
|
|
131
|
+
ref={textareaRef}
|
|
117
132
|
className="w-full text-base pl-14 pr-20 py-3 bg-transparent border-none resize-none overflow-hidden placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-0 scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent"
|
|
118
133
|
style={{
|
|
119
134
|
height: `${inputHeight}px`,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useEffect, useRef } 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
|
+
|
|
31
|
+
console.log('SubscriptionHandler: Setting up subscription with variables:', variables);
|
|
32
|
+
|
|
33
|
+
const unsubscribe = subscribeToMore({
|
|
34
|
+
document,
|
|
35
|
+
variables,
|
|
36
|
+
updateQuery,
|
|
37
|
+
onError,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
console.log('SubscriptionHandler: Subscription setup successful, unsubscribe function:', unsubscribe);
|
|
41
|
+
|
|
42
|
+
return () => {
|
|
43
|
+
console.log('SubscriptionHandler: Cleaning up subscription');
|
|
44
|
+
if (unsubscribe && typeof unsubscribe === 'function') {
|
|
45
|
+
try {
|
|
46
|
+
unsubscribe();
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error('Error unsubscribing:', error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}, [subscribeToMore, document, variables, updateQuery, onError, enabled]);
|
|
53
|
+
|
|
54
|
+
return null;
|
|
55
|
+
}
|