@messenger-box/tailwind-ui-inbox 10.0.3-alpha.65 → 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.
Files changed (38) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/lib/components/InboxMessage/ConversationItem.d.ts +10 -7
  3. package/lib/components/InboxMessage/ConversationItem.d.ts.map +1 -1
  4. package/lib/components/InboxMessage/ConversationItem.js +58 -77
  5. package/lib/components/InboxMessage/ConversationItem.js.map +1 -1
  6. package/lib/components/InboxMessage/LeftSidebar.d.ts +2 -1
  7. package/lib/components/InboxMessage/LeftSidebar.d.ts.map +1 -1
  8. package/lib/components/InboxMessage/LeftSidebar.js +15 -8
  9. package/lib/components/InboxMessage/LeftSidebar.js.map +1 -1
  10. package/lib/components/InboxMessage/MessageInput.d.ts.map +1 -1
  11. package/lib/components/InboxMessage/MessageInput.js +15 -1
  12. package/lib/components/InboxMessage/MessageInput.js.map +1 -1
  13. package/lib/components/InboxMessage/SubscriptionHandler.d.ts +19 -0
  14. package/lib/components/InboxMessage/SubscriptionHandler.d.ts.map +1 -0
  15. package/lib/components/InboxMessage/SubscriptionHandler.js +41 -0
  16. package/lib/components/InboxMessage/SubscriptionHandler.js.map +1 -0
  17. package/lib/container/Inbox.d.ts.map +1 -1
  18. package/lib/container/Inbox.js +49 -28
  19. package/lib/container/Inbox.js.map +1 -1
  20. package/lib/container/InboxWithLoader.d.ts +10 -3
  21. package/lib/container/InboxWithLoader.d.ts.map +1 -1
  22. package/lib/container/InboxWithLoader.js +81 -30
  23. package/lib/container/InboxWithLoader.js.map +1 -1
  24. package/lib/container/ServiceInbox.js +1 -1
  25. package/lib/container/ServiceInbox.js.map +1 -1
  26. package/lib/container/ThreadMessages.js +1 -1
  27. package/lib/container/ThreadMessages.js.map +1 -1
  28. package/lib/container/ThreadMessagesInbox.js +1 -1
  29. package/lib/container/ThreadMessagesInbox.js.map +1 -1
  30. package/lib/container/Threads.js +1 -1
  31. package/lib/container/Threads.js.map +1 -1
  32. package/package.json +4 -4
  33. package/src/components/InboxMessage/ConversationItem.tsx +188 -186
  34. package/src/components/InboxMessage/LeftSidebar.tsx +20 -11
  35. package/src/components/InboxMessage/MessageInput.tsx +16 -1
  36. package/src/components/InboxMessage/SubscriptionHandler.tsx +55 -0
  37. package/src/container/Inbox.tsx +53 -35
  38. package/src/container/InboxWithLoader.tsx +104 -38
@@ -18,6 +18,7 @@ import { objectId } from '@messenger-box/core';
18
18
  import { ThreadsInbox } from './ThreadsInbox';
19
19
  import { ThreadMessagesInbox } from './ThreadMessagesInbox';
20
20
  import { useApolloClient } from '@apollo/client';
21
+ import { SubscriptionHandler } from '../components/InboxMessage/SubscriptionHandler';
21
22
 
22
23
  const { MESSAGES_PER_PAGE } = config;
23
24
 
@@ -151,11 +152,16 @@ const Inbox = (props: InboxProps) => {
151
152
  return uniqBy([...(userChannels?.supportServiceChannels ?? []), ...(userChannels?.channelsByUser ?? [])], 'id');
152
153
  }, [userChannels]);
153
154
 
155
+ // Memoize stable channel array to prevent unnecessary re-renders
156
+ const stableChannels = useMemo(() => {
157
+ return channels || [];
158
+ }, [channels]);
159
+
154
160
  // Memoized values derived from Apollo cache data
155
161
  const channelFilters = useMemo(() => {
156
162
  const filters = { ...channelFilterProp };
157
163
  const channelType = filters?.type ?? RoomType.Direct;
158
- filters.type = supportServices ? [channelType, RoomType.Service] : RoomType.Direct;
164
+ filters.type = supportServices ? [channelType, RoomType.Service] : channelType;
159
165
  return filters;
160
166
  }, [channelFilterProp, supportServices]);
161
167
 
@@ -168,10 +174,11 @@ const Inbox = (props: InboxProps) => {
168
174
  );
169
175
  }, [channels]);
170
176
 
171
- const currentUser = useMemo(
172
- () => users?.find((user) => user && user.alias?.includes(auth?.authUserId)),
173
- [users, auth?.authUserId],
174
- );
177
+ // const currentUser = useMemo(
178
+ // () => users?.find((user) => user && user.alias?.includes(auth?.authUserId)),
179
+ // [users, auth?.authUserId],
180
+ // );
181
+ const currentUser = auth;
175
182
 
176
183
  const channelName = useMemo(() => {
177
184
  if (!channels || !pathChannelId) return '';
@@ -226,8 +233,8 @@ const Inbox = (props: InboxProps) => {
226
233
  const basePath = pathPrefix ? `${pathPrefix}${mainPath}` : mainPath;
227
234
 
228
235
  const searchParams = new URLSearchParams();
229
- if (channelRole) searchParams.set('channelRole', channelRole);
230
- if (orgName) searchParams.set('orgName', orgName);
236
+ // if (channelRole) searchParams.set('channelRole', channelRole);
237
+ // if (orgName) searchParams.set('orgName', orgName);
231
238
 
232
239
  const newPath = searchParams.toString() ? `${basePath}?${searchParams.toString()}` : basePath;
233
240
  navigate(newPath, { replace: true });
@@ -282,7 +289,7 @@ const Inbox = (props: InboxProps) => {
282
289
  >
283
290
  <LeftSidebar
284
291
  currentUser={currentUser}
285
- userChannels={channels || []}
292
+ userChannels={stableChannels}
286
293
  userChannelsLoading={userChannelsLoading}
287
294
  users={users}
288
295
  handleSelectChannel={handleSelectChannel}
@@ -290,6 +297,7 @@ const Inbox = (props: InboxProps) => {
290
297
  channelToTop={0}
291
298
  getChannelsRefetch={getChannelsRefetch}
292
299
  role={channelRole}
300
+ messagesQuery={data?.[1]}
293
301
  />
294
302
  </div>
295
303
 
@@ -764,33 +772,43 @@ const MessagesComponent = React.memo((props: any) => {
764
772
  onScroll={onMessagesScroll}
765
773
  >
766
774
  {messages.length > 0 ? (
767
- <Messages
768
- innerRef={messageListRef}
769
- channelId={channelId}
770
- currentUser={auth}
771
- channelMessages={messages}
772
- totalCount={totalCount}
773
- onMessageClick={onMessageClick}
774
- subscribeToNewMessages={() =>
775
- subscribeToMore?.({
776
- document: CHAT_MESSAGE_ADDED,
777
- variables: { channelId: channelId.toString() },
778
- updateQuery: (prev: any, { subscriptionData }: any) => {
779
- if (!subscriptionData.data) return prev;
780
- const newMessage = subscriptionData.data.chatMessageAdded;
781
-
782
- return {
783
- ...prev,
784
- messages: {
785
- ...prev?.messages,
786
- data: uniqBy([...(prev?.messages?.data || []), newMessage], 'id'),
787
- totalCount: (prev?.messages?.totalCount || 0) + 1,
788
- },
789
- };
790
- },
791
- })
792
- }
793
- />
775
+ <>
776
+ <Messages
777
+ innerRef={messageListRef}
778
+ channelId={channelId}
779
+ currentUser={auth}
780
+ channelMessages={messages}
781
+ totalCount={totalCount}
782
+ onMessageClick={onMessageClick}
783
+ />
784
+ <SubscriptionHandler
785
+ subscribeToMore={subscribeToMore}
786
+ document={CHAT_MESSAGE_ADDED}
787
+ variables={{ channelId: channelId.toString() }}
788
+ enabled={!!channelId && !!subscribeToMore}
789
+ updateQuery={(prev: any, { subscriptionData }: any) => {
790
+ console.log('Subscription updateQuery called:', { prev, subscriptionData });
791
+ if (!subscriptionData.data) {
792
+ console.log('No subscription data, returning prev');
793
+ return prev;
794
+ }
795
+ const newMessage = subscriptionData.data.chatMessageAdded;
796
+ console.log('New message received via subscription:', newMessage);
797
+
798
+ return {
799
+ ...prev,
800
+ messages: {
801
+ ...prev?.messages,
802
+ data: uniqBy([...(prev?.messages?.data || []), newMessage], 'id'),
803
+ totalCount: (prev?.messages?.totalCount || 0) + 1,
804
+ },
805
+ };
806
+ }}
807
+ onError={(error) => {
808
+ console.error('Subscription error:', error);
809
+ }}
810
+ />
811
+ </>
794
812
  ) : (
795
813
  <div className="flex-1 flex items-center justify-center text-gray-500">
796
814
  <div className="text-center">
@@ -1,23 +1,38 @@
1
- import React, { useMemo } from 'react';
1
+ import React, { useMemo, useCallback } from 'react';
2
2
  import { useParams, useLocation } from '@remix-run/react';
3
3
 
4
4
  // import AddGovernmentId from './AddGovernmentId';
5
5
  import Inbox from './Inbox';
6
- import { useGetChannelsByUserQuery, useMessagesQuery, useViewChannelDetailQuery } from 'common/graphql';
6
+ import {
7
+ useGetChannelsByUserQuery,
8
+ useGetChannelsByUserWithLastMessageQuery,
9
+ useMessagesQuery,
10
+ useViewChannelDetailQuery,
11
+ } from 'common/graphql';
7
12
  import { RoomType } from 'common';
8
13
  import { config } from '../config';
9
14
 
10
15
  const { MESSAGES_PER_PAGE } = config;
11
16
 
12
- // Static utility function moved outside component - exported for external use
13
- export const queryParamsGenerator = (params) => ({
17
+ // Enhanced query parameters generator with better typing and flexibility
18
+ export const queryParamsGenerator = (params: {
19
+ role?: string;
20
+ criteria?: Record<string, any>;
21
+ supportServices?: boolean;
22
+ orgName?: string;
23
+ }) => ({
14
24
  variable1: {
15
25
  role: params.role,
16
- criteria: params.criteria,
17
- supportServices: params.supportServices ? true : false,
18
- supportServiceCriteria: {
19
- type: RoomType.Service,
26
+ criteria: {
27
+ ...params.criteria,
28
+ ...(params.orgName && { orgName: params.orgName }),
20
29
  },
30
+ supportServices: params.supportServices ? true : false,
31
+ supportServiceCriteria: params.supportServices
32
+ ? {
33
+ type: RoomType.Service,
34
+ }
35
+ : undefined,
21
36
  orderBy: {
22
37
  lastPostAt: 'desc',
23
38
  },
@@ -172,28 +187,35 @@ const InboxWithLoader = (props: InboxWithLoaderProps) => {
172
187
  const { channelFilters: channelFilterProp, channelRole: channelRoleProp, supportServices, pathPrefix } = props;
173
188
  const { orgName, channelRole: channelRoleParam } = useParams();
174
189
  const channelRole = channelRoleParam || channelRoleProp;
175
- // Get full location info
190
+
176
191
  // Create new props object with orgName instead of mutating original props
177
- const inboxProps = {
178
- ...props,
179
- orgName: orgName || '',
180
- pathPrefix: pathPrefix || '',
181
- };
192
+ const inboxProps = useMemo(
193
+ () => ({
194
+ ...props,
195
+ orgName: orgName || '',
196
+ pathPrefix: pathPrefix || '',
197
+ }),
198
+ [props, orgName, pathPrefix],
199
+ );
182
200
 
183
201
  const { id: pathChannelId } = useParams();
184
202
 
185
- // Optimized query variables with stable references
186
- const channelsQueryVariables = useMemo(
187
- () =>
188
- queryParamsGenerator({
189
- role: channelRole,
190
- criteria: orgName
191
- ? { ...channelFilterProp, orgName: channelFilterProp?.orgName || orgName || '' }
192
- : channelFilterProp,
193
- supportServices,
194
- }).variable1,
195
- [channelRole, channelFilterProp, supportServices, orgName],
196
- );
203
+ // Enhanced query variables with better filtering and stable references
204
+ const channelsQueryVariables = useMemo(() => {
205
+ const channelFilters = { ...channelFilterProp };
206
+ const channelType = channelFilters?.type ?? RoomType.Direct;
207
+ const baseFilters = {
208
+ ...channelFilters,
209
+ type: supportServices ? [channelType, RoomType.Service] : channelType,
210
+ };
211
+
212
+ return queryParamsGenerator({
213
+ role: channelRole,
214
+ criteria: baseFilters,
215
+ supportServices,
216
+ orgName: orgName || '',
217
+ }).variable1;
218
+ }, [channelRole, channelFilterProp, supportServices, orgName]);
197
219
 
198
220
  const messagesQueryVariables = useMemo(
199
221
  () => ({
@@ -211,43 +233,67 @@ const InboxWithLoader = (props: InboxWithLoaderProps) => {
211
233
  [pathChannelId],
212
234
  );
213
235
 
214
- // Apollo queries with optimized cache policies
236
+ // Enhanced Apollo queries with better cache policies and error handling
215
237
  const channelsQuery = useGetChannelsByUserQuery({
216
238
  variables: channelsQueryVariables,
217
- fetchPolicy: 'cache-first',
239
+ fetchPolicy: 'cache-and-network', // Better for real-time updates
218
240
  errorPolicy: 'all',
219
- notifyOnNetworkStatusChange: true,
220
- // Enable optimistic updates and real-time subscriptions
241
+ notifyOnNetworkStatusChange: false,
221
242
  returnPartialData: true,
243
+ // Add context for better cache management
244
+ context: {
245
+ cacheKey: 'channels-list',
246
+ },
222
247
  });
223
248
 
224
249
  const messagesQuery = useMessagesQuery({
225
250
  variables: messagesQueryVariables,
226
251
  skip: !pathChannelId,
227
- fetchPolicy: 'cache-and-network', // Changed to cache-and-network for better UX
252
+ fetchPolicy: 'cache-and-network', // Better for real-time messaging
228
253
  errorPolicy: 'all',
229
254
  notifyOnNetworkStatusChange: true,
230
255
  returnPartialData: true,
231
- // Enable polling for real-time updates as fallback
232
- pollInterval: process.env.NODE_ENV === 'development' ? 0 : 30000,
256
+ // Disable polling to allow subscriptions to work properly
257
+ pollInterval: 0,
258
+ // Add context for better cache management
259
+ context: {
260
+ cacheKey: 'messages-list',
261
+ },
233
262
  });
234
263
 
235
264
  const channelDetailQuery = useViewChannelDetailQuery({
236
265
  variables: channelDetailQueryVariables,
237
266
  skip: !pathChannelId,
238
- fetchPolicy: 'cache-first',
267
+ fetchPolicy: 'cache-first', // Channel details don't change often
239
268
  errorPolicy: 'all',
240
- notifyOnNetworkStatusChange: true,
269
+ notifyOnNetworkStatusChange: false, // No need for real-time updates
241
270
  returnPartialData: true,
271
+ // Add context for better cache management
272
+ context: {
273
+ cacheKey: 'channel-detail',
274
+ },
242
275
  });
243
276
 
244
- // Create loader data array for Inbox component
277
+ // Enhanced error handling with retry logic
278
+ const handleRetry = useCallback(() => {
279
+ if (channelsQuery.error) {
280
+ channelsQuery.refetch();
281
+ }
282
+ if (messagesQuery.error) {
283
+ messagesQuery.refetch();
284
+ }
285
+ if (channelDetailQuery.error) {
286
+ channelDetailQuery.refetch();
287
+ }
288
+ }, [channelsQuery, messagesQuery, channelDetailQuery]);
289
+
290
+ // Create loader data array for Inbox component with better error handling
245
291
  const loaderData = useMemo(
246
292
  () => [channelsQuery, messagesQuery, channelDetailQuery],
247
293
  [channelsQuery, messagesQuery, channelDetailQuery],
248
294
  );
249
295
 
250
- // Enhanced loading states with skeleton
296
+ // Enhanced loading states with better UX
251
297
  if (channelsQuery.loading && !pathChannelId && !channelsQuery.data) {
252
298
  return <InboxSkeleton showRightSidebar={false} />;
253
299
  }
@@ -262,8 +308,28 @@ const InboxWithLoader = (props: InboxWithLoaderProps) => {
262
308
  return <InboxSkeleton showRightSidebar={true} />;
263
309
  }
264
310
 
311
+ // Enhanced error handling with retry option
265
312
  if (channelsQuery.error && !channelsQuery.data) {
266
- return <TailwindAlert>Error loading channels: {channelsQuery.error.message}</TailwindAlert>;
313
+ return (
314
+ <div className="flex flex-col items-center justify-center min-h-screen space-y-4">
315
+ <TailwindAlert>Error loading channels: {channelsQuery.error.message}</TailwindAlert>
316
+ <button
317
+ onClick={handleRetry}
318
+ className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
319
+ >
320
+ Retry
321
+ </button>
322
+ </div>
323
+ );
324
+ }
325
+
326
+ // Handle individual query errors gracefully
327
+ if (messagesQuery.error && !messagesQuery.data && pathChannelId) {
328
+ console.warn('Messages query error:', messagesQuery.error);
329
+ }
330
+
331
+ if (channelDetailQuery.error && !channelDetailQuery.data && pathChannelId) {
332
+ console.warn('Channel detail query error:', channelDetailQuery.error);
267
333
  }
268
334
 
269
335
  return <Inbox data={loaderData} {...inboxProps} />;