@ermis-network/ermis-chat-react 1.0.7 → 1.0.8

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 (50) hide show
  1. package/dist/index.cjs +2780 -1852
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +364 -8
  4. package/dist/index.css.map +1 -1
  5. package/dist/index.d.mts +160 -1
  6. package/dist/index.d.ts +160 -1
  7. package/dist/index.mjs +2780 -1884
  8. package/dist/index.mjs.map +1 -1
  9. package/package.json +2 -2
  10. package/src/channelRoleUtils.ts +73 -0
  11. package/src/channelTypeUtils.ts +46 -0
  12. package/src/components/Avatar.tsx +57 -31
  13. package/src/components/ChannelActions.tsx +13 -11
  14. package/src/components/ChannelHeader.tsx +89 -4
  15. package/src/components/ChannelInfo/ChannelInfo.tsx +23 -17
  16. package/src/components/ChannelInfo/ChannelInfoTabs.tsx +57 -26
  17. package/src/components/ChannelInfo/ChannelSettingsPanel.tsx +4 -2
  18. package/src/components/ChannelInfo/EditChannelModal.tsx +2 -1
  19. package/src/components/ChannelInfo/MemberListItem.tsx +2 -1
  20. package/src/components/ChannelList.tsx +59 -14
  21. package/src/components/CreateChannelModal.tsx +53 -16
  22. package/src/components/EditPreview.tsx +2 -1
  23. package/src/components/ForwardMessageModal.tsx +2 -1
  24. package/src/components/MediaLightbox.tsx +314 -0
  25. package/src/components/MessageInput.tsx +3 -2
  26. package/src/components/MessageItem.tsx +2 -1
  27. package/src/components/MessageRenderers.tsx +168 -46
  28. package/src/components/PendingOverlay.tsx +11 -1
  29. package/src/components/PinnedMessages.tsx +2 -1
  30. package/src/components/ReplyPreview.tsx +2 -1
  31. package/src/components/SkippedOverlay.tsx +36 -0
  32. package/src/components/UserPicker.tsx +1 -1
  33. package/src/components/VirtualMessageList.tsx +91 -7
  34. package/src/hooks/useBlockedState.ts +3 -2
  35. package/src/hooks/useChannelCapabilities.ts +10 -12
  36. package/src/hooks/useChannelListUpdates.ts +6 -4
  37. package/src/hooks/useChannelMessages.ts +2 -3
  38. package/src/hooks/useChannelRowUpdates.ts +3 -2
  39. package/src/hooks/useMessageActions.ts +23 -9
  40. package/src/hooks/useOnlineStatus.ts +71 -0
  41. package/src/hooks/useOnlineUsers.ts +115 -0
  42. package/src/hooks/usePendingState.ts +8 -3
  43. package/src/index.ts +61 -9
  44. package/src/messageTypeUtils.ts +64 -0
  45. package/src/styles/_channel-list.css +59 -0
  46. package/src/styles/_media-lightbox.css +263 -0
  47. package/src/styles/_message-bubble.css +99 -8
  48. package/src/styles/_message-list.css +25 -0
  49. package/src/styles/index.css +1 -0
  50. package/src/types.ts +46 -0
@@ -182,6 +182,10 @@
182
182
  color: rgba(255, 255, 255, 0.9);
183
183
  }
184
184
 
185
+ .ermis-message-bubble--own .ermis-attachment__link-title {
186
+ color: rgba(255, 255, 255, 0.9);
187
+ }
188
+
185
189
  /* --- Message Bubble --- */
186
190
  .ermis-message-bubble {
187
191
  position: relative;
@@ -200,6 +204,24 @@
200
204
  border-bottom-right-radius: var(--ermis-radius-sm);
201
205
  }
202
206
 
207
+ /* --- Override muted text inside own bubble (unreadable on accent bg in dark mode) --- */
208
+ .ermis-message-bubble--own .ermis-attachment__file-size,
209
+ .ermis-message-bubble--own .ermis-attachment__link-url,
210
+ .ermis-message-bubble--own .ermis-attachment__link-description,
211
+ .ermis-message-bubble--own .ermis-attachment__voice-duration,
212
+ .ermis-message-bubble--own .ermis-message-list__forwarded-indicator,
213
+ .ermis-message-bubble--own .ermis-attachment--file {
214
+ color: rgba(255, 255, 255, 0.6);
215
+ }
216
+
217
+ .ermis-message-bubble--own .ermis-attachment--link-preview {
218
+ border-color: rgba(255, 255, 255, 0.2);
219
+ }
220
+
221
+ .ermis-message-bubble--own .ermis-attachment--file {
222
+ background-color: rgba(255, 255, 255, 0.1);
223
+ }
224
+
203
225
  .ermis-message-bubble--other {
204
226
  background-color: var(--ermis-bubble-other-bg);
205
227
  color: var(--ermis-bubble-other-text);
@@ -364,7 +386,8 @@
364
386
  }
365
387
 
366
388
  .ermis-attachment-grid .ermis-attachment--image,
367
- .ermis-attachment-grid .ermis-attachment--video {
389
+ .ermis-attachment-grid .ermis-attachment--video,
390
+ .ermis-attachment-grid .ermis-attachment--video-poster {
368
391
  width: 100%;
369
392
  height: 100%;
370
393
  max-width: none;
@@ -373,7 +396,8 @@
373
396
  display: block;
374
397
  }
375
398
 
376
- .ermis-attachment-grid .ermis-attachment--image {
399
+ .ermis-attachment-grid .ermis-attachment--image,
400
+ .ermis-attachment-grid .ermis-attachment--video-poster {
377
401
  object-fit: cover;
378
402
  }
379
403
 
@@ -384,7 +408,8 @@
384
408
 
385
409
  /* Single media: larger height allowed */
386
410
  .ermis-attachment-grid--single .ermis-attachment--image,
387
- .ermis-attachment-grid--single .ermis-attachment--video {
411
+ .ermis-attachment-grid--single .ermis-attachment--video,
412
+ .ermis-attachment-grid--single .ermis-attachment--video-poster {
388
413
  max-height: 300px;
389
414
  }
390
415
 
@@ -449,7 +474,8 @@
449
474
 
450
475
  /* Full media — hidden until loaded, fades in over blur/shimmer */
451
476
  .ermis-attachment-aspect-box .ermis-attachment--image,
452
- .ermis-attachment-aspect-box .ermis-attachment--video {
477
+ .ermis-attachment-aspect-box .ermis-attachment--video,
478
+ .ermis-attachment-aspect-box .ermis-attachment--video-poster {
453
479
  position: absolute;
454
480
  top: 0;
455
481
  left: 0;
@@ -464,7 +490,8 @@
464
490
  border-radius: 0;
465
491
  }
466
492
 
467
- .ermis-attachment-aspect-box .ermis-attachment--image {
493
+ .ermis-attachment-aspect-box .ermis-attachment--image,
494
+ .ermis-attachment-aspect-box .ermis-attachment--video-poster {
468
495
  object-fit: cover;
469
496
  }
470
497
 
@@ -474,7 +501,8 @@
474
501
  }
475
502
 
476
503
  .ermis-attachment-aspect-box .ermis-attachment--image.ermis-attachment--loaded,
477
- .ermis-attachment-aspect-box .ermis-attachment--video.ermis-attachment--loaded {
504
+ .ermis-attachment-aspect-box .ermis-attachment--video.ermis-attachment--loaded,
505
+ .ermis-attachment-aspect-box .ermis-attachment--video-poster.ermis-attachment--loaded {
478
506
  opacity: 1;
479
507
  }
480
508
 
@@ -485,7 +513,8 @@
485
513
  min-height: 100px;
486
514
  }
487
515
 
488
- .ermis-attachment--image {
516
+ .ermis-attachment--image,
517
+ .ermis-attachment--video-poster {
489
518
  max-width: 300px;
490
519
  max-height: 200px;
491
520
  border-radius: var(--ermis-radius-md);
@@ -517,14 +546,34 @@
517
546
  }
518
547
 
519
548
  .ermis-attachment__file-icon {
520
- font-size: 1.25rem;
549
+ width: 40px;
550
+ height: 40px;
551
+ min-width: 40px;
552
+ border-radius: var(--ermis-radius-md);
553
+ background: var(--ermis-bg-secondary);
554
+ display: flex;
555
+ flex-direction: column;
556
+ align-items: center;
557
+ justify-content: center;
558
+ color: var(--ermis-text-muted);
559
+ gap: 1px;
521
560
  flex-shrink: 0;
522
561
  }
523
562
 
563
+ .ermis-attachment__file-ext {
564
+ font-size: 8px;
565
+ font-weight: 700;
566
+ text-transform: uppercase;
567
+ letter-spacing: 0.3px;
568
+ color: var(--ermis-text-muted);
569
+ line-height: 1;
570
+ }
571
+
524
572
  .ermis-attachment__file-info {
525
573
  display: flex;
526
574
  flex-direction: column;
527
575
  min-width: 0;
576
+ flex: 1;
528
577
  }
529
578
 
530
579
  .ermis-attachment__file-name {
@@ -540,6 +589,48 @@
540
589
  color: var(--ermis-text-muted);
541
590
  }
542
591
 
592
+ .ermis-attachment__file-download {
593
+ display: flex;
594
+ align-items: center;
595
+ justify-content: center;
596
+ flex-shrink: 0;
597
+ width: 32px;
598
+ height: 32px;
599
+ border: none;
600
+ border-radius: var(--ermis-radius-full);
601
+ background: transparent;
602
+ color: var(--ermis-text-muted);
603
+ cursor: pointer;
604
+ transition: background-color var(--ermis-transition), color var(--ermis-transition);
605
+ }
606
+
607
+ .ermis-attachment__file-download:hover {
608
+ background-color: var(--ermis-bg-active);
609
+ color: var(--ermis-accent);
610
+ }
611
+
612
+ .ermis-message-bubble--own .ermis-attachment__file-name {
613
+ color: rgba(255, 255, 255, 0.9);
614
+ }
615
+
616
+ .ermis-message-bubble--own .ermis-attachment__file-icon {
617
+ background: rgba(255, 255, 255, 0.2);
618
+ color: #fff;
619
+ }
620
+
621
+ .ermis-message-bubble--own .ermis-attachment__file-ext {
622
+ color: rgba(255, 255, 255, 0.8);
623
+ }
624
+
625
+ .ermis-message-bubble--own .ermis-attachment__file-download {
626
+ color: rgba(255, 255, 255, 0.7);
627
+ }
628
+
629
+ .ermis-message-bubble--own .ermis-attachment__file-download:hover {
630
+ background-color: rgba(255, 255, 255, 0.15);
631
+ color: #fff;
632
+ }
633
+
543
634
  /* --- Voice recording --- */
544
635
  .ermis-attachment--voice {
545
636
  display: flex;
@@ -459,3 +459,28 @@
459
459
  .ermis-message-list__reject-btn:active {
460
460
  transform: translateY(0);
461
461
  }
462
+
463
+ /* Pending Invitee Notification */
464
+ .ermis-message-list__pending-invitee {
465
+ background-color: var(--ermis-bg-secondary);
466
+ border: 1px solid var(--ermis-border);
467
+ border-radius: var(--ermis-radius-md, 8px);
468
+ padding: var(--ermis-spacing-sm, 12px) var(--ermis-spacing-md, 16px);
469
+ margin: var(--ermis-spacing-sm) var(--ermis-spacing-lg);
470
+ display: flex;
471
+ align-items: center;
472
+ justify-content: center;
473
+ }
474
+
475
+ .ermis-message-list__pending-invitee-content {
476
+ display: flex;
477
+ align-items: center;
478
+ gap: var(--ermis-spacing-md, 12px);
479
+ color: var(--ermis-text-secondary);
480
+ font-size: var(--ermis-font-size-sm, 14px);
481
+ font-family: var(--ermis-font-family, sans-serif);
482
+ }
483
+
484
+ .ermis-message-list__pending-invitee-content svg {
485
+ color: var(--ermis-accent);
486
+ }
@@ -26,3 +26,4 @@
26
26
  @import './_create-channel-modal.css';
27
27
  @import './_call-ui.css';
28
28
  @import './_topic-modal.css';
29
+ @import './_media-lightbox.css';
package/src/types.ts CHANGED
@@ -254,6 +254,8 @@ export type AvatarProps = {
254
254
  size?: number;
255
255
  /** Additional CSS class name */
256
256
  className?: string;
257
+ /** Disable opening the lightbox on click */
258
+ disableLightbox?: boolean;
257
259
  };
258
260
 
259
261
  /* ----------------------------------------------------------
@@ -296,6 +298,14 @@ export type ChannelHeaderProps = {
296
298
  videoCallTitle?: string;
297
299
  /** Custom component to show when a call is active (e.g. "Call in progress" badge) */
298
300
  CallBadgeComponent?: React.ComponentType<{ callType: string }>;
301
+ /** Show online/offline indicator for direct friend channels (default: true) */
302
+ showOnlineStatus?: boolean;
303
+ /** I18n label for "Online" subtitle (default: "Online") */
304
+ onlineLabel?: string;
305
+ /** I18n label for "Offline" subtitle (default: "Offline") */
306
+ offlineLabel?: string;
307
+ /** Custom online indicator component (replaces the default dot + label) */
308
+ OnlineIndicatorComponent?: React.ComponentType<{ isOnline: boolean }>;
299
309
  };
300
310
 
301
311
  /** Data passed to a fully custom HeaderComponent */
@@ -384,6 +394,8 @@ export type ChannelItemProps = {
384
394
  actionLabels?: ChannelActionLabels;
385
395
  /** Custom icons for default channel actions */
386
396
  actionIcons?: ChannelActionIcons;
397
+ /** Whether the other user in this direct channel is online (friend channels only) */
398
+ isOnline?: boolean;
387
399
  };
388
400
 
389
401
  export type ChannelListProps = {
@@ -436,6 +448,8 @@ export type ChannelListProps = {
436
448
  actionLabels?: ChannelActionLabels;
437
449
  /** Custom icons for default channel actions */
438
450
  actionIcons?: ChannelActionIcons;
451
+ /** Show online/offline indicator dots on channel item avatars for friend channels (default: true) */
452
+ showOnlineStatus?: boolean;
439
453
  };
440
454
 
441
455
  /* ----------------------------------------------------------
@@ -443,6 +457,8 @@ export type ChannelListProps = {
443
457
  ---------------------------------------------------------- */
444
458
  export type AttachmentProps = {
445
459
  attachment: Attachment;
460
+ /** Click handler — when provided, attachment becomes clickable (opens lightbox) */
461
+ onClick?: () => void;
446
462
  };
447
463
 
448
464
  export type MessageRendererProps = {
@@ -464,6 +480,23 @@ export type JumpToLatestProps = {
464
480
  onClick: () => void;
465
481
  };
466
482
 
483
+ /* ----------------------------------------------------------
484
+ MediaLightbox types
485
+ ---------------------------------------------------------- */
486
+ export type MediaLightboxItem = {
487
+ type: 'image' | 'video';
488
+ src: string;
489
+ alt?: string;
490
+ posterSrc?: string;
491
+ };
492
+
493
+ export type MediaLightboxProps = {
494
+ items: MediaLightboxItem[];
495
+ initialIndex?: number;
496
+ isOpen: boolean;
497
+ onClose: () => void;
498
+ };
499
+
467
500
  /* ----------------------------------------------------------
468
501
  MessageList types
469
502
  ---------------------------------------------------------- */
@@ -514,6 +547,8 @@ export type MessageListProps = {
514
547
  TypingIndicatorComponent?: React.ComponentType;
515
548
  /** Custom component for message reactions */
516
549
  MessageReactionsComponent?: React.ComponentType<MessageReactionsProps>;
550
+ /** Custom media lightbox component (replaces the default lightbox entirely) */
551
+ MediaLightboxComponent?: React.ComponentType<MediaLightboxProps>;
517
552
 
518
553
  /** I18n Labels */
519
554
  emptyTitle?: string;
@@ -527,9 +562,19 @@ export type MessageListProps = {
527
562
  pendingOverlaySubtitle?: string;
528
563
  pendingAcceptLabel?: string;
529
564
  pendingRejectLabel?: string;
565
+ /** I18n Label for skip button on direct messaging channels (default: "Skip") */
566
+ pendingSkipLabel?: string;
567
+ skippedOverlayTitle?: string;
568
+ skippedOverlaySubtitle?: string;
569
+ skippedAcceptLabel?: string;
530
570
  closedTopicOverlayTitle?: string;
531
571
  closedTopicOverlaySubtitle?: string;
532
572
  closedTopicReopenLabel?: string;
573
+
574
+ /** Custom component for pending invitee notification in direct channels */
575
+ PendingInviteeNotificationComponent?: React.ComponentType<{ inviteeName?: string, label?: string }>;
576
+ /** I18n Label for pending invitee notification */
577
+ pendingInviteeLabel?: string | ((inviteeName?: string) => string);
533
578
  };
534
579
 
535
580
  /* ----------------------------------------------------------
@@ -1369,6 +1414,7 @@ export type CreateChannelModalProps = {
1369
1414
  cancelButtonLabel?: string;
1370
1415
  createButtonLabel?: string;
1371
1416
  creatingButtonLabel?: string;
1417
+ messageButtonLabel?: string;
1372
1418
 
1373
1419
  /** File upload configuration for group channel images */
1374
1420
  imageAccept?: string;