hazo_chat 2.0.10 → 2.0.11

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 (37) hide show
  1. package/README.md +336 -1
  2. package/SETUP_CHECKLIST.md +95 -0
  3. package/dist/api/index.d.ts +2 -0
  4. package/dist/api/index.d.ts.map +1 -1
  5. package/dist/api/index.js +1 -0
  6. package/dist/api/index.js.map +1 -1
  7. package/dist/api/unread_count.d.ts +57 -0
  8. package/dist/api/unread_count.d.ts.map +1 -0
  9. package/dist/api/unread_count.js +86 -0
  10. package/dist/api/unread_count.js.map +1 -0
  11. package/dist/components/hazo_chat/hazo_chat.d.ts.map +1 -1
  12. package/dist/components/hazo_chat/hazo_chat.js +11 -10
  13. package/dist/components/hazo_chat/hazo_chat.js.map +1 -1
  14. package/dist/components/hazo_chat/hazo_chat_header.d.ts +1 -1
  15. package/dist/components/hazo_chat/hazo_chat_header.d.ts.map +1 -1
  16. package/dist/components/hazo_chat/hazo_chat_header.js +2 -2
  17. package/dist/components/hazo_chat/hazo_chat_header.js.map +1 -1
  18. package/dist/components/hazo_chat/hazo_chat_messages.d.ts +1 -1
  19. package/dist/components/hazo_chat/hazo_chat_messages.d.ts.map +1 -1
  20. package/dist/components/hazo_chat/hazo_chat_messages.js +2 -2
  21. package/dist/components/hazo_chat/hazo_chat_messages.js.map +1 -1
  22. package/dist/components/ui/button.d.ts +1 -1
  23. package/dist/components/ui/chat_bubble.d.ts +2 -2
  24. package/dist/components/ui/chat_bubble.d.ts.map +1 -1
  25. package/dist/components/ui/chat_bubble.js +29 -10
  26. package/dist/components/ui/chat_bubble.js.map +1 -1
  27. package/dist/components/ui/hover-card.d.ts +13 -0
  28. package/dist/components/ui/hover-card.d.ts.map +1 -0
  29. package/dist/components/ui/hover-card.js +17 -0
  30. package/dist/components/ui/hover-card.js.map +1 -0
  31. package/dist/components/ui/index.d.ts +1 -0
  32. package/dist/components/ui/index.d.ts.map +1 -1
  33. package/dist/components/ui/index.js +1 -0
  34. package/dist/components/ui/index.js.map +1 -1
  35. package/dist/types/index.d.ts +16 -0
  36. package/dist/types/index.d.ts.map +1 -1
  37. package/package.json +2 -1
package/README.md CHANGED
@@ -344,6 +344,7 @@ hazo_chat requires these API endpoints:
344
344
  |----------|--------|-------------|
345
345
  | `/api/hazo_chat/messages` | GET | Fetch chat messages |
346
346
  | `/api/hazo_chat/messages` | POST | Send a new message |
347
+ | `/api/hazo_chat/unread_count` | GET | Get unread message counts by reference_id (optional) |
347
348
  | `/api/hazo_auth/me` | GET | Get current authenticated user |
348
349
  | `/api/hazo_auth/profiles` | POST | Fetch user profiles by IDs |
349
350
 
@@ -372,6 +373,157 @@ export { GET, POST };
372
373
 
373
374
  If you need more control, implement the endpoints manually. See [SETUP_CHECKLIST.md](./SETUP_CHECKLIST.md) for detailed examples.
374
375
 
376
+ ## Library Functions
377
+
378
+ hazo_chat provides server-side library functions that can be used in API routes, server components, or server actions.
379
+
380
+ ### hazo_chat_get_unread_count
381
+
382
+ Get unread message counts grouped by reference_id for a receiver user.
383
+
384
+ **Purpose:** Returns an array of reference IDs with the count of unread messages (where `read_at` is `null`) for a given receiver user ID. Useful for displaying unread message badges or notifications.
385
+
386
+ **Usage:**
387
+
388
+ ```typescript
389
+ import { createUnreadCountFunction } from 'hazo_chat/api';
390
+ import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
391
+
392
+ // Create the function using the factory
393
+ const hazo_chat_get_unread_count = createUnreadCountFunction({
394
+ getHazoConnect: () => getHazoConnectSingleton()
395
+ });
396
+
397
+ // Use the function
398
+ const unreadCounts = await hazo_chat_get_unread_count('receiver-user-id-123');
399
+ // Returns: [
400
+ // { reference_id: 'ref-1', count: 5 },
401
+ // { reference_id: 'ref-2', count: 3 },
402
+ // { reference_id: '', count: 1 } // Empty reference_id for general messages
403
+ // ]
404
+ ```
405
+
406
+ **Return Type:**
407
+
408
+ ```typescript
409
+ interface UnreadCountResult {
410
+ reference_id: string; // The reference ID (empty string for messages without reference)
411
+ count: number; // Number of unread messages for this reference
412
+ }
413
+ ```
414
+
415
+ **Function Behavior:**
416
+ - Only counts messages where `read_at` is `null` and `deleted_at` is `null`
417
+ - Groups results by `reference_id`
418
+ - Sorts results by count (descending - most unread first)
419
+ - Returns empty array if no unread messages found
420
+ - Returns empty array on errors (doesn't throw)
421
+
422
+ **Example: API Route Implementation**
423
+
424
+ ```typescript
425
+ // app/api/hazo_chat/unread_count/route.ts
426
+ import { createUnreadCountFunction } from 'hazo_chat/api';
427
+ import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
428
+ import { NextRequest, NextResponse } from 'next/server';
429
+
430
+ export const dynamic = 'force-dynamic';
431
+
432
+ const hazo_chat_get_unread_count = createUnreadCountFunction({
433
+ getHazoConnect: () => getHazoConnectSingleton()
434
+ });
435
+
436
+ export async function GET(request: NextRequest) {
437
+ try {
438
+ const { searchParams } = new URL(request.url);
439
+ const receiver_user_id = searchParams.get('receiver_user_id');
440
+
441
+ if (!receiver_user_id) {
442
+ return NextResponse.json(
443
+ {
444
+ success: false,
445
+ error: 'receiver_user_id is required',
446
+ unread_counts: []
447
+ },
448
+ { status: 400 }
449
+ );
450
+ }
451
+
452
+ const unread_counts = await hazo_chat_get_unread_count(receiver_user_id);
453
+
454
+ return NextResponse.json({
455
+ success: true,
456
+ receiver_user_id,
457
+ unread_counts,
458
+ total_references: unread_counts.length,
459
+ total_unread: unread_counts.reduce((sum, item) => sum + item.count, 0)
460
+ });
461
+ } catch (error) {
462
+ const error_message = error instanceof Error ? error.message : 'Unknown error';
463
+ return NextResponse.json(
464
+ {
465
+ success: false,
466
+ error: error_message,
467
+ unread_counts: []
468
+ },
469
+ { status: 500 }
470
+ );
471
+ }
472
+ }
473
+ ```
474
+
475
+ **Example: Server Component Usage**
476
+
477
+ ```typescript
478
+ // app/chat/unread-badge.tsx
479
+ import { createUnreadCountFunction } from 'hazo_chat/api';
480
+ import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
481
+
482
+ const hazo_chat_get_unread_count = createUnreadCountFunction({
483
+ getHazoConnect: () => getHazoConnectSingleton()
484
+ });
485
+
486
+ export default async function UnreadBadge({
487
+ receiver_user_id
488
+ }: {
489
+ receiver_user_id: string
490
+ }) {
491
+ const unread_counts = await hazo_chat_get_unread_count(receiver_user_id);
492
+ const total_unread = unread_counts.reduce((sum, item) => sum + item.count, 0);
493
+
494
+ if (total_unread === 0) return null;
495
+
496
+ return (
497
+ <div className="flex gap-2">
498
+ {unread_counts.map((item) => (
499
+ <div key={item.reference_id || 'general'}>
500
+ <span>{item.reference_id || 'General'}: {item.count}</span>
501
+ </div>
502
+ ))}
503
+ <span>Total: {total_unread}</span>
504
+ </div>
505
+ );
506
+ }
507
+ ```
508
+
509
+ **Example: Server Action Usage**
510
+
511
+ ```typescript
512
+ // app/actions/chat.ts
513
+ 'use server';
514
+
515
+ import { createUnreadCountFunction } from 'hazo_chat/api';
516
+ import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
517
+
518
+ const hazo_chat_get_unread_count = createUnreadCountFunction({
519
+ getHazoConnect: () => getHazoConnectSingleton()
520
+ });
521
+
522
+ export async function getUnreadCounts(receiver_user_id: string) {
523
+ return await hazo_chat_get_unread_count(receiver_user_id);
524
+ }
525
+ ```
526
+
375
527
  ## Props Reference
376
528
 
377
529
  ### HazoChatProps
@@ -390,6 +542,9 @@ If you need more control, implement the endpoints manually. See [SETUP_CHECKLIST
390
542
  | `title` | `string` | ❌ | - | Chat header title |
391
543
  | `subtitle` | `string` | ❌ | - | Chat header subtitle |
392
544
  | `on_close` | `() => void` | ❌ | - | Close button callback |
545
+ | `show_sidebar_toggle` | `boolean` | ❌ | `false` | Show sidebar toggle button (hamburger menu) |
546
+ | `show_delete_button` | `boolean` | ❌ | `true` | Show delete button on chat bubbles |
547
+ | `bubble_radius` | `'default' \| 'full'` | ❌ | `'default'` | Bubble border radius style: `'default'` (rounded with tail) or `'full'` (fully round) |
393
548
  | `className` | `string` | ❌ | - | Additional CSS classes |
394
549
 
395
550
  ### Example with All Props
@@ -413,6 +568,186 @@ If you need more control, implement the endpoints manually. See [SETUP_CHECKLIST
413
568
  />
414
569
  ```
415
570
 
571
+ ## Customization
572
+
573
+ hazo_chat provides props to customize component appearance and behavior without needing CSS overrides. This eliminates the need for extensive CSS customizations in consuming projects.
574
+
575
+ ### Common Customizations
576
+
577
+ #### Hide Sidebar Toggle Button (Hamburger Menu)
578
+
579
+ By default, the sidebar toggle button is hidden (`show_sidebar_toggle={false}`). To show it:
580
+
581
+ ```tsx
582
+ <HazoChat
583
+ receiver_user_id="user-123"
584
+ show_sidebar_toggle={true} // Show hamburger menu button
585
+ />
586
+ ```
587
+
588
+ #### Hide Delete Button on Chat Bubbles
589
+
590
+ To hide the delete button on chat bubbles:
591
+
592
+ ```tsx
593
+ <HazoChat
594
+ receiver_user_id="user-123"
595
+ show_delete_button={false} // Hide delete button
596
+ />
597
+ ```
598
+
599
+ #### Make Chat Bubbles Fully Round
600
+
601
+ To make all chat bubbles fully round (instead of the default style with a tail):
602
+
603
+ ```tsx
604
+ <HazoChat
605
+ receiver_user_id="user-123"
606
+ bubble_radius="full" // Fully round all corners
607
+ />
608
+ ```
609
+
610
+ ### Customization Props Summary
611
+
612
+ | Prop | Default | Options | Description |
613
+ |------|--------|---------|------------|
614
+ | `show_sidebar_toggle` | `false` | `boolean` | Show/hide the hamburger menu button |
615
+ | `show_delete_button` | `true` | `boolean` | Show/hide delete button on chat bubbles |
616
+ | `bubble_radius` | `'default'` | `'default' \| 'full'` | Bubble border radius style |
617
+
618
+ ### Example: Full Customization
619
+
620
+ ```tsx
621
+ <HazoChat
622
+ receiver_user_id="user-123"
623
+ reference_id="project-456"
624
+ show_sidebar_toggle={false} // Hide hamburger menu
625
+ show_delete_button={false} // Hide delete buttons
626
+ bubble_radius="full" // Fully round bubbles
627
+ title="Project Chat"
628
+ className="h-[600px]"
629
+ />
630
+ ```
631
+
632
+ ### Why Use Props Instead of CSS?
633
+
634
+ Using props instead of CSS overrides provides:
635
+ - **Type safety**: TypeScript will catch invalid values
636
+ - **Consistency**: Ensures all instances use the same styling
637
+ - **Maintainability**: Easier to update across the codebase
638
+ - **No CSS conflicts**: Avoids specificity issues with Tailwind utilities
639
+
640
+ If you need more advanced styling that isn't covered by props, you can still use CSS overrides, but props should cover most common customization needs.
641
+
642
+ ## Checking for Messages
643
+
644
+ If you need to check whether messages exist for a given reference (without loading all messages), you can call the messages API endpoint directly. This is useful for showing indicators or badges.
645
+
646
+ **Important:** The API requires `receiver_user_id` as a query parameter and returns an object response, not a direct array.
647
+
648
+ ### Correct API Usage Pattern
649
+
650
+ ```typescript
651
+ // ✅ CORRECT: Include receiver_user_id and handle object response
652
+ async function checkMessagesExist(
653
+ recipient_user_id: string,
654
+ reference_id: string,
655
+ reference_type?: string
656
+ ): Promise<boolean> {
657
+ const params = new URLSearchParams({
658
+ receiver_user_id: recipient_user_id, // ✅ Required parameter
659
+ reference_id,
660
+ ...(reference_type && { reference_type }),
661
+ });
662
+
663
+ const response = await fetch(`/api/hazo_chat/messages?${params.toString()}`, {
664
+ credentials: 'include'
665
+ });
666
+
667
+ if (!response.ok) {
668
+ return false;
669
+ }
670
+
671
+ const data = await response.json();
672
+
673
+ // ✅ Handle object response format: { success: true, messages: [], current_user_id }
674
+ const messages = data.messages || data; // Handle both object and array responses
675
+ const has_messages_result = Array.isArray(messages) && messages.length > 0;
676
+
677
+ return has_messages_result;
678
+ }
679
+ ```
680
+
681
+ ### Common Mistakes to Avoid
682
+
683
+ ```typescript
684
+ // ❌ WRONG: Missing receiver_user_id parameter
685
+ const params = new URLSearchParams({
686
+ reference_id,
687
+ ...(reference_type && { reference_type }),
688
+ });
689
+ // This will return a 400 error: "receiver_user_id is required"
690
+
691
+ // ❌ WRONG: Expecting direct array response
692
+ const data = await response.json();
693
+ const has_messages_result = Array.isArray(data) && data.length > 0;
694
+ // This fails because API returns: { success: true, messages: [], current_user_id }
695
+ ```
696
+
697
+ ### Example: Custom Hook Implementation
698
+
699
+ ```typescript
700
+ 'use client';
701
+
702
+ import { useState, useEffect } from 'react';
703
+
704
+ function useChatMessagesCheck(
705
+ recipient_user_id: string,
706
+ reference_id: string,
707
+ reference_type?: string
708
+ ) {
709
+ const [has_messages, set_has_messages] = useState(false);
710
+ const [is_checking, set_is_checking] = useState(true);
711
+
712
+ useEffect(() => {
713
+ async function check() {
714
+ if (!recipient_user_id || !reference_id) {
715
+ set_is_checking(false);
716
+ return;
717
+ }
718
+
719
+ try {
720
+ const params = new URLSearchParams({
721
+ receiver_user_id: recipient_user_id, // ✅ Required
722
+ reference_id,
723
+ ...(reference_type && { reference_type }),
724
+ });
725
+
726
+ const response = await fetch(
727
+ `/api/hazo_chat/messages?${params.toString()}`,
728
+ { credentials: 'include' }
729
+ );
730
+
731
+ if (response.ok) {
732
+ const data = await response.json();
733
+ const messages = data.messages || data; // ✅ Handle object response
734
+ set_has_messages(Array.isArray(messages) && messages.length > 0);
735
+ }
736
+ } catch (error) {
737
+ console.error('[useChatMessagesCheck] Error:', error);
738
+ set_has_messages(false);
739
+ } finally {
740
+ set_is_checking(false);
741
+ }
742
+ }
743
+
744
+ check();
745
+ }, [recipient_user_id, reference_id, reference_type]);
746
+
747
+ return { has_messages, is_checking };
748
+ }
749
+ ```
750
+
416
751
  ## Hooks
417
752
 
418
753
  ### useChatMessages
@@ -707,7 +1042,7 @@ npm run build:test-app
707
1042
  import { HazoChat, useChatMessages, useChatReferences, useFileUpload } from 'hazo_chat';
708
1043
 
709
1044
  // API handlers (for server-side routes)
710
- import { createMessagesHandler } from 'hazo_chat/api';
1045
+ import { createMessagesHandler, createUnreadCountFunction } from 'hazo_chat/api';
711
1046
 
712
1047
  // Components only
713
1048
  import { HazoChat, ChatBubble } from 'hazo_chat/components';
@@ -233,11 +233,101 @@ export async function POST(request: NextRequest) {
233
233
  }
234
234
  ```
235
235
 
236
+ ### Step 4.4: Unread Count API (Optional - For Unread Badges)
237
+
238
+ **File: `src/app/api/hazo_chat/unread_count/route.ts`**
239
+
240
+ This endpoint is optional but useful for displaying unread message counts or badges in your UI.
241
+
242
+ ```typescript
243
+ /**
244
+ * API route to get unread message counts grouped by reference_id
245
+ * Uses the exportable library function from hazo_chat
246
+ */
247
+
248
+ export const dynamic = 'force-dynamic';
249
+
250
+ import { createUnreadCountFunction } from 'hazo_chat/api';
251
+ import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
252
+ import { NextRequest, NextResponse } from 'next/server';
253
+
254
+ // Create the unread count function using the factory
255
+ const hazo_chat_get_unread_count = createUnreadCountFunction({
256
+ getHazoConnect: () => getHazoConnectSingleton()
257
+ });
258
+
259
+ export async function GET(request: NextRequest) {
260
+ try {
261
+ const { searchParams } = new URL(request.url);
262
+ const receiver_user_id = searchParams.get('receiver_user_id');
263
+
264
+ if (!receiver_user_id) {
265
+ return NextResponse.json(
266
+ {
267
+ success: false,
268
+ error: 'receiver_user_id is required',
269
+ unread_counts: []
270
+ },
271
+ { status: 400 }
272
+ );
273
+ }
274
+
275
+ // Call the library function
276
+ const unread_counts = await hazo_chat_get_unread_count(receiver_user_id);
277
+
278
+ return NextResponse.json({
279
+ success: true,
280
+ receiver_user_id,
281
+ unread_counts,
282
+ total_references: unread_counts.length,
283
+ total_unread: unread_counts.reduce((sum, item) => sum + item.count, 0)
284
+ });
285
+ } catch (error) {
286
+ const error_message = error instanceof Error ? error.message : 'Unknown error';
287
+ console.error('[hazo_chat/unread_count] Error:', error_message, error);
288
+
289
+ return NextResponse.json(
290
+ {
291
+ success: false,
292
+ error: error_message,
293
+ unread_counts: []
294
+ },
295
+ { status: 500 }
296
+ );
297
+ }
298
+ }
299
+ ```
300
+
301
+ **What this endpoint does:**
302
+ - Takes `receiver_user_id` as a query parameter
303
+ - Returns an array of objects with `reference_id` and `count` of unread messages
304
+ - Only counts messages where `read_at` is `null` and `deleted_at` is `null`
305
+ - Groups results by `reference_id`
306
+ - Sorts by count (descending - most unread first)
307
+
308
+ **Response format:**
309
+ ```json
310
+ {
311
+ "success": true,
312
+ "receiver_user_id": "user-123",
313
+ "unread_counts": [
314
+ { "reference_id": "ref-1", "count": 5 },
315
+ { "reference_id": "ref-2", "count": 3 },
316
+ { "reference_id": "", "count": 1 }
317
+ ],
318
+ "total_references": 3,
319
+ "total_unread": 9
320
+ }
321
+ ```
322
+
323
+ **Note:** This endpoint is optional. Only create it if you need to display unread message counts in your UI.
324
+
236
325
  ### API Routes Summary
237
326
 
238
327
  | Endpoint | Method | File | Purpose |
239
328
  |----------|--------|------|---------|
240
329
  | `/api/hazo_chat/messages` | GET, POST | `api/hazo_chat/messages/route.ts` | Message CRUD |
330
+ | `/api/hazo_chat/unread_count` | GET | `api/hazo_chat/unread_count/route.ts` | Get unread counts (optional) |
241
331
  | `/api/hazo_auth/me` | GET | `api/hazo_auth/me/route.ts` | Get current user |
242
332
  | `/api/hazo_auth/profiles` | POST | `api/hazo_auth/profiles/route.ts` | Get user profiles |
243
333
 
@@ -246,6 +336,7 @@ export async function POST(request: NextRequest) {
246
336
  - [ ] `GET /api/hazo_auth/me` returns user data when logged in
247
337
  - [ ] `POST /api/hazo_auth/profiles` returns profiles for given IDs
248
338
  - [ ] `GET /api/hazo_chat/messages?receiver_user_id=xxx` works
339
+ - [ ] `GET /api/hazo_chat/unread_count?receiver_user_id=xxx` returns unread counts (if implemented)
249
340
 
250
341
  ---
251
342
 
@@ -735,6 +826,9 @@ curl -X POST http://localhost:3000/api/hazo_auth/profiles \
735
826
 
736
827
  # Test messages endpoint
737
828
  curl "http://localhost:3000/api/hazo_chat/messages?receiver_user_id=user-id"
829
+
830
+ # Test unread count endpoint (if implemented)
831
+ curl "http://localhost:3000/api/hazo_chat/unread_count?receiver_user_id=user-id"
738
832
  ```
739
833
 
740
834
  ### UI Verification & Responsive Behavior
@@ -912,6 +1006,7 @@ npm install hazo_chat hazo_connect
912
1006
 
913
1007
  # 2. Create API routes
914
1008
  mkdir -p src/app/api/hazo_chat/messages
1009
+ mkdir -p src/app/api/hazo_chat/unread_count # Optional: for unread counts
915
1010
  mkdir -p src/app/api/hazo_auth/me
916
1011
  mkdir -p src/app/api/hazo_auth/profiles
917
1012
 
@@ -20,5 +20,7 @@
20
20
  * ```
21
21
  */
22
22
  export { createMessagesHandler } from './messages.js';
23
+ export { createUnreadCountFunction } from './unread_count.js';
23
24
  export type { MessagesHandlerOptions, ChatMessageInput, ChatMessageRecord } from './types.js';
25
+ export type { UnreadCountFunctionOptions, UnreadCountResult } from './unread_count.js';
24
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGtD,YAAY,EACV,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAG9D,YAAY,EACV,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,0BAA0B,EAC1B,iBAAiB,EAClB,MAAM,mBAAmB,CAAC"}
package/dist/api/index.js CHANGED
@@ -21,4 +21,5 @@
21
21
  */
22
22
  // Export handler factories
23
23
  export { createMessagesHandler } from './messages.js';
24
+ export { createUnreadCountFunction } from './unread_count.js';
24
25
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,2BAA2B;AAC3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,2BAA2B;AAC3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Unread Count Library Function Factory
3
+ *
4
+ * Creates a function to get unread message counts grouped by reference_id
5
+ * for a given receiver user ID.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // In your server-side code
10
+ * import { createUnreadCountFunction } from 'hazo_chat/api';
11
+ * import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
12
+ *
13
+ * const getUnreadCount = createUnreadCountFunction({
14
+ * getHazoConnect: () => getHazoConnectSingleton()
15
+ * });
16
+ *
17
+ * // Use the function
18
+ * const unreadCounts = await getUnreadCount('receiver-user-id-123');
19
+ * // Returns: [{ reference_id: 'ref-1', count: 5 }, { reference_id: 'ref-2', count: 3 }]
20
+ * ```
21
+ */
22
+ /**
23
+ * Options for creating the unread count function
24
+ */
25
+ export interface UnreadCountFunctionOptions {
26
+ /**
27
+ * Function to get the hazo_connect adapter instance.
28
+ * Called each time the function is invoked to get a fresh connection.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
33
+ *
34
+ * const options = {
35
+ * getHazoConnect: () => getHazoConnectSingleton()
36
+ * };
37
+ * ```
38
+ */
39
+ getHazoConnect: () => unknown;
40
+ }
41
+ /**
42
+ * Result type for unread count function
43
+ */
44
+ export interface UnreadCountResult {
45
+ /** The reference ID */
46
+ reference_id: string;
47
+ /** Number of unread messages for this reference */
48
+ count: number;
49
+ }
50
+ /**
51
+ * Creates a function to get unread message counts by reference_id
52
+ *
53
+ * @param options - Configuration options
54
+ * @returns Function that takes receiver_user_id and returns unread counts
55
+ */
56
+ export declare function createUnreadCountFunction(options: UnreadCountFunctionOptions): (receiver_user_id: string) => Promise<UnreadCountResult[]>;
57
+ //# sourceMappingURL=unread_count.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unread_count.d.ts","sourceRoot":"","sources":["../../src/api/unread_count.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;;;;;;;;;;OAYG;IACH,cAAc,EAAE,MAAM,OAAO,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,uBAAuB;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,0BAA0B,IAUzE,kBAAkB,MAAM,KACvB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA2DhC"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Unread Count Library Function Factory
3
+ *
4
+ * Creates a function to get unread message counts grouped by reference_id
5
+ * for a given receiver user ID.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // In your server-side code
10
+ * import { createUnreadCountFunction } from 'hazo_chat/api';
11
+ * import { getHazoConnectSingleton } from 'hazo_connect/nextjs/setup';
12
+ *
13
+ * const getUnreadCount = createUnreadCountFunction({
14
+ * getHazoConnect: () => getHazoConnectSingleton()
15
+ * });
16
+ *
17
+ * // Use the function
18
+ * const unreadCounts = await getUnreadCount('receiver-user-id-123');
19
+ * // Returns: [{ reference_id: 'ref-1', count: 5 }, { reference_id: 'ref-2', count: 3 }]
20
+ * ```
21
+ */
22
+ import { createCrudService } from 'hazo_connect/server';
23
+ /**
24
+ * Creates a function to get unread message counts by reference_id
25
+ *
26
+ * @param options - Configuration options
27
+ * @returns Function that takes receiver_user_id and returns unread counts
28
+ */
29
+ export function createUnreadCountFunction(options) {
30
+ const { getHazoConnect } = options;
31
+ /**
32
+ * Get unread message counts grouped by reference_id for a receiver user
33
+ *
34
+ * @param receiver_user_id - The user ID to get unread counts for
35
+ * @returns Array of objects with reference_id and count of unread messages
36
+ */
37
+ return async function hazo_chat_get_unread_count(receiver_user_id) {
38
+ try {
39
+ if (!receiver_user_id || receiver_user_id.trim() === '') {
40
+ console.error('[hazo_chat_get_unread_count] Missing receiver_user_id');
41
+ return [];
42
+ }
43
+ console.log('[hazo_chat_get_unread_count] Fetching unread counts for:', {
44
+ receiver_user_id,
45
+ });
46
+ // Get hazo_connect instance and create CRUD service
47
+ const hazoConnect = getHazoConnect();
48
+ const chatService = createCrudService(hazoConnect, 'hazo_chat');
49
+ // Fetch all unread messages for this receiver
50
+ const unread_messages = await chatService.list((qb) => {
51
+ return qb
52
+ .select('*')
53
+ .where('receiver_user_id', 'eq', receiver_user_id)
54
+ .where('read_at', 'is', null)
55
+ .where('deleted_at', 'is', null);
56
+ });
57
+ // Group by reference_id and count
58
+ const count_map = new Map();
59
+ for (const message of unread_messages) {
60
+ const ref_id = message.reference_id || '';
61
+ const current_count = count_map.get(ref_id) || 0;
62
+ count_map.set(ref_id, current_count + 1);
63
+ }
64
+ // Convert map to array of results
65
+ const results = Array.from(count_map.entries()).map(([reference_id, count]) => ({
66
+ reference_id,
67
+ count,
68
+ }));
69
+ // Sort by count descending (most unread first)
70
+ results.sort((a, b) => b.count - a.count);
71
+ console.log('[hazo_chat_get_unread_count] Found unread counts:', {
72
+ receiver_user_id,
73
+ total_references: results.length,
74
+ total_unread: unread_messages.length,
75
+ });
76
+ return results;
77
+ }
78
+ catch (error) {
79
+ const error_message = error instanceof Error ? error.message : 'Unknown error';
80
+ console.error('[hazo_chat_get_unread_count] Error:', error_message, error);
81
+ // Return empty array on error rather than throwing
82
+ return [];
83
+ }
84
+ };
85
+ }
86
+ //# sourceMappingURL=unread_count.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unread_count.js","sourceRoot":"","sources":["../../src/api/unread_count.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAkCxD;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAmC;IAC3E,MAAM,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC;IAEnC;;;;;OAKG;IACH,OAAO,KAAK,UAAU,0BAA0B,CAC9C,gBAAwB;QAExB,IAAI,CAAC;YACH,IAAI,CAAC,gBAAgB,IAAI,gBAAgB,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;gBACxD,OAAO,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;gBACvE,OAAO,EAAE,CAAC;YACZ,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,0DAA0D,EAAE;gBACtE,gBAAgB;aACjB,CAAC,CAAC;YAEH,oDAAoD;YACpD,MAAM,WAAW,GAAG,cAAc,EAAwB,CAAC;YAC3D,MAAM,WAAW,GAAG,iBAAiB,CAAoB,WAAW,EAAE,WAAW,CAAC,CAAC;YAEnF,8CAA8C;YAC9C,MAAM,eAAe,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE;gBACpD,OAAO,EAAE;qBACN,MAAM,CAAC,GAAG,CAAC;qBACX,KAAK,CAAC,kBAAkB,EAAE,IAAI,EAAE,gBAAgB,CAAC;qBACjD,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC;qBAC5B,KAAK,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;YACrC,CAAC,CAAC,CAAC;YAEH,kCAAkC;YAClC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;YAE5C,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,CAAC;gBACtC,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;gBAC1C,MAAM,aAAa,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACjD,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,GAAG,CAAC,CAAC,CAAC;YAC3C,CAAC;YAED,kCAAkC;YAClC,MAAM,OAAO,GAAwB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CACtE,CAAC,CAAC,YAAY,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC1B,YAAY;gBACZ,KAAK;aACN,CAAC,CACH,CAAC;YAEF,+CAA+C;YAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;YAE1C,OAAO,CAAC,GAAG,CAAC,mDAAmD,EAAE;gBAC/D,gBAAgB;gBAChB,gBAAgB,EAAE,OAAO,CAAC,MAAM;gBAChC,YAAY,EAAE,eAAe,CAAC,MAAM;aACrC,CAAC,CAAC;YAEH,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,aAAa,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;YAC/E,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC;YAE3E,mDAAmD;YACnD,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"hazo_chat.d.ts","sourceRoot":"","sources":["../../../src/components/hazo_chat/hazo_chat.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,OAAO,KAAK,EACV,aAAa,EAId,MAAM,sBAAsB,CAAC;AA6Y9B;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,2CAmD5C;yBAnDe,QAAQ"}
1
+ {"version":3,"file":"hazo_chat.d.ts","sourceRoot":"","sources":["../../../src/components/hazo_chat/hazo_chat.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,OAAO,KAAK,EACV,aAAa,EAId,MAAM,sBAAsB,CAAC;AAyZ9B;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,2CAyD5C;yBAzDe,QAAQ"}