@ermis-network/ermis-chat-react 1.0.6 → 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 (62) hide show
  1. package/dist/index.cjs +3802 -1772
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +836 -25
  4. package/dist/index.css.map +1 -1
  5. package/dist/index.d.mts +304 -1
  6. package/dist/index.d.ts +304 -1
  7. package/dist/index.mjs +3755 -1761
  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/BannedOverlay.tsx +40 -0
  14. package/src/components/ChannelActions.tsx +233 -0
  15. package/src/components/ChannelHeader.tsx +126 -5
  16. package/src/components/ChannelInfo/ChannelInfo.tsx +128 -24
  17. package/src/components/ChannelInfo/ChannelInfoTabs.tsx +67 -28
  18. package/src/components/ChannelInfo/ChannelSettingsPanel.tsx +90 -1
  19. package/src/components/ChannelInfo/EditChannelModal.tsx +5 -4
  20. package/src/components/ChannelInfo/MemberListItem.tsx +2 -1
  21. package/src/components/ChannelList.tsx +514 -47
  22. package/src/components/ClosedTopicOverlay.tsx +38 -0
  23. package/src/components/CreateChannelModal.tsx +53 -16
  24. package/src/components/EditPreview.tsx +2 -1
  25. package/src/components/ForwardMessageModal.tsx +2 -1
  26. package/src/components/MediaLightbox.tsx +314 -0
  27. package/src/components/MessageInput.tsx +21 -3
  28. package/src/components/MessageItem.tsx +10 -12
  29. package/src/components/MessageQuickReactions.tsx +3 -2
  30. package/src/components/MessageReactions.tsx +8 -3
  31. package/src/components/MessageRenderers.tsx +174 -54
  32. package/src/components/PendingOverlay.tsx +51 -0
  33. package/src/components/PinnedMessages.tsx +2 -1
  34. package/src/components/ReplyPreview.tsx +2 -1
  35. package/src/components/SkippedOverlay.tsx +36 -0
  36. package/src/components/TopicModal.tsx +189 -0
  37. package/src/components/UserPicker.tsx +1 -1
  38. package/src/components/VirtualMessageList.tsx +162 -47
  39. package/src/hooks/useBannedState.ts +27 -3
  40. package/src/hooks/useBlockedState.ts +3 -2
  41. package/src/hooks/useChannelCapabilities.ts +10 -8
  42. package/src/hooks/useChannelData.ts +1 -1
  43. package/src/hooks/useChannelListUpdates.ts +28 -5
  44. package/src/hooks/useChannelMessages.ts +2 -3
  45. package/src/hooks/useChannelRowUpdates.ts +9 -2
  46. package/src/hooks/useMessageActions.ts +23 -9
  47. package/src/hooks/useOnlineStatus.ts +71 -0
  48. package/src/hooks/useOnlineUsers.ts +115 -0
  49. package/src/hooks/usePendingState.ts +8 -3
  50. package/src/index.ts +67 -10
  51. package/src/messageTypeUtils.ts +64 -0
  52. package/src/styles/_channel-info.css +21 -0
  53. package/src/styles/_channel-list.css +276 -6
  54. package/src/styles/_media-lightbox.css +263 -0
  55. package/src/styles/_message-bubble.css +170 -13
  56. package/src/styles/_message-input.css +24 -0
  57. package/src/styles/_message-list.css +76 -6
  58. package/src/styles/_message-quick-reactions.css +5 -0
  59. package/src/styles/_message-reactions.css +7 -0
  60. package/src/styles/_topic-modal.css +154 -0
  61. package/src/styles/index.css +2 -0
  62. package/src/types.ts +203 -3
package/src/types.ts CHANGED
@@ -1,4 +1,13 @@
1
- import type { FormatMessageResponse, MessageLabel, Attachment, Channel, ChannelFilters, ChannelSort, ChannelQueryOptions, UserCallInfo } from '@ermis-network/ermis-chat-sdk';
1
+ import type {
2
+ FormatMessageResponse,
3
+ MessageLabel,
4
+ Attachment,
5
+ Channel,
6
+ ChannelFilters,
7
+ ChannelSort,
8
+ ChannelQueryOptions,
9
+ UserCallInfo,
10
+ } from '@ermis-network/ermis-chat-sdk';
2
11
  import type { ErmisChat } from '@ermis-network/ermis-chat-sdk';
3
12
 
4
13
  /* ----------------------------------------------------------
@@ -245,6 +254,8 @@ export type AvatarProps = {
245
254
  size?: number;
246
255
  /** Additional CSS class name */
247
256
  className?: string;
257
+ /** Disable opening the lightbox on click */
258
+ disableLightbox?: boolean;
248
259
  };
249
260
 
250
261
  /* ----------------------------------------------------------
@@ -287,6 +298,14 @@ export type ChannelHeaderProps = {
287
298
  videoCallTitle?: string;
288
299
  /** Custom component to show when a call is active (e.g. "Call in progress" badge) */
289
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 }>;
290
309
  };
291
310
 
292
311
  /** Data passed to a fully custom HeaderComponent */
@@ -299,6 +318,48 @@ export type ChannelHeaderData = {
299
318
  /* ----------------------------------------------------------
300
319
  ChannelList types
301
320
  ---------------------------------------------------------- */
321
+ export type ChannelAction = {
322
+ id: string;
323
+ label: string;
324
+ icon?: React.ReactNode;
325
+ onClick: (channel: Channel, e: React.MouseEvent) => void;
326
+ isDanger?: boolean;
327
+ };
328
+
329
+ export type ChannelActionLabels = {
330
+ pinChannel?: string;
331
+ unpinChannel?: string;
332
+ pinTopic?: string;
333
+ unpinTopic?: string;
334
+ blockUser?: string;
335
+ unblockUser?: string;
336
+ editTopic?: string;
337
+ closeTopic?: string;
338
+ reopenTopic?: string;
339
+ createTopic?: string;
340
+ deleteChannel?: string;
341
+ leaveChannel?: string;
342
+ };
343
+
344
+ export type ChannelActionIcons = {
345
+ PinIcon?: React.ReactNode;
346
+ UnpinIcon?: React.ReactNode;
347
+ BlockIcon?: React.ReactNode;
348
+ UnblockIcon?: React.ReactNode;
349
+ EditTopicIcon?: React.ReactNode;
350
+ CloseTopicIcon?: React.ReactNode;
351
+ ReopenTopicIcon?: React.ReactNode;
352
+ CreateTopicIcon?: React.ReactNode;
353
+ DeleteChannelIcon?: React.ReactNode;
354
+ LeaveChannelIcon?: React.ReactNode;
355
+ };
356
+
357
+ export type ChannelActionsProps = {
358
+ channel: Channel;
359
+ actions: ChannelAction[];
360
+ onClose: () => void;
361
+ };
362
+
302
363
  export type ChannelItemProps = {
303
364
  channel: Channel;
304
365
  isActive: boolean;
@@ -306,6 +367,7 @@ export type ChannelItemProps = {
306
367
  unreadCount: number;
307
368
  lastMessageText: string;
308
369
  lastMessageUser: string;
370
+ lastMessageTimestamp?: Date | string | null;
309
371
  onSelect: (channel: Channel) => void;
310
372
  AvatarComponent: React.ComponentType<AvatarProps>;
311
373
  /** Whether the current user has blocked this channel (messaging only) */
@@ -316,6 +378,24 @@ export type ChannelItemProps = {
316
378
  pendingBadgeLabel?: string;
317
379
  /** Label for the blocked channel badge indicator */
318
380
  blockedBadgeLabel?: string;
381
+ isClosedTopic?: boolean;
382
+ closedTopicIcon?: React.ReactNode;
383
+ PinnedIconComponent?: React.ComponentType;
384
+ ChannelActionsComponent?: React.ComponentType<ChannelActionsProps>;
385
+ /** Handler when Create Topic action is triggered */
386
+ onAddTopic?: (channel: Channel) => void;
387
+ /** Handler when Edit Topic action is triggered */
388
+ onEditTopic?: (channel: Channel) => void;
389
+ /** Handler when Close/Reopen Topic action is triggered */
390
+ onToggleCloseTopic?: (channel: Channel, isClosed: boolean) => void;
391
+ /** Array of action IDs to hide from the actions dropdown */
392
+ hiddenActions?: string[];
393
+ /** Custom labels for default channel actions */
394
+ actionLabels?: ChannelActionLabels;
395
+ /** Custom icons for default channel actions */
396
+ actionIcons?: ChannelActionIcons;
397
+ /** Whether the other user in this direct channel is online (friend channels only) */
398
+ isOnline?: boolean;
319
399
  };
320
400
 
321
401
  export type ChannelListProps = {
@@ -325,6 +405,8 @@ export type ChannelListProps = {
325
405
  renderChannel?: (channel: Channel, isActive: boolean) => React.ReactNode;
326
406
  onChannelSelect?: (channel: Channel) => void;
327
407
  className?: string;
408
+ /** Array of action IDs to hide from the actions dropdown */
409
+ hiddenActions?: string[];
328
410
  LoadingIndicator?: React.ComponentType<{ text?: string }>;
329
411
  EmptyStateIndicator?: React.ComponentType<{ text?: string }>;
330
412
  AvatarComponent?: React.ComponentType<AvatarProps>;
@@ -342,6 +424,32 @@ export type ChannelListProps = {
342
424
  emptyStateLabel?: string;
343
425
  /** Label for the blocked channel badge hover */
344
426
  blockedBadgeLabel?: string;
427
+ /** Custom component for rendering topic group */
428
+ ChannelTopicGroupComponent?: React.ComponentType<any>;
429
+ /** Custom avatar component for general topic */
430
+ GeneralTopicAvatarComponent?: React.ComponentType<any>;
431
+ /** Custom avatar component for other topics */
432
+ TopicAvatarComponent?: React.ComponentType<any>;
433
+ /** Name for the general topic (default: "general") */
434
+ generalTopicLabel?: string;
435
+ /** Handler when Add Topic button is clicked on a team channel */
436
+ onAddTopic?: (channel: Channel) => void;
437
+ /** Optional custom emoji picker for TopicModal */
438
+ TopicEmojiPickerComponent?: React.ComponentType<any>;
439
+ closedTopicIcon?: React.ReactNode;
440
+ PinnedIconComponent?: React.ComponentType;
441
+ /** Custom component for channel actions dropdown */
442
+ ChannelActionsComponent?: React.ComponentType<ChannelActionsProps>;
443
+ /** Handler when Edit Topic action is triggered */
444
+ onEditTopic?: (channel: Channel) => void;
445
+ /** Handler when Close/Reopen Topic action is triggered */
446
+ onToggleCloseTopic?: (channel: Channel, isClosed: boolean) => void;
447
+ /** Custom labels for default channel actions */
448
+ actionLabels?: ChannelActionLabels;
449
+ /** Custom icons for default channel actions */
450
+ actionIcons?: ChannelActionIcons;
451
+ /** Show online/offline indicator dots on channel item avatars for friend channels (default: true) */
452
+ showOnlineStatus?: boolean;
345
453
  };
346
454
 
347
455
  /* ----------------------------------------------------------
@@ -349,6 +457,8 @@ export type ChannelListProps = {
349
457
  ---------------------------------------------------------- */
350
458
  export type AttachmentProps = {
351
459
  attachment: Attachment;
460
+ /** Click handler — when provided, attachment becomes clickable (opens lightbox) */
461
+ onClick?: () => void;
352
462
  };
353
463
 
354
464
  export type MessageRendererProps = {
@@ -370,6 +480,23 @@ export type JumpToLatestProps = {
370
480
  onClick: () => void;
371
481
  };
372
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
+
373
500
  /* ----------------------------------------------------------
374
501
  MessageList types
375
502
  ---------------------------------------------------------- */
@@ -420,6 +547,8 @@ export type MessageListProps = {
420
547
  TypingIndicatorComponent?: React.ComponentType;
421
548
  /** Custom component for message reactions */
422
549
  MessageReactionsComponent?: React.ComponentType<MessageReactionsProps>;
550
+ /** Custom media lightbox component (replaces the default lightbox entirely) */
551
+ MediaLightboxComponent?: React.ComponentType<MediaLightboxProps>;
423
552
 
424
553
  /** I18n Labels */
425
554
  emptyTitle?: string;
@@ -433,6 +562,19 @@ export type MessageListProps = {
433
562
  pendingOverlaySubtitle?: string;
434
563
  pendingAcceptLabel?: string;
435
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;
570
+ closedTopicOverlayTitle?: string;
571
+ closedTopicOverlaySubtitle?: string;
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);
436
578
  };
437
579
 
438
580
  /* ----------------------------------------------------------
@@ -460,6 +602,8 @@ export type MessageReactionsProps = {
460
602
  AvatarComponent?: React.ComponentType<AvatarProps>;
461
603
  /** Callback when clicking a reaction */
462
604
  onClickReaction?: (type: string) => void;
605
+ /** Whether interactions are disabled */
606
+ disabled?: boolean;
463
607
  };
464
608
 
465
609
  /* ----------------------------------------------------------
@@ -580,7 +724,11 @@ export type MessageInputProps = {
580
724
  /** Custom reply preview component */
581
725
  ReplyPreviewComponent?: React.ComponentType<ReplyPreviewProps>;
582
726
  /** Custom edit preview component */
583
- EditPreviewComponent?: React.ComponentType<{ message: FormatMessageResponse; onDismiss: () => void; editingMessageLabel?: string }>;
727
+ EditPreviewComponent?: React.ComponentType<{
728
+ message: FormatMessageResponse;
729
+ onDismiss: () => void;
730
+ editingMessageLabel?: string;
731
+ }>;
584
732
  /** I18n Label for banned state */
585
733
  bannedLabel?: string;
586
734
  /** I18n Label for blocked state (messaging channels) */
@@ -593,6 +741,8 @@ export type MessageInputProps = {
593
741
  sendDisabledLabel?: string;
594
742
  /** I18n Label for slow mode active */
595
743
  slowModeLabel?: (cooldown: number) => React.ReactNode;
744
+ /** I18n Label for closed topic */
745
+ closedTopicLabel?: string;
596
746
  };
597
747
 
598
748
  /* ----------------------------------------------------------
@@ -867,6 +1017,10 @@ export type ChannelInfoCoverProps = {
867
1017
  onEditClick?: () => void;
868
1018
  /** Whether the channel is public */
869
1019
  isPublic?: boolean;
1020
+ /** Name of the parent channel (if this is a topic) */
1021
+ parentChannelName?: string;
1022
+ /** Whether the channel is a topic */
1023
+ isTopic?: boolean;
870
1024
  /** Whether the channel is a team channel */
871
1025
  isTeamChannel?: boolean;
872
1026
  };
@@ -879,6 +1033,8 @@ export type ChannelInfoActionsProps = {
879
1033
  onBlockUser?: () => void;
880
1034
  onUnblockUser?: () => void;
881
1035
  isTeamChannel?: boolean;
1036
+ isTopic?: boolean;
1037
+ isClosedTopic?: boolean;
882
1038
  isBlocked?: boolean;
883
1039
  currentUserRole?: string;
884
1040
  searchLabel?: string;
@@ -887,6 +1043,10 @@ export type ChannelInfoActionsProps = {
887
1043
  leaveLabel?: string;
888
1044
  blockLabel?: string;
889
1045
  unblockLabel?: string;
1046
+ onCloseTopic?: () => void;
1047
+ onReopenTopic?: () => void;
1048
+ closeTopicLabel?: string;
1049
+ reopenTopicLabel?: string;
890
1050
  };
891
1051
 
892
1052
  export type ChannelInfoMember = {
@@ -988,7 +1148,11 @@ export type AddMemberModalProps = {
988
1148
  /** Custom user item component (replaces the default row) */
989
1149
  UserItemComponent?: React.ComponentType<AddMemberUserItemProps>;
990
1150
  /** Custom search input component */
991
- SearchInputComponent?: React.ComponentType<{ value: string; onChange: (e: React.ChangeEvent<HTMLInputElement>) => void; placeholder: string }>;
1151
+ SearchInputComponent?: React.ComponentType<{
1152
+ value: string;
1153
+ onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
1154
+ placeholder: string;
1155
+ }>;
992
1156
  };
993
1157
 
994
1158
  export type ChannelInfoTabsProps = {
@@ -1101,6 +1265,13 @@ export type ChannelInfoProps = {
1101
1265
  /** I18n labels for block/unblock actions */
1102
1266
  actionsBlockLabel?: string;
1103
1267
  actionsUnblockLabel?: string;
1268
+ actionsCloseTopicLabel?: string;
1269
+ actionsReopenTopicLabel?: string;
1270
+
1271
+ /** Settings Panel Topics Labels */
1272
+ settingsWorkspaceTopicsTitle?: string;
1273
+ settingsTopicsFeatureName?: string;
1274
+ settingsTopicsFeatureDescription?: string;
1104
1275
  };
1105
1276
 
1106
1277
  /* ----------------------------------------------------------
@@ -1144,6 +1315,10 @@ export type ChannelSettingsPanelProps = {
1144
1315
  title?: string;
1145
1316
  /** Custom slow mode options */
1146
1317
  slowModeOptions?: { label: string; value: number }[];
1318
+ /** I18n labels for Topics settings */
1319
+ workspaceTopicsTitle?: string;
1320
+ topicsFeatureName?: string;
1321
+ topicsFeatureDescription?: string;
1147
1322
  };
1148
1323
 
1149
1324
  /* ----------------------------------------------------------
@@ -1239,9 +1414,34 @@ export type CreateChannelModalProps = {
1239
1414
  cancelButtonLabel?: string;
1240
1415
  createButtonLabel?: string;
1241
1416
  creatingButtonLabel?: string;
1417
+ messageButtonLabel?: string;
1242
1418
 
1243
1419
  /** File upload configuration for group channel images */
1244
1420
  imageAccept?: string;
1245
1421
  maxImageSize?: number; // bytes
1246
1422
  maxImageSizeError?: string;
1247
1423
  };
1424
+
1425
+ export type TopicModalProps = {
1426
+ isOpen: boolean;
1427
+ onClose: () => void;
1428
+ onSuccess?: (channel: Channel) => void;
1429
+ /** Inject external emoji picker component */
1430
+ EmojiPickerComponent?: React.ComponentType<{ onSelect: (emoji: any) => void; [key: string]: any }>;
1431
+ /** Parent team channel to create topic under, will use activeChannel if not provided */
1432
+ parentChannel?: Channel;
1433
+
1434
+ /** If provided, operates in edit mode for this topic */
1435
+ topic?: Channel;
1436
+
1437
+ /** i18n labels */
1438
+ title?: string;
1439
+ nameLabel?: string;
1440
+ namePlaceholder?: string;
1441
+ emojiLabel?: string;
1442
+ descriptionLabel?: string;
1443
+ descriptionPlaceholder?: string;
1444
+ cancelButtonLabel?: string;
1445
+ saveButtonLabel?: string;
1446
+ savingButtonLabel?: string;
1447
+ };