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.
- package/README.md +60 -8
- package/SETUP_CHECKLIST.md +37 -0
- package/dist/api/index.d.ts +26 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +25 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/messages.d.ts +52 -0
- package/dist/api/messages.d.ts.map +1 -0
- package/dist/api/messages.js +330 -0
- package/dist/api/messages.js.map +1 -0
- package/dist/api/types.d.ts +69 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +8 -0
- package/dist/api/types.js.map +1 -0
- package/dist/api/unread_count.d.ts +57 -0
- package/dist/api/unread_count.d.ts.map +1 -0
- package/dist/api/unread_count.js +86 -0
- package/dist/api/unread_count.js.map +1 -0
- package/dist/components/hazo_chat/hazo_chat.d.ts +28 -0
- package/dist/components/hazo_chat/hazo_chat.d.ts.map +1 -0
- package/dist/components/hazo_chat/hazo_chat.js +220 -0
- package/dist/components/hazo_chat/hazo_chat.js.map +1 -0
- package/dist/components/hazo_chat/hazo_chat_attachment_preview.d.ts +17 -0
- package/dist/components/hazo_chat/hazo_chat_attachment_preview.d.ts.map +1 -0
- package/dist/components/hazo_chat/hazo_chat_attachment_preview.js +60 -0
- package/dist/components/hazo_chat/hazo_chat_attachment_preview.js.map +1 -0
- package/dist/components/hazo_chat/hazo_chat_context.d.ts +42 -0
- package/dist/components/hazo_chat/hazo_chat_context.d.ts.map +1 -0
- package/dist/components/hazo_chat/hazo_chat_context.js +273 -0
- package/dist/components/hazo_chat/hazo_chat_context.js.map +1 -0
- package/dist/components/hazo_chat/hazo_chat_document_viewer.d.ts +15 -0
- package/dist/components/hazo_chat/hazo_chat_document_viewer.d.ts.map +1 -0
- package/dist/components/hazo_chat/hazo_chat_document_viewer.js +140 -0
- package/dist/components/hazo_chat/hazo_chat_document_viewer.js.map +1 -0
- package/dist/components/hazo_chat/hazo_chat_header.d.ts +16 -0
- package/dist/components/hazo_chat/hazo_chat_header.d.ts.map +1 -0
- package/dist/components/hazo_chat/hazo_chat_header.js +32 -0
- package/dist/components/hazo_chat/hazo_chat_header.js.map +1 -0
- package/dist/components/hazo_chat/hazo_chat_input.d.ts +16 -0
- package/dist/components/hazo_chat/hazo_chat_input.d.ts.map +1 -0
- package/dist/components/hazo_chat/hazo_chat_input.js +75 -0
- package/dist/components/hazo_chat/hazo_chat_input.js.map +1 -0
- package/dist/components/hazo_chat/hazo_chat_messages.d.ts +17 -0
- package/dist/components/hazo_chat/hazo_chat_messages.d.ts.map +1 -0
- package/dist/components/hazo_chat/hazo_chat_messages.js +215 -0
- package/dist/components/hazo_chat/hazo_chat_messages.js.map +1 -0
- package/dist/components/hazo_chat/hazo_chat_reference_list.d.ts +16 -0
- package/dist/components/hazo_chat/hazo_chat_reference_list.d.ts.map +1 -0
- package/dist/components/hazo_chat/hazo_chat_reference_list.js +59 -0
- package/dist/components/hazo_chat/hazo_chat_reference_list.js.map +1 -0
- package/dist/components/hazo_chat/hazo_chat_sidebar.d.ts +18 -0
- package/dist/components/hazo_chat/hazo_chat_sidebar.d.ts.map +1 -0
- package/dist/components/hazo_chat/hazo_chat_sidebar.js +72 -0
- package/dist/components/hazo_chat/hazo_chat_sidebar.js.map +1 -0
- package/dist/components/hazo_chat/index.d.ts +16 -0
- package/dist/components/hazo_chat/index.d.ts.map +1 -0
- package/dist/components/hazo_chat/index.js +19 -0
- package/dist/components/hazo_chat/index.js.map +1 -0
- package/dist/components/index.d.ts +9 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +11 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/ui/avatar.d.ts +13 -0
- package/dist/components/ui/avatar.d.ts.map +1 -0
- package/dist/components/ui/avatar.js +28 -0
- package/dist/components/ui/avatar.js.map +1 -0
- package/dist/components/ui/badge.d.ts +16 -0
- package/dist/components/ui/badge.d.ts.map +1 -0
- package/dist/components/ui/badge.js +36 -0
- package/dist/components/ui/badge.js.map +1 -0
- package/dist/components/ui/button.d.ts +18 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/button.js +47 -0
- package/dist/components/ui/button.js.map +1 -0
- package/dist/components/ui/chat_bubble.d.ts +18 -0
- package/dist/components/ui/chat_bubble.d.ts.map +1 -0
- package/dist/components/ui/chat_bubble.js +130 -0
- package/dist/components/ui/chat_bubble.js.map +1 -0
- package/dist/components/ui/hover-card.d.ts +13 -0
- package/dist/components/ui/hover-card.d.ts.map +1 -0
- package/dist/components/ui/hover-card.js +17 -0
- package/dist/components/ui/hover-card.js.map +1 -0
- package/dist/components/ui/index.d.ts +19 -0
- package/dist/components/ui/index.d.ts.map +1 -0
- package/dist/components/ui/index.js +21 -0
- package/dist/components/ui/index.js.map +1 -0
- package/dist/components/ui/input.d.ts +11 -0
- package/dist/components/ui/input.d.ts.map +1 -0
- package/dist/components/ui/input.js +18 -0
- package/dist/components/ui/input.js.map +1 -0
- package/dist/components/ui/loading_skeleton.d.ts +19 -0
- package/dist/components/ui/loading_skeleton.d.ts.map +1 -0
- package/dist/components/ui/loading_skeleton.js +30 -0
- package/dist/components/ui/loading_skeleton.js.map +1 -0
- package/dist/components/ui/scroll-area.d.ts +12 -0
- package/dist/components/ui/scroll-area.d.ts.map +1 -0
- package/dist/components/ui/scroll-area.js +25 -0
- package/dist/components/ui/scroll-area.js.map +1 -0
- package/dist/components/ui/separator.d.ts +11 -0
- package/dist/components/ui/separator.d.ts.map +1 -0
- package/dist/components/ui/separator.js +18 -0
- package/dist/components/ui/separator.js.map +1 -0
- package/dist/components/ui/skeleton.d.ts +9 -0
- package/dist/components/ui/skeleton.d.ts.map +1 -0
- package/dist/components/ui/skeleton.js +16 -0
- package/dist/components/ui/skeleton.js.map +1 -0
- package/dist/components/ui/textarea.d.ts +11 -0
- package/dist/components/ui/textarea.d.ts.map +1 -0
- package/dist/components/ui/textarea.js +18 -0
- package/dist/components/ui/textarea.js.map +1 -0
- package/dist/components/ui/tooltip.d.ts +14 -0
- package/dist/components/ui/tooltip.d.ts.map +1 -0
- package/dist/components/ui/tooltip.js +30 -0
- package/dist/components/ui/tooltip.js.map +1 -0
- package/dist/hooks/index.d.ts +10 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +10 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/use_chat_messages.d.ts +33 -0
- package/dist/hooks/use_chat_messages.d.ts.map +1 -0
- package/dist/hooks/use_chat_messages.js +485 -0
- package/dist/hooks/use_chat_messages.js.map +1 -0
- package/dist/hooks/use_chat_references.d.ts +17 -0
- package/dist/hooks/use_chat_references.d.ts.map +1 -0
- package/dist/hooks/use_chat_references.js +133 -0
- package/dist/hooks/use_chat_references.js.map +1 -0
- package/dist/hooks/use_file_upload.d.ts +23 -0
- package/dist/hooks/use_file_upload.d.ts.map +1 -0
- package/dist/hooks/use_file_upload.js +212 -0
- package/dist/hooks/use_file_upload.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +41 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +93 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/constants.d.ts +41 -0
- package/dist/lib/constants.d.ts.map +1 -0
- package/dist/lib/constants.js +72 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/index.d.ts +9 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +9 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/utils.d.ts +17 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +20 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/types/index.d.ts +405 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +11 -0
- package/dist/types/index.js.map +1 -0
- 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
|