hazo_chat 2.0.15 → 2.1.0

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 (155) hide show
  1. package/README.md +60 -8
  2. package/SETUP_CHECKLIST.md +37 -0
  3. package/dist/api/index.d.ts +26 -0
  4. package/dist/api/index.d.ts.map +1 -0
  5. package/dist/api/index.js +25 -0
  6. package/dist/api/index.js.map +1 -0
  7. package/dist/api/messages.d.ts +52 -0
  8. package/dist/api/messages.d.ts.map +1 -0
  9. package/dist/api/messages.js +330 -0
  10. package/dist/api/messages.js.map +1 -0
  11. package/dist/api/types.d.ts +69 -0
  12. package/dist/api/types.d.ts.map +1 -0
  13. package/dist/api/types.js +8 -0
  14. package/dist/api/types.js.map +1 -0
  15. package/dist/api/unread_count.d.ts +57 -0
  16. package/dist/api/unread_count.d.ts.map +1 -0
  17. package/dist/api/unread_count.js +86 -0
  18. package/dist/api/unread_count.js.map +1 -0
  19. package/dist/components/hazo_chat/hazo_chat.d.ts +28 -0
  20. package/dist/components/hazo_chat/hazo_chat.d.ts.map +1 -0
  21. package/dist/components/hazo_chat/hazo_chat.js +220 -0
  22. package/dist/components/hazo_chat/hazo_chat.js.map +1 -0
  23. package/dist/components/hazo_chat/hazo_chat_attachment_preview.d.ts +17 -0
  24. package/dist/components/hazo_chat/hazo_chat_attachment_preview.d.ts.map +1 -0
  25. package/dist/components/hazo_chat/hazo_chat_attachment_preview.js +60 -0
  26. package/dist/components/hazo_chat/hazo_chat_attachment_preview.js.map +1 -0
  27. package/dist/components/hazo_chat/hazo_chat_context.d.ts +42 -0
  28. package/dist/components/hazo_chat/hazo_chat_context.d.ts.map +1 -0
  29. package/dist/components/hazo_chat/hazo_chat_context.js +273 -0
  30. package/dist/components/hazo_chat/hazo_chat_context.js.map +1 -0
  31. package/dist/components/hazo_chat/hazo_chat_document_viewer.d.ts +15 -0
  32. package/dist/components/hazo_chat/hazo_chat_document_viewer.d.ts.map +1 -0
  33. package/dist/components/hazo_chat/hazo_chat_document_viewer.js +140 -0
  34. package/dist/components/hazo_chat/hazo_chat_document_viewer.js.map +1 -0
  35. package/dist/components/hazo_chat/hazo_chat_header.d.ts +16 -0
  36. package/dist/components/hazo_chat/hazo_chat_header.d.ts.map +1 -0
  37. package/dist/components/hazo_chat/hazo_chat_header.js +32 -0
  38. package/dist/components/hazo_chat/hazo_chat_header.js.map +1 -0
  39. package/dist/components/hazo_chat/hazo_chat_input.d.ts +16 -0
  40. package/dist/components/hazo_chat/hazo_chat_input.d.ts.map +1 -0
  41. package/dist/components/hazo_chat/hazo_chat_input.js +75 -0
  42. package/dist/components/hazo_chat/hazo_chat_input.js.map +1 -0
  43. package/dist/components/hazo_chat/hazo_chat_messages.d.ts +17 -0
  44. package/dist/components/hazo_chat/hazo_chat_messages.d.ts.map +1 -0
  45. package/dist/components/hazo_chat/hazo_chat_messages.js +215 -0
  46. package/dist/components/hazo_chat/hazo_chat_messages.js.map +1 -0
  47. package/dist/components/hazo_chat/hazo_chat_reference_list.d.ts +16 -0
  48. package/dist/components/hazo_chat/hazo_chat_reference_list.d.ts.map +1 -0
  49. package/dist/components/hazo_chat/hazo_chat_reference_list.js +59 -0
  50. package/dist/components/hazo_chat/hazo_chat_reference_list.js.map +1 -0
  51. package/dist/components/hazo_chat/hazo_chat_sidebar.d.ts +18 -0
  52. package/dist/components/hazo_chat/hazo_chat_sidebar.d.ts.map +1 -0
  53. package/dist/components/hazo_chat/hazo_chat_sidebar.js +72 -0
  54. package/dist/components/hazo_chat/hazo_chat_sidebar.js.map +1 -0
  55. package/dist/components/hazo_chat/index.d.ts +16 -0
  56. package/dist/components/hazo_chat/index.d.ts.map +1 -0
  57. package/dist/components/hazo_chat/index.js +19 -0
  58. package/dist/components/hazo_chat/index.js.map +1 -0
  59. package/dist/components/index.d.ts +9 -0
  60. package/dist/components/index.d.ts.map +1 -0
  61. package/dist/components/index.js +11 -0
  62. package/dist/components/index.js.map +1 -0
  63. package/dist/components/ui/avatar.d.ts +13 -0
  64. package/dist/components/ui/avatar.d.ts.map +1 -0
  65. package/dist/components/ui/avatar.js +28 -0
  66. package/dist/components/ui/avatar.js.map +1 -0
  67. package/dist/components/ui/badge.d.ts +16 -0
  68. package/dist/components/ui/badge.d.ts.map +1 -0
  69. package/dist/components/ui/badge.js +36 -0
  70. package/dist/components/ui/badge.js.map +1 -0
  71. package/dist/components/ui/button.d.ts +18 -0
  72. package/dist/components/ui/button.d.ts.map +1 -0
  73. package/dist/components/ui/button.js +47 -0
  74. package/dist/components/ui/button.js.map +1 -0
  75. package/dist/components/ui/chat_bubble.d.ts +18 -0
  76. package/dist/components/ui/chat_bubble.d.ts.map +1 -0
  77. package/dist/components/ui/chat_bubble.js +130 -0
  78. package/dist/components/ui/chat_bubble.js.map +1 -0
  79. package/dist/components/ui/hover-card.d.ts +13 -0
  80. package/dist/components/ui/hover-card.d.ts.map +1 -0
  81. package/dist/components/ui/hover-card.js +17 -0
  82. package/dist/components/ui/hover-card.js.map +1 -0
  83. package/dist/components/ui/index.d.ts +19 -0
  84. package/dist/components/ui/index.d.ts.map +1 -0
  85. package/dist/components/ui/index.js +21 -0
  86. package/dist/components/ui/index.js.map +1 -0
  87. package/dist/components/ui/input.d.ts +11 -0
  88. package/dist/components/ui/input.d.ts.map +1 -0
  89. package/dist/components/ui/input.js +18 -0
  90. package/dist/components/ui/input.js.map +1 -0
  91. package/dist/components/ui/loading_skeleton.d.ts +19 -0
  92. package/dist/components/ui/loading_skeleton.d.ts.map +1 -0
  93. package/dist/components/ui/loading_skeleton.js +30 -0
  94. package/dist/components/ui/loading_skeleton.js.map +1 -0
  95. package/dist/components/ui/scroll-area.d.ts +12 -0
  96. package/dist/components/ui/scroll-area.d.ts.map +1 -0
  97. package/dist/components/ui/scroll-area.js +25 -0
  98. package/dist/components/ui/scroll-area.js.map +1 -0
  99. package/dist/components/ui/separator.d.ts +11 -0
  100. package/dist/components/ui/separator.d.ts.map +1 -0
  101. package/dist/components/ui/separator.js +18 -0
  102. package/dist/components/ui/separator.js.map +1 -0
  103. package/dist/components/ui/skeleton.d.ts +9 -0
  104. package/dist/components/ui/skeleton.d.ts.map +1 -0
  105. package/dist/components/ui/skeleton.js +16 -0
  106. package/dist/components/ui/skeleton.js.map +1 -0
  107. package/dist/components/ui/textarea.d.ts +11 -0
  108. package/dist/components/ui/textarea.d.ts.map +1 -0
  109. package/dist/components/ui/textarea.js +18 -0
  110. package/dist/components/ui/textarea.js.map +1 -0
  111. package/dist/components/ui/tooltip.d.ts +14 -0
  112. package/dist/components/ui/tooltip.d.ts.map +1 -0
  113. package/dist/components/ui/tooltip.js +30 -0
  114. package/dist/components/ui/tooltip.js.map +1 -0
  115. package/dist/hooks/index.d.ts +10 -0
  116. package/dist/hooks/index.d.ts.map +1 -0
  117. package/dist/hooks/index.js +10 -0
  118. package/dist/hooks/index.js.map +1 -0
  119. package/dist/hooks/use_chat_messages.d.ts +33 -0
  120. package/dist/hooks/use_chat_messages.d.ts.map +1 -0
  121. package/dist/hooks/use_chat_messages.js +485 -0
  122. package/dist/hooks/use_chat_messages.js.map +1 -0
  123. package/dist/hooks/use_chat_references.d.ts +17 -0
  124. package/dist/hooks/use_chat_references.d.ts.map +1 -0
  125. package/dist/hooks/use_chat_references.js +133 -0
  126. package/dist/hooks/use_chat_references.js.map +1 -0
  127. package/dist/hooks/use_file_upload.d.ts +23 -0
  128. package/dist/hooks/use_file_upload.d.ts.map +1 -0
  129. package/dist/hooks/use_file_upload.js +212 -0
  130. package/dist/hooks/use_file_upload.js.map +1 -0
  131. package/dist/index.d.ts +13 -0
  132. package/dist/index.d.ts.map +1 -0
  133. package/dist/index.js +17 -0
  134. package/dist/index.js.map +1 -0
  135. package/dist/lib/config.d.ts +41 -0
  136. package/dist/lib/config.d.ts.map +1 -0
  137. package/dist/lib/config.js +93 -0
  138. package/dist/lib/config.js.map +1 -0
  139. package/dist/lib/constants.d.ts +41 -0
  140. package/dist/lib/constants.d.ts.map +1 -0
  141. package/dist/lib/constants.js +72 -0
  142. package/dist/lib/constants.js.map +1 -0
  143. package/dist/lib/index.d.ts +9 -0
  144. package/dist/lib/index.d.ts.map +1 -0
  145. package/dist/lib/index.js +9 -0
  146. package/dist/lib/index.js.map +1 -0
  147. package/dist/lib/utils.d.ts +17 -0
  148. package/dist/lib/utils.d.ts.map +1 -0
  149. package/dist/lib/utils.js +20 -0
  150. package/dist/lib/utils.js.map +1 -0
  151. package/dist/types/index.d.ts +405 -0
  152. package/dist/types/index.d.ts.map +1 -0
  153. package/dist/types/index.js +11 -0
  154. package/dist/types/index.js.map +1 -0
  155. package/package.json +1 -1
@@ -0,0 +1,485 @@
1
+ /**
2
+ * useChatMessages Hook
3
+ *
4
+ * Manages chat messages with:
5
+ * - Cursor-based pagination (infinite scroll)
6
+ * - Polling for new messages with configurable interval
7
+ * - Optimistic updates for sent messages
8
+ * - Soft delete functionality
9
+ * - Exponential backoff on errors
10
+ *
11
+ * Uses fetch() to call API endpoints instead of direct database access.
12
+ * This allows the hook to work in client components without Node.js dependencies.
13
+ */
14
+ 'use client';
15
+ import { useState, useEffect, useCallback, useRef } from 'react';
16
+ import { DEFAULT_REALTIME_MODE, DEFAULT_POLLING_INTERVAL, DEFAULT_MESSAGES_PER_PAGE, MAX_RETRY_ATTEMPTS, RETRY_BASE_DELAY } from '../lib/constants.js';
17
+ // ============================================================================
18
+ // Hook Implementation
19
+ // ============================================================================
20
+ export function useChatMessages({ receiver_user_id, reference_id = '', reference_type = 'chat', api_base_url = '/api/hazo_chat', realtime_mode = DEFAULT_REALTIME_MODE, polling_interval = DEFAULT_POLLING_INTERVAL, messages_per_page = DEFAULT_MESSAGES_PER_PAGE }) {
21
+ // -------------------------------------------------------------------------
22
+ // State
23
+ // -------------------------------------------------------------------------
24
+ const [messages, set_messages] = useState([]);
25
+ const [is_loading, set_is_loading] = useState(true);
26
+ const [is_loading_more, set_is_loading_more] = useState(false);
27
+ const [has_more, set_has_more] = useState(true);
28
+ const [error, set_error] = useState(null);
29
+ const [polling_status, set_polling_status] = useState('connected');
30
+ const [current_user_id, set_current_user_id] = useState(null);
31
+ // -------------------------------------------------------------------------
32
+ // Refs
33
+ // -------------------------------------------------------------------------
34
+ const cursor_ref = useRef(0);
35
+ const retry_count_ref = useRef(0);
36
+ const user_profiles_cache_ref = useRef(new Map());
37
+ const polling_timer_ref = useRef(null);
38
+ const is_mounted_ref = useRef(true);
39
+ // -------------------------------------------------------------------------
40
+ // Cleanup on unmount
41
+ // -------------------------------------------------------------------------
42
+ useEffect(() => {
43
+ is_mounted_ref.current = true;
44
+ return () => {
45
+ is_mounted_ref.current = false;
46
+ if (polling_timer_ref.current) {
47
+ clearInterval(polling_timer_ref.current);
48
+ }
49
+ };
50
+ }, []);
51
+ // -------------------------------------------------------------------------
52
+ // Fetch user profiles via API
53
+ // -------------------------------------------------------------------------
54
+ const fetch_user_profiles = useCallback(async (user_ids) => {
55
+ const uncached_ids = user_ids.filter((id) => !user_profiles_cache_ref.current.has(id));
56
+ if (uncached_ids.length > 0) {
57
+ try {
58
+ // Use the hazo_auth profiles endpoint
59
+ const response = await fetch('/api/hazo_auth/profiles', {
60
+ method: 'POST',
61
+ headers: { 'Content-Type': 'application/json' },
62
+ body: JSON.stringify({ user_ids: uncached_ids }),
63
+ credentials: 'include'
64
+ });
65
+ if (response.ok) {
66
+ const data = await response.json();
67
+ if (data.success && data.profiles) {
68
+ data.profiles.forEach((profile) => {
69
+ user_profiles_cache_ref.current.set(profile.id, profile);
70
+ });
71
+ }
72
+ }
73
+ }
74
+ catch (err) {
75
+ console.error('[useChatMessages] Failed to fetch user profiles:', err);
76
+ }
77
+ }
78
+ return user_profiles_cache_ref.current;
79
+ }, []);
80
+ // -------------------------------------------------------------------------
81
+ // Transform DB messages to ChatMessage
82
+ // -------------------------------------------------------------------------
83
+ const transform_messages = useCallback(async (db_messages, user_id) => {
84
+ // Collect all unique user IDs
85
+ const user_ids = new Set();
86
+ db_messages.forEach((msg) => {
87
+ user_ids.add(msg.sender_user_id);
88
+ user_ids.add(msg.receiver_user_id);
89
+ });
90
+ // Fetch profiles
91
+ const profiles = await fetch_user_profiles(Array.from(user_ids));
92
+ // Transform messages
93
+ return db_messages.map((msg) => ({
94
+ ...msg,
95
+ sender_profile: profiles.get(msg.sender_user_id),
96
+ receiver_profile: profiles.get(msg.receiver_user_id),
97
+ is_sender: msg.sender_user_id === user_id,
98
+ send_status: 'sent'
99
+ }));
100
+ }, [fetch_user_profiles]);
101
+ // -------------------------------------------------------------------------
102
+ // Fetch messages via API
103
+ // -------------------------------------------------------------------------
104
+ const fetch_messages_from_api = useCallback(async () => {
105
+ if (!receiver_user_id) {
106
+ return { messages: [], user_id: null };
107
+ }
108
+ try {
109
+ const params = new URLSearchParams({
110
+ receiver_user_id,
111
+ ...(reference_id && { reference_id }),
112
+ ...(reference_type && { reference_type }),
113
+ });
114
+ const response = await fetch(`${api_base_url}/messages?${params.toString()}`, {
115
+ credentials: 'include'
116
+ });
117
+ if (!response.ok) {
118
+ throw new Error(`HTTP ${response.status}`);
119
+ }
120
+ const data = await response.json();
121
+ if (data.success) {
122
+ return {
123
+ messages: data.messages || [],
124
+ user_id: data.current_user_id || null
125
+ };
126
+ }
127
+ else {
128
+ throw new Error(data.error || 'Failed to fetch messages');
129
+ }
130
+ }
131
+ catch (err) {
132
+ console.error('[useChatMessages] Fetch error:', err);
133
+ throw err;
134
+ }
135
+ }, [receiver_user_id, reference_id, reference_type, api_base_url]);
136
+ // -------------------------------------------------------------------------
137
+ // Initial load
138
+ // -------------------------------------------------------------------------
139
+ const load_initial = useCallback(async () => {
140
+ if (!receiver_user_id) {
141
+ set_is_loading(false);
142
+ return;
143
+ }
144
+ set_is_loading(true);
145
+ set_error(null);
146
+ try {
147
+ const { messages: db_messages, user_id } = await fetch_messages_from_api();
148
+ if (user_id && is_mounted_ref.current) {
149
+ set_current_user_id(user_id);
150
+ }
151
+ if (is_mounted_ref.current) {
152
+ const transformed = user_id
153
+ ? await transform_messages(db_messages, user_id)
154
+ : db_messages.map(msg => ({ ...msg, is_sender: false, send_status: 'sent' }));
155
+ // Sort messages in ascending order (oldest first, newest last)
156
+ const sorted = transformed.sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
157
+ set_messages(sorted);
158
+ set_has_more(db_messages.length >= messages_per_page);
159
+ cursor_ref.current = db_messages.length;
160
+ retry_count_ref.current = 0;
161
+ set_polling_status('connected');
162
+ }
163
+ }
164
+ catch (err) {
165
+ if (is_mounted_ref.current) {
166
+ set_error('Failed to load messages');
167
+ set_polling_status('error');
168
+ }
169
+ }
170
+ finally {
171
+ if (is_mounted_ref.current) {
172
+ set_is_loading(false);
173
+ }
174
+ }
175
+ }, [receiver_user_id, fetch_messages_from_api, transform_messages, messages_per_page]);
176
+ // -------------------------------------------------------------------------
177
+ // Load more (pagination) - Note: simplified, API should support pagination params
178
+ // -------------------------------------------------------------------------
179
+ const load_more = useCallback(async () => {
180
+ if (!current_user_id || !has_more || is_loading_more) {
181
+ return;
182
+ }
183
+ set_is_loading_more(true);
184
+ try {
185
+ // For now, reload all messages - pagination can be added to API later
186
+ const { messages: db_messages } = await fetch_messages_from_api();
187
+ const transformed = await transform_messages(db_messages, current_user_id);
188
+ if (is_mounted_ref.current) {
189
+ // Sort messages in ascending order (oldest first, newest last)
190
+ const sorted = transformed.sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
191
+ set_messages(sorted);
192
+ set_has_more(false); // Simplified - loaded all
193
+ cursor_ref.current = db_messages.length;
194
+ }
195
+ }
196
+ catch (err) {
197
+ console.error('[useChatMessages] Load more error:', err);
198
+ }
199
+ finally {
200
+ if (is_mounted_ref.current) {
201
+ set_is_loading_more(false);
202
+ }
203
+ }
204
+ }, [current_user_id, has_more, is_loading_more, fetch_messages_from_api, transform_messages]);
205
+ // -------------------------------------------------------------------------
206
+ // Poll for new messages
207
+ // -------------------------------------------------------------------------
208
+ const poll_for_new_messages = useCallback(async () => {
209
+ if (!current_user_id || !receiver_user_id) {
210
+ return;
211
+ }
212
+ try {
213
+ const { messages: db_messages } = await fetch_messages_from_api();
214
+ if (db_messages.length > 0 && is_mounted_ref.current) {
215
+ const transformed = await transform_messages(db_messages, current_user_id);
216
+ set_messages((prev) => {
217
+ // Merge new messages, avoiding duplicates
218
+ const existing_ids = new Set(prev.map(m => m.id));
219
+ const new_messages = transformed.filter(m => !existing_ids.has(m.id));
220
+ if (new_messages.length > 0) {
221
+ // Combine and sort by created_at
222
+ const combined = [...prev, ...new_messages];
223
+ combined.sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
224
+ return combined;
225
+ }
226
+ return prev;
227
+ });
228
+ }
229
+ retry_count_ref.current = 0;
230
+ if (is_mounted_ref.current) {
231
+ set_polling_status('connected');
232
+ }
233
+ }
234
+ catch (err) {
235
+ console.error('[useChatMessages] Polling error:', err);
236
+ retry_count_ref.current += 1;
237
+ if (is_mounted_ref.current) {
238
+ if (retry_count_ref.current >= MAX_RETRY_ATTEMPTS) {
239
+ set_polling_status('error');
240
+ }
241
+ else {
242
+ set_polling_status('reconnecting');
243
+ }
244
+ }
245
+ }
246
+ }, [current_user_id, receiver_user_id, fetch_messages_from_api, transform_messages]);
247
+ // -------------------------------------------------------------------------
248
+ // Start polling (only if realtime_mode is 'polling')
249
+ // -------------------------------------------------------------------------
250
+ useEffect(() => {
251
+ // Only start polling if mode is 'polling'
252
+ if (realtime_mode !== 'polling' || !receiver_user_id) {
253
+ // Clear any existing timer if switching to manual mode
254
+ if (polling_timer_ref.current) {
255
+ clearInterval(polling_timer_ref.current);
256
+ polling_timer_ref.current = null;
257
+ }
258
+ // Set status to connected for manual mode (no polling needed)
259
+ if (realtime_mode === 'manual') {
260
+ set_polling_status('connected');
261
+ }
262
+ return;
263
+ }
264
+ // Clear any existing timer
265
+ if (polling_timer_ref.current) {
266
+ clearInterval(polling_timer_ref.current);
267
+ }
268
+ // Calculate delay with exponential backoff
269
+ const get_poll_delay = () => {
270
+ if (retry_count_ref.current === 0) {
271
+ return polling_interval;
272
+ }
273
+ return Math.min(polling_interval * Math.pow(2, retry_count_ref.current), RETRY_BASE_DELAY * 30 // Cap at 30 seconds
274
+ );
275
+ };
276
+ const start_polling = () => {
277
+ polling_timer_ref.current = setInterval(() => {
278
+ poll_for_new_messages();
279
+ }, get_poll_delay());
280
+ };
281
+ start_polling();
282
+ return () => {
283
+ if (polling_timer_ref.current) {
284
+ clearInterval(polling_timer_ref.current);
285
+ }
286
+ };
287
+ }, [receiver_user_id, realtime_mode, polling_interval, poll_for_new_messages]);
288
+ // -------------------------------------------------------------------------
289
+ // Initial load effect
290
+ // -------------------------------------------------------------------------
291
+ useEffect(() => {
292
+ load_initial();
293
+ }, [load_initial]);
294
+ // -------------------------------------------------------------------------
295
+ // Send message via API
296
+ // -------------------------------------------------------------------------
297
+ const send_message = useCallback(async (payload) => {
298
+ if (!current_user_id) {
299
+ set_error('Not authenticated');
300
+ return false;
301
+ }
302
+ // Create optimistic message
303
+ const optimistic_id = `optimistic-${Date.now()}`;
304
+ const optimistic_message = {
305
+ id: optimistic_id,
306
+ reference_id: payload.reference_id,
307
+ reference_type: payload.reference_type,
308
+ sender_user_id: current_user_id,
309
+ receiver_user_id: payload.receiver_user_id,
310
+ message_text: payload.message_text,
311
+ reference_list: payload.reference_list || null,
312
+ read_at: null,
313
+ deleted_at: null,
314
+ created_at: new Date().toISOString(),
315
+ changed_at: new Date().toISOString(),
316
+ sender_profile: user_profiles_cache_ref.current.get(current_user_id),
317
+ receiver_profile: user_profiles_cache_ref.current.get(payload.receiver_user_id),
318
+ is_sender: true,
319
+ send_status: 'sending'
320
+ };
321
+ // Add optimistic message to state (will be sorted when real message arrives)
322
+ set_messages((prev) => {
323
+ const updated = [...prev, optimistic_message];
324
+ // Sort to maintain chronological order
325
+ return updated.sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
326
+ });
327
+ try {
328
+ const response = await fetch(`${api_base_url}/messages`, {
329
+ method: 'POST',
330
+ headers: { 'Content-Type': 'application/json' },
331
+ credentials: 'include',
332
+ body: JSON.stringify({
333
+ receiver_user_id: payload.receiver_user_id,
334
+ message_text: payload.message_text,
335
+ reference_id: payload.reference_id,
336
+ reference_type: payload.reference_type,
337
+ }),
338
+ });
339
+ const data = await response.json();
340
+ if (data.success && data.message && is_mounted_ref.current) {
341
+ // Replace optimistic message with real one
342
+ const real_message = {
343
+ ...data.message,
344
+ // Ensure all required fields are set with proper defaults
345
+ reference_list: data.message.reference_list ?? null,
346
+ read_at: data.message.read_at ?? null,
347
+ deleted_at: data.message.deleted_at ?? null,
348
+ changed_at: data.message.changed_at ?? data.message.created_at,
349
+ sender_profile: user_profiles_cache_ref.current.get(current_user_id),
350
+ receiver_profile: user_profiles_cache_ref.current.get(payload.receiver_user_id),
351
+ is_sender: true,
352
+ send_status: 'sent'
353
+ };
354
+ set_messages((prev) => {
355
+ // Check if real message already exists (from polling)
356
+ const real_message_exists = prev.some(msg => msg.id === real_message.id);
357
+ if (real_message_exists) {
358
+ // Real message already exists from polling, just remove optimistic one
359
+ return prev.filter(msg => msg.id !== optimistic_id);
360
+ }
361
+ else {
362
+ // Replace optimistic message with real one
363
+ const replaced = prev.map((msg) => msg.id === optimistic_id ? real_message : msg);
364
+ // Sort to ensure correct chronological order
365
+ return replaced.sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
366
+ }
367
+ });
368
+ return true;
369
+ }
370
+ else {
371
+ throw new Error(data.error || 'Failed to send message');
372
+ }
373
+ }
374
+ catch (err) {
375
+ console.error('[useChatMessages] Send error:', err);
376
+ // Mark optimistic message as failed
377
+ if (is_mounted_ref.current) {
378
+ set_messages((prev) => prev.map((msg) => msg.id === optimistic_id
379
+ ? { ...msg, send_status: 'failed' }
380
+ : msg));
381
+ }
382
+ return false;
383
+ }
384
+ }, [current_user_id, api_base_url]);
385
+ // -------------------------------------------------------------------------
386
+ // Delete message (soft delete) via API
387
+ // -------------------------------------------------------------------------
388
+ const delete_message = useCallback(async (message_id) => {
389
+ if (!current_user_id) {
390
+ return false;
391
+ }
392
+ // Find the message to verify ownership
393
+ const message = messages.find((m) => m.id === message_id);
394
+ if (!message || message.sender_user_id !== current_user_id) {
395
+ set_error('Cannot delete this message');
396
+ return false;
397
+ }
398
+ // Optimistic update
399
+ set_messages((prev) => prev.map((msg) => msg.id === message_id
400
+ ? { ...msg, deleted_at: new Date().toISOString(), message_text: null }
401
+ : msg));
402
+ try {
403
+ const response = await fetch(`${api_base_url}/messages/${message_id}`, {
404
+ method: 'DELETE',
405
+ credentials: 'include'
406
+ });
407
+ if (!response.ok) {
408
+ throw new Error('Failed to delete message');
409
+ }
410
+ return true;
411
+ }
412
+ catch (err) {
413
+ console.error('[useChatMessages] Delete error:', err);
414
+ // Rollback on error
415
+ if (is_mounted_ref.current) {
416
+ set_messages((prev) => prev.map((msg) => msg.id === message_id
417
+ ? { ...msg, deleted_at: message.deleted_at, message_text: message.message_text }
418
+ : msg));
419
+ }
420
+ return false;
421
+ }
422
+ }, [current_user_id, messages, api_base_url]);
423
+ // -------------------------------------------------------------------------
424
+ // Mark as read via API
425
+ // -------------------------------------------------------------------------
426
+ const mark_as_read = useCallback(async (message_id) => {
427
+ if (!current_user_id) {
428
+ return;
429
+ }
430
+ const message = messages.find((m) => m.id === message_id);
431
+ if (!message || message.read_at || message.sender_user_id === current_user_id) {
432
+ return;
433
+ }
434
+ try {
435
+ console.log('[useChatMessages] Marking message as read:', message_id);
436
+ const response = await fetch(`${api_base_url}/messages/${message_id}/read`, {
437
+ method: 'PATCH',
438
+ credentials: 'include'
439
+ });
440
+ if (!response.ok) {
441
+ const error_text = await response.text();
442
+ console.error('[useChatMessages] Mark as read failed:', response.status, error_text);
443
+ return;
444
+ }
445
+ const data = await response.json();
446
+ if (data.success && is_mounted_ref.current) {
447
+ console.log('[useChatMessages] Message marked as read successfully:', message_id, data.message?.read_at);
448
+ set_messages((prev) => prev.map((msg) => msg.id === message_id
449
+ ? { ...msg, read_at: data.message?.read_at || new Date().toISOString() }
450
+ : msg));
451
+ }
452
+ else {
453
+ console.error('[useChatMessages] Mark as read response not successful:', data);
454
+ }
455
+ }
456
+ catch (err) {
457
+ console.error('[useChatMessages] Mark as read error:', err);
458
+ }
459
+ }, [current_user_id, messages, api_base_url]);
460
+ // -------------------------------------------------------------------------
461
+ // Refresh
462
+ // -------------------------------------------------------------------------
463
+ const refresh = useCallback(() => {
464
+ cursor_ref.current = 0;
465
+ set_messages([]);
466
+ load_initial();
467
+ }, [load_initial]);
468
+ // -------------------------------------------------------------------------
469
+ // Return
470
+ // -------------------------------------------------------------------------
471
+ return {
472
+ messages,
473
+ is_loading,
474
+ is_loading_more,
475
+ has_more,
476
+ error,
477
+ polling_status,
478
+ load_more,
479
+ send_message,
480
+ delete_message,
481
+ mark_as_read,
482
+ refresh
483
+ };
484
+ }
485
+ //# sourceMappingURL=use_chat_messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use_chat_messages.js","sourceRoot":"","sources":["../../src/hooks/use_chat_messages.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * useChatReferences Hook
3
+ *
4
+ * Manages chat references with:
5
+ * - Aggregating references from messages and props
6
+ * - Selection state management
7
+ * - Finding source message for a reference
8
+ */
9
+ import type { ChatReferenceItem, ChatMessage, ReferenceItem, UseChatReferencesReturn } from '../types/index.js';
10
+ interface UseChatReferencesParams {
11
+ messages: ChatMessage[];
12
+ initial_references?: ReferenceItem[];
13
+ on_selection_change?: (reference: ChatReferenceItem | null) => void;
14
+ }
15
+ export declare function useChatReferences({ messages, initial_references, on_selection_change }: UseChatReferencesParams): UseChatReferencesReturn;
16
+ export {};
17
+ //# sourceMappingURL=use_chat_references.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use_chat_references.d.ts","sourceRoot":"","sources":["../../src/hooks/use_chat_references.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EACV,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,uBAAuB,EACxB,MAAM,mBAAmB,CAAC;AAM3B,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,kBAAkB,CAAC,EAAE,aAAa,EAAE,CAAC;IACrC,mBAAmB,CAAC,EAAE,CAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI,KAAK,IAAI,CAAC;CACrE;AAMD,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,kBAAuB,EACvB,mBAAmB,EACpB,EAAE,uBAAuB,GAAG,uBAAuB,CA6InD"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * useChatReferences Hook
3
+ *
4
+ * Manages chat references with:
5
+ * - Aggregating references from messages and props
6
+ * - Selection state management
7
+ * - Finding source message for a reference
8
+ */
9
+ 'use client';
10
+ import { useState, useCallback, useMemo, useEffect } from 'react';
11
+ // ============================================================================
12
+ // Hook Implementation
13
+ // ============================================================================
14
+ export function useChatReferences({ messages, initial_references = [], on_selection_change }) {
15
+ // -------------------------------------------------------------------------
16
+ // State
17
+ // -------------------------------------------------------------------------
18
+ const [selected_reference, set_selected_reference_state] = useState(null);
19
+ // -------------------------------------------------------------------------
20
+ // Aggregate all references
21
+ // -------------------------------------------------------------------------
22
+ const references = useMemo(() => {
23
+ const reference_map = new Map();
24
+ // Add initial references (from props)
25
+ initial_references.forEach((ref) => {
26
+ const chat_ref = {
27
+ ...ref,
28
+ scope: ref.scope || 'field'
29
+ };
30
+ reference_map.set(ref.id, chat_ref);
31
+ });
32
+ // Add references from messages
33
+ messages.forEach((message) => {
34
+ if (message.reference_list && Array.isArray(message.reference_list)) {
35
+ message.reference_list.forEach((ref) => {
36
+ // Update scope to 'chat' and add message_id
37
+ const existing = reference_map.get(ref.id);
38
+ if (existing) {
39
+ // If reference already exists, it's now in both chat and field
40
+ reference_map.set(ref.id, {
41
+ ...existing,
42
+ message_id: message.id
43
+ });
44
+ }
45
+ else {
46
+ reference_map.set(ref.id, {
47
+ ...ref,
48
+ scope: 'chat',
49
+ message_id: message.id
50
+ });
51
+ }
52
+ });
53
+ }
54
+ });
55
+ return Array.from(reference_map.values());
56
+ }, [messages, initial_references]);
57
+ // -------------------------------------------------------------------------
58
+ // Message lookup for references
59
+ // -------------------------------------------------------------------------
60
+ const reference_to_message_map = useMemo(() => {
61
+ const map = new Map();
62
+ messages.forEach((message) => {
63
+ if (message.reference_list && Array.isArray(message.reference_list)) {
64
+ message.reference_list.forEach((ref) => {
65
+ // Store the first message that contains this reference
66
+ if (!map.has(ref.id)) {
67
+ map.set(ref.id, message.id);
68
+ }
69
+ });
70
+ }
71
+ });
72
+ return map;
73
+ }, [messages]);
74
+ // -------------------------------------------------------------------------
75
+ // Select reference
76
+ // -------------------------------------------------------------------------
77
+ const select_reference = useCallback((reference) => {
78
+ // If same reference, toggle off
79
+ if (selected_reference?.id === reference.id) {
80
+ set_selected_reference_state(null);
81
+ on_selection_change?.(null);
82
+ return;
83
+ }
84
+ // Ensure message_id is set if available
85
+ const ref_with_message = {
86
+ ...reference,
87
+ message_id: reference.message_id || reference_to_message_map.get(reference.id)
88
+ };
89
+ set_selected_reference_state(ref_with_message);
90
+ on_selection_change?.(ref_with_message);
91
+ }, [selected_reference, reference_to_message_map, on_selection_change]);
92
+ // -------------------------------------------------------------------------
93
+ // Clear selection
94
+ // -------------------------------------------------------------------------
95
+ const clear_selection = useCallback(() => {
96
+ set_selected_reference_state(null);
97
+ on_selection_change?.(null);
98
+ }, [on_selection_change]);
99
+ // -------------------------------------------------------------------------
100
+ // Add new reference
101
+ // -------------------------------------------------------------------------
102
+ const add_reference = useCallback((reference) => {
103
+ // This is typically handled by the context or parent component
104
+ // Here we just select the newly added reference
105
+ select_reference(reference);
106
+ }, [select_reference]);
107
+ // -------------------------------------------------------------------------
108
+ // Get message ID for reference
109
+ // -------------------------------------------------------------------------
110
+ const get_message_for_reference = useCallback((reference_id) => {
111
+ return reference_to_message_map.get(reference_id) || null;
112
+ }, [reference_to_message_map]);
113
+ // -------------------------------------------------------------------------
114
+ // Clear selection if selected reference is removed
115
+ // -------------------------------------------------------------------------
116
+ useEffect(() => {
117
+ if (selected_reference && !references.some((r) => r.id === selected_reference.id)) {
118
+ set_selected_reference_state(null);
119
+ }
120
+ }, [references, selected_reference]);
121
+ // -------------------------------------------------------------------------
122
+ // Return
123
+ // -------------------------------------------------------------------------
124
+ return {
125
+ references,
126
+ selected_reference,
127
+ select_reference,
128
+ clear_selection,
129
+ add_reference,
130
+ get_message_for_reference
131
+ };
132
+ }
133
+ //# sourceMappingURL=use_chat_references.js.map