@medipha/chat 1.0.14

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 (119) hide show
  1. package/README.md +103 -0
  2. package/dist/api/client.d.ts +5 -0
  3. package/dist/features/composer/attachment-file-helpers.d.ts +22 -0
  4. package/dist/features/composer/attachments/attachment-buttons.d.ts +8 -0
  5. package/dist/features/composer/conversation-composer.d.ts +20 -0
  6. package/dist/features/composer/editor/font-size-extension.d.ts +18 -0
  7. package/dist/features/composer/editor/mention-suggestion-list.d.ts +15 -0
  8. package/dist/features/composer/editor/paste-text-style.d.ts +5 -0
  9. package/dist/features/composer/emoji/emoji-picker-button.d.ts +9 -0
  10. package/dist/features/composer/emoji/emoji-shortcode-extension.d.ts +9 -0
  11. package/dist/features/composer/emoji/emoji-shortcodes.d.ts +2 -0
  12. package/dist/features/composer/format-bar/format-bar.d.ts +14 -0
  13. package/dist/features/composer/more-menu/more-menu.d.ts +4 -0
  14. package/dist/features/composer/pending-attachment.d.ts +10 -0
  15. package/dist/features/composer/reply/composer-reply-banner.d.ts +11 -0
  16. package/dist/features/composer/reply/use-reply-target.d.ts +12 -0
  17. package/dist/features/composer/stickers.d.ts +6 -0
  18. package/dist/features/composer/toolbar/composer-toolbar.d.ts +19 -0
  19. package/dist/features/composer/typing/use-typing-notifier.d.ts +11 -0
  20. package/dist/features/composer/use-attachment-upload.d.ts +4 -0
  21. package/dist/features/composer/use-pending-attachments.d.ts +16 -0
  22. package/dist/features/create-conversation/create-conversation-panel.d.ts +9 -0
  23. package/dist/features/create-conversation/tabs/create-group-tab.d.ts +13 -0
  24. package/dist/features/create-conversation/tabs/direct-chat-tab.d.ts +12 -0
  25. package/dist/features/create-conversation/tabs/selected-members-modal.d.ts +11 -0
  26. package/dist/features/files/file-attachment.d.ts +24 -0
  27. package/dist/features/forward/forward-dialog.d.ts +11 -0
  28. package/dist/features/forward/use-forward-message.d.ts +21 -0
  29. package/dist/features/media-preview/media-preview-modal.d.ts +23 -0
  30. package/dist/features/media-preview/use-image-lightbox.d.ts +16 -0
  31. package/dist/features/message-list/bubble/message-actions/message-actions-menu.d.ts +27 -0
  32. package/dist/features/message-list/bubble/message-bubble.d.ts +53 -0
  33. package/dist/features/message-list/bubble/message-content-body.d.ts +12 -0
  34. package/dist/features/message-list/bubble/message-reactions.d.ts +18 -0
  35. package/dist/features/message-list/bubble/rich-message-content.d.ts +8 -0
  36. package/dist/features/message-list/overlays/message-details/message-details-modal.d.ts +16 -0
  37. package/dist/features/message-list/overlays/message-details/use-message-details-modal.d.ts +14 -0
  38. package/dist/features/message-list/overlays/message-menu/use-message-menu.d.ts +21 -0
  39. package/dist/features/message-list/overlays/reaction/reaction-modal.d.ts +22 -0
  40. package/dist/features/message-list/overlays/reaction/use-reaction-modal.d.ts +23 -0
  41. package/dist/features/message-list/overlays/reaction/use-reaction-picker.d.ts +8 -0
  42. package/dist/features/message-list/overlays/system-members/system-members-modal.d.ts +22 -0
  43. package/dist/features/message-list/overlays/system-members/use-system-members-modal.d.ts +18 -0
  44. package/dist/features/message-list/use-message-removal.d.ts +12 -0
  45. package/dist/features/message-list/use-message-timeline-scroll.d.ts +25 -0
  46. package/dist/features/message-window/use-message-window.d.ts +46 -0
  47. package/dist/features/pinned-messages/pinned-messages-bar.d.ts +18 -0
  48. package/dist/features/pinned-messages/use-pinned-messages.d.ts +21 -0
  49. package/dist/features/typing/use-typing-indicator.d.ts +12 -0
  50. package/dist/index.cjs +278 -0
  51. package/dist/index.d.ts +2 -0
  52. package/dist/index.js +66322 -0
  53. package/dist/module/chat-module.d.ts +3 -0
  54. package/dist/module/chat-module.types.d.ts +11 -0
  55. package/dist/module/chat-routes.d.ts +4 -0
  56. package/dist/module/chat-runtime-context.d.ts +5 -0
  57. package/dist/pages/Setting/archive/archive-attachment-preview.d.ts +13 -0
  58. package/dist/pages/Setting/archive/archive-file-list.d.ts +15 -0
  59. package/dist/pages/Setting/archive/archive-helpers.d.ts +37 -0
  60. package/dist/pages/Setting/archive/archive-media-grid.d.ts +14 -0
  61. package/dist/pages/Setting/archive/archive-view.d.ts +19 -0
  62. package/dist/pages/Setting/archive/hooks/use-conversation-archive.d.ts +22 -0
  63. package/dist/pages/Setting/conversation-settings-panel.d.ts +20 -0
  64. package/dist/pages/Setting/hooks/use-conversation-settings.d.ts +41 -0
  65. package/dist/pages/Setting/hooks/use-search-panel.d.ts +7 -0
  66. package/dist/pages/Setting/index.d.ts +39 -0
  67. package/dist/pages/Setting/members/add-member-modal.d.ts +11 -0
  68. package/dist/pages/Setting/members/group-members-modal.d.ts +21 -0
  69. package/dist/pages/Setting/members/hooks/use-rename-group-dialog.d.ts +15 -0
  70. package/dist/pages/Setting/members/member-selection-footer.d.ts +12 -0
  71. package/dist/pages/Setting/members/members-list-modal.d.ts +29 -0
  72. package/dist/pages/Setting/members/settings-modal.d.ts +36 -0
  73. package/dist/pages/Setting/members/use-member-selection.d.ts +18 -0
  74. package/dist/pages/Setting/search-panel.d.ts +13 -0
  75. package/dist/pages/conversation/conversation-detail/conversation-detail.d.ts +92 -0
  76. package/dist/pages/conversation/conversation-footer/conversation-footer.d.ts +42 -0
  77. package/dist/pages/conversation/conversation-header/conversation-header.d.ts +27 -0
  78. package/dist/pages/conversation/hooks/use-current-membership.d.ts +14 -0
  79. package/dist/pages/conversation/index.d.ts +28 -0
  80. package/dist/pages/conversations/empty-state/conversation-empty-state.d.ts +3 -0
  81. package/dist/pages/conversations/index.d.ts +4 -0
  82. package/dist/pages/sidebar/hooks/use-conversations.d.ts +17 -0
  83. package/dist/pages/sidebar/index.d.ts +4 -0
  84. package/dist/pages/sidebar/sidbar-conversation-list/sidbar-conversation-list.d.ts +13 -0
  85. package/dist/pages/sidebar/sidebar-filter/sidebar-filter.d.ts +13 -0
  86. package/dist/pages/sidebar/sidebar-header-infor/sidebar-header-infor.d.ts +4 -0
  87. package/dist/shared/components/banner-carousel.d.ts +9 -0
  88. package/dist/shared/components/conversation-avatar.d.ts +19 -0
  89. package/dist/shared/components/date-range-filter-pill.d.ts +17 -0
  90. package/dist/shared/components/download-action.d.ts +11 -0
  91. package/dist/shared/components/filter-pill.d.ts +15 -0
  92. package/dist/shared/components/image-not-available.d.ts +12 -0
  93. package/dist/shared/components/image-with-fallback.d.ts +13 -0
  94. package/dist/shared/components/load-more-sentinel.d.ts +15 -0
  95. package/dist/shared/components/quote-icon.d.ts +5 -0
  96. package/dist/shared/components/search-empty-state.d.ts +8 -0
  97. package/dist/shared/components/shared-icon.d.ts +5 -0
  98. package/dist/shared/components/user-avatar.d.ts +12 -0
  99. package/dist/shared/components/user-profile-modal.d.ts +13 -0
  100. package/dist/shared/components/verified-badge.d.ts +5 -0
  101. package/dist/shared/helper/dayjs-date-picker.d.ts +14 -0
  102. package/dist/shared/hooks/use-directory-filter.d.ts +13 -0
  103. package/dist/shared/hooks/use-user-profile-modal.d.ts +9 -0
  104. package/dist/socket/chatSocket.d.ts +6 -0
  105. package/dist/store/authStore.d.ts +34 -0
  106. package/dist/store/connectionStore.d.ts +8 -0
  107. package/dist/store/conversationsStore.d.ts +38 -0
  108. package/dist/store/forwardStore.d.ts +14 -0
  109. package/dist/store/pinnedMessagesStore.d.ts +16 -0
  110. package/dist/store/presenceStore.d.ts +11 -0
  111. package/dist/store/verifiedPermissionsStore.d.ts +20 -0
  112. package/dist/styles.css +2 -0
  113. package/dist/utils/chat-display.d.ts +44 -0
  114. package/dist/utils/chat-member-resolvers.d.ts +9 -0
  115. package/dist/utils/chat-toast.d.ts +18 -0
  116. package/dist/utils/chat.d.ts +282 -0
  117. package/dist/utils/token.d.ts +0 -0
  118. package/dist/utils/work-status.d.ts +15 -0
  119. package/package.json +96 -0
@@ -0,0 +1,3 @@
1
+ import { ChatModuleProps } from './chat-module.types';
2
+ /** Mounts chat inside the host router without persisting authentication state. */
3
+ export declare function ChatModule(chatModuleProps: ChatModuleProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,11 @@
1
+ export interface ChatModuleProps {
2
+ hasAddGroupVerified: boolean;
3
+ hasUpdateGroupVerified: boolean;
4
+ hasDeleteGroupVerified: boolean;
5
+ userId: string;
6
+ accessToken: string;
7
+ apiUrl: string;
8
+ socketUrl: string;
9
+ onUnauthorized?: () => void;
10
+ onNavigateOutsideChat?: (path: string) => void;
11
+ }
@@ -0,0 +1,4 @@
1
+ /** Declares routes relative to the ERP route that mounts ChatModule. ConversationScreen
2
+ * itself renders the sidebar plus either the empty state or the active conversation, so
3
+ * the index route (no conversationId) reuses it directly. */
4
+ export declare function ChatRoutes(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,5 @@
1
+ import { ChatModuleProps } from './chat-module.types';
2
+ export type ChatRuntimeContextValue = ChatModuleProps;
3
+ export declare const ChatRuntimeContext: import('react').Context<ChatModuleProps | undefined>;
4
+ /** Returns the current ERP-provided authentication and endpoint configuration. */
5
+ export declare function useChatRuntime(): ChatRuntimeContextValue;
@@ -0,0 +1,13 @@
1
+ import { ChatMessageAttachment } from '../../../utils/chat';
2
+ export interface AttachmentPreviewState {
3
+ kind: 'image' | 'file';
4
+ attachment: ChatMessageAttachment;
5
+ }
6
+ interface ArchiveAttachmentPreviewProps {
7
+ state: AttachmentPreviewState;
8
+ onClose: () => void;
9
+ }
10
+ /** Full-screen image preview for the archive media grid. Documents open in a new tab via
11
+ * an online document viewer from their file card instead. */
12
+ export declare function ArchiveAttachmentPreview({ state, onClose }: ArchiveAttachmentPreviewProps): import("react/jsx-runtime").JSX.Element;
13
+ export {};
@@ -0,0 +1,15 @@
1
+ import { ChatMessage, ChatMessageAttachment } from '../../../utils/chat';
2
+ import { ArchiveDayGroup, FileEntry } from './archive-helpers';
3
+ interface ArchiveFileListProps {
4
+ groups: Array<ArchiveDayGroup<FileEntry>>;
5
+ resolveSenderName: (senderId?: string) => string;
6
+ /** Kept for signature compatibility; preview is now handled inside each file card. */
7
+ onSelect?: (message: ChatMessage, attachment: ChatMessageAttachment) => void;
8
+ /** Forward this file to another conversation; omitted when sharing isn't available. */
9
+ onShare?: (message: ChatMessage) => void;
10
+ /** Jumps back to the message this file was sent in; omitted when unavailable. */
11
+ onJumpToMessage?: (message: ChatMessage) => void;
12
+ }
13
+ /** "Files" tab body — attachment cards grouped by day, each with preview/download/share. */
14
+ export declare function ArchiveFileList({ groups, resolveSenderName, onShare, onJumpToMessage }: ArchiveFileListProps): import("react/jsx-runtime").JSX.Element;
15
+ export {};
@@ -0,0 +1,37 @@
1
+ import { ChatMessage, ChatMessageAttachment } from '../../../utils/chat';
2
+ /**
3
+ * A message can carry several attachments (e.g. a multi-image grid), which live in
4
+ * `attachments[]`; a single-attachment message only has the flat `attachmentId`/`attachmentUrl`
5
+ * fields and an empty `attachments[]`. Normalize both shapes to one list so every attachment
6
+ * gets its own archive entry instead of just the message's first/cover one.
7
+ */
8
+ export declare function getMessageAttachments(message: ChatMessage): ChatMessageAttachment[];
9
+ export interface FileEntry {
10
+ message: ChatMessage;
11
+ attachment: ChatMessageAttachment;
12
+ }
13
+ export declare function flattenFileEntries(messages: ChatMessage[]): FileEntry[];
14
+ export type FileTypeFilter = 'all' | 'word' | 'excel' | 'pdf' | 'zip' | 'other';
15
+ export declare const FILE_TYPE_OPTIONS: Array<{
16
+ value: FileTypeFilter;
17
+ label: string;
18
+ }>;
19
+ export declare function getFileTypeCategoryByName(name: string | null | undefined): FileTypeFilter;
20
+ export declare function getFileTypeCategory(attachment: ChatMessageAttachment): FileTypeFilter;
21
+ /** Icon (label + color class) for a file by its name/extension — the archive's icon set. */
22
+ export declare function getFileIcon(name: string | null | undefined): {
23
+ label: string;
24
+ className: string;
25
+ };
26
+ export declare const FILE_TYPE_ICON: Record<FileTypeFilter, {
27
+ label: string;
28
+ className: string;
29
+ }>;
30
+ export declare function formatSentTime(createdAt: string): string;
31
+ export interface ArchiveDayGroup<T> {
32
+ label: string;
33
+ items: T[];
34
+ }
35
+ export declare function groupByDay<T>(items: T[], getDate: (item: T) => string): Array<ArchiveDayGroup<T>>;
36
+ /** Extracts the hostname from a URL for a compact "gitlab.meditech.vn"-style subtitle. */
37
+ export declare function getUrlHost(url: string): string;
@@ -0,0 +1,14 @@
1
+ import { ChatMessage, ChatMessageAttachment } from '../../../utils/chat';
2
+ import { ArchiveDayGroup } from './archive-helpers';
3
+ interface ArchiveMediaGridProps {
4
+ groups: Array<ArchiveDayGroup<ChatMessage>>;
5
+ resolveSenderName: (senderId?: string) => string;
6
+ onSelect: (message: ChatMessage, attachment: ChatMessageAttachment) => void;
7
+ /** Forward the image to another conversation; omitted when sharing isn't available. */
8
+ onShare?: (message: ChatMessage) => void;
9
+ /** Jumps back to the message this image was sent in; omitted when unavailable. */
10
+ onJumpToMessage?: (message: ChatMessage) => void;
11
+ }
12
+ /** "Ảnh/Video" tab body — thumbnails grouped by day, each with preview/download/share. */
13
+ export declare function ArchiveMediaGrid({ groups, resolveSenderName, onSelect, onShare, onJumpToMessage, }: ArchiveMediaGridProps): import("react/jsx-runtime").JSX.Element;
14
+ export {};
@@ -0,0 +1,19 @@
1
+ import { ChatMessage, ConversationMember } from '../../../utils/chat';
2
+ import { ArchiveTab } from './hooks/use-conversation-archive';
3
+ interface ArchiveViewProps {
4
+ conversationId: string | undefined;
5
+ initialTab: ArchiveTab;
6
+ members: ConversationMember[];
7
+ onBack: () => void;
8
+ /** Forwards a file's message to another conversation (opens the forward picker). */
9
+ onForwardMessage?: (message: ChatMessage) => void;
10
+ /** Jumps back to the message an attachment was sent in; omitted when unavailable. */
11
+ onJumpToMessage?: (message: ChatMessage) => void;
12
+ }
13
+ /**
14
+ * "Kho lưu trữ": browses a conversation's media, files, and links (entire history by
15
+ * default), filterable by day/sender/type. Renders inline in place of the settings panel
16
+ * body (not an overlay modal).
17
+ */
18
+ export declare function ArchiveView({ conversationId, initialTab, members, onBack, onForwardMessage, onJumpToMessage, }: ArchiveViewProps): import("react/jsx-runtime").JSX.Element;
19
+ export {};
@@ -0,0 +1,22 @@
1
+ import { ChatMessage } from '../../../../utils/chat';
2
+ export type ArchiveTab = 'media' | 'file';
3
+ export interface ArchiveDateRange {
4
+ from?: string | null;
5
+ to?: string | null;
6
+ }
7
+ /** Loads media/file/link archive data for a conversation (entire history by default),
8
+ * filterable by sender and an explicit sent-date range. */
9
+ export declare function useConversationArchive(conversationId: string | undefined): {
10
+ error: string;
11
+ fileItems: ChatMessage[];
12
+ load: (tab: ArchiveTab, dateRange?: ArchiveDateRange) => Promise<void>;
13
+ loadingTab: ArchiveTab | null;
14
+ mediaItems: ChatMessage[];
15
+ senderId: string | null;
16
+ setSenderId: import('react').Dispatch<import('react').SetStateAction<string | null>>;
17
+ };
18
+ /** Fetches a small preview (default 5-day window) of media and file items for the settings panel. */
19
+ export declare function useConversationArchivePreview(conversationId: string | undefined): {
20
+ filePreview: ChatMessage[];
21
+ mediaPreview: ChatMessage[];
22
+ };
@@ -0,0 +1,20 @@
1
+ import { ChatMessage, Conversation } from '../../utils/chat';
2
+ interface ConversationSettingsPanelProps {
3
+ conversation: Conversation | null;
4
+ conversationId: string | undefined;
5
+ currentUserId: string | null;
6
+ onClose: () => void;
7
+ onConversationUpdated: (update: Partial<Conversation>) => void;
8
+ onDeleteSuccess: () => void;
9
+ onLeaveSuccess: () => void;
10
+ onMembersChanged?: () => void;
11
+ /** Opens (or reuses) a direct conversation with the given user. */
12
+ onStartChat: (userId: string) => void;
13
+ /** Forwards a file's message to another conversation (opens the forward picker). */
14
+ onForwardMessage?: (message: ChatMessage) => void;
15
+ /** Jumps back to the message an attachment was sent in; omitted when unavailable. */
16
+ onJumpToMessage?: (message: ChatMessage) => void;
17
+ }
18
+ /** Slide-over panel exposing conversation settings and membership management. */
19
+ export declare function ConversationSettingsPanel({ conversation, conversationId, currentUserId, onClose, onConversationUpdated, onDeleteSuccess, onLeaveSuccess, onMembersChanged, onStartChat, onForwardMessage, onJumpToMessage, }: ConversationSettingsPanelProps): import("react/jsx-runtime").JSX.Element;
20
+ export {};
@@ -0,0 +1,41 @@
1
+ import { Conversation, ConversationMember } from '../../../utils/chat';
2
+ interface UseConversationSettingsParams {
3
+ conversationId: string | undefined;
4
+ currentUserId: string | null;
5
+ onConversationUpdated?: (conversation: Conversation) => void;
6
+ onDeleteSuccess?: () => void;
7
+ onLeaveSuccess?: () => void;
8
+ onMembersChanged?: () => void;
9
+ }
10
+ /** Coordinates member management, rename, delete, and leave actions for a conversation. */
11
+ export declare function useConversationSettings({ conversationId, currentUserId, onConversationUpdated, onDeleteSuccess, onLeaveSuccess, onMembersChanged, }: UseConversationSettingsParams): {
12
+ actionError: string;
13
+ addingUserId: string | null;
14
+ addMember: (userId: string) => Promise<boolean>;
15
+ addMembers: (userIds: string[]) => Promise<boolean>;
16
+ currentMember: ConversationMember | null;
17
+ deletingUserId: string | null;
18
+ deleteGroup: () => Promise<boolean>;
19
+ deleteMember: (userId: string) => Promise<boolean>;
20
+ removeMembers: (userIds: string[]) => Promise<boolean>;
21
+ isDeletingGroup: boolean;
22
+ isLeaving: boolean;
23
+ isLoadingMembers: boolean;
24
+ isLoadingMoreMembers: boolean;
25
+ isRenaming: boolean;
26
+ isUpdatingAvatar: boolean;
27
+ isOwner: boolean;
28
+ leave: (nextOwnerId?: string | null) => Promise<boolean>;
29
+ loadMoreMembers: () => Promise<void>;
30
+ memberCountLabel: string;
31
+ memberUserIds: Set<string>;
32
+ members: ConversationMember[];
33
+ membersError: string;
34
+ membersSearch: string;
35
+ nextCursor: string | null;
36
+ otherMembers: ConversationMember[];
37
+ renameConversation: (title: string) => Promise<boolean>;
38
+ searchMembers: (search: string) => Promise<void>;
39
+ updateGroupAvatarFile: (file: File) => Promise<boolean>;
40
+ };
41
+ export {};
@@ -0,0 +1,7 @@
1
+ /** Whether the full-conversation SearchPanel is open — the header's search button opens
2
+ * it directly (no separate inline header search box to manage a draft for). */
3
+ export declare function useSearchPanel(): {
4
+ isPanelOpen: boolean;
5
+ openPanel: () => void;
6
+ closePanel: () => void;
7
+ };
@@ -0,0 +1,39 @@
1
+ import { ChatMessage, Conversation } from '../../utils/chat';
2
+ export interface ConversationSettingHandle {
3
+ toggleSettings: () => void;
4
+ openSearch: () => void;
5
+ openRename: () => void;
6
+ openMembers: () => void;
7
+ }
8
+ interface ConversationSettingProps {
9
+ conversationId: string | undefined;
10
+ conversation: Conversation | null;
11
+ currentUserId: string | null;
12
+ onConversationUpdated: (update: Partial<Conversation>) => void;
13
+ onDeleteSuccess: () => void;
14
+ onLeaveSuccess: () => void;
15
+ onStartChat: (userId: string) => void;
16
+ onForwardMessage: (message: ChatMessage) => void;
17
+ onOpenUserProfile: (userId: string) => void;
18
+ onSelectSearchMessage: (messageId: string) => Promise<boolean>;
19
+ /** Scrolls the timeline to a message chosen from the documents/media lists. The panel
20
+ * stays open on purpose - jumping is a navigation aid used while browsing the list, so
21
+ * closing it would throw the user out of the list they were working through. Required
22
+ * rather than optional so the jump buttons cannot silently disappear again if this prop
23
+ * is ever dropped while moving these panels around. */
24
+ onJumpToMessage: (message: ChatMessage) => void;
25
+ /** Notifies the page whenever the settings panel or the search panel open/close — the
26
+ * page needs `isSettingsOpen` specifically (the header's settings-toggle button shows
27
+ * its own active state from it) and the combination of both for its "has-side-panel"
28
+ * layout class. The panels themselves stay owned here. */
29
+ onOpenChange: (state: {
30
+ isSettingsOpen: boolean;
31
+ isSearchOpen: boolean;
32
+ }) => void;
33
+ }
34
+ /** Everything under the "Setting" umbrella for the currently open conversation: the
35
+ * search panel, the settings/info panel, the member-list modal, and the rename dialog.
36
+ * Owns their open/closed state itself — the header (a sibling under `conversation/`)
37
+ * only reaches in via the imperative handle to trigger them. */
38
+ export declare const ConversationSetting: import('react').ForwardRefExoticComponent<ConversationSettingProps & import('react').RefAttributes<ConversationSettingHandle>>;
39
+ export {};
@@ -0,0 +1,11 @@
1
+ interface AddMemberModalProps {
2
+ currentUserId: string | null;
3
+ /** Users already in the conversation (plus self) — hidden from the pickable list. */
4
+ memberUserIds: Set<string>;
5
+ onClose: () => void;
6
+ onConfirm: (userIds: string[]) => Promise<void>;
7
+ }
8
+ /** "Thêm thành viên" — branch/department filters + search + checkbox multi-select grouped
9
+ * alphabetically, each row showing the avatar, employee code, and job title. */
10
+ export declare function AddMemberModal({ currentUserId, memberUserIds, onClose, onConfirm, }: AddMemberModalProps): import("react/jsx-runtime").JSX.Element;
11
+ export {};
@@ -0,0 +1,21 @@
1
+ import { ConversationMember } from '../../../utils/chat';
2
+ interface GroupMembersModalProps {
3
+ members: ConversationMember[] | undefined;
4
+ memberCount: number;
5
+ onClose: () => void;
6
+ /** Opens the account-info modal for a member. */
7
+ onOpenProfile?: (userId: string) => void;
8
+ /** Who is looking — they never get a tick next to their own row. */
9
+ currentUserId?: string | null;
10
+ /** Whether this viewer may remove members at all (group owner, group not verified). */
11
+ canRemove?: boolean;
12
+ isRemoving?: boolean;
13
+ /** Removes the whole tick-selection in one request, so the group gets a single "đã xóa"
14
+ * notice instead of one per person. */
15
+ onDeleteMembers?: (userIds: string[]) => Promise<boolean> | void;
16
+ }
17
+ /** Read-only member list opened from the group header's member count. Reads straight from
18
+ * the conversation's own `members` (kept live in the shared conversationsStore) instead of
19
+ * its own fetch — no separate loading state, and no staleness while it's open. */
20
+ export declare function GroupMembersModal({ members, memberCount, onClose, onOpenProfile, currentUserId, canRemove, isRemoving, onDeleteMembers, }: GroupMembersModalProps): import("react/jsx-runtime").JSX.Element;
21
+ export {};
@@ -0,0 +1,15 @@
1
+ import { Conversation } from '../../../../utils/chat';
2
+ interface UseRenameGroupDialogOptions {
3
+ conversationId: string | undefined;
4
+ onRenamed: (updated: Partial<Conversation>) => void;
5
+ }
6
+ /** Backs the chat-header pencil's "Đổi tên nhóm" dialog: open/saving state plus the
7
+ * rename call itself. */
8
+ export declare function useRenameGroupDialog({ conversationId, onRenamed }: UseRenameGroupDialogOptions): {
9
+ isOpen: boolean;
10
+ open: () => void;
11
+ close: () => void;
12
+ isSaving: boolean;
13
+ submit: (title: string) => Promise<boolean>;
14
+ };
15
+ export {};
@@ -0,0 +1,12 @@
1
+ interface MemberSelectionFooterProps {
2
+ selectedCount: number;
3
+ selectableCount: number;
4
+ isAllSelected: boolean;
5
+ isBusy: boolean;
6
+ onToggleAll: () => void;
7
+ onDelete: () => void;
8
+ }
9
+ /** "Chọn tất cả" + selected count + the destructive action, shared by both member lists so the
10
+ * bulk-remove bar looks and behaves the same wherever it appears. */
11
+ export declare function MemberSelectionFooter({ selectedCount, selectableCount, isAllSelected, isBusy, onToggleAll, onDelete, }: MemberSelectionFooterProps): import("react/jsx-runtime").JSX.Element;
12
+ export {};
@@ -0,0 +1,29 @@
1
+ import { ConversationMember } from '../../../utils/chat';
2
+ interface MembersListModalProps {
3
+ members: ConversationMember[];
4
+ memberCountLabel: string;
5
+ currentUserId: string | null;
6
+ isOwner: boolean;
7
+ isLoadingMembers: boolean;
8
+ membersError: string;
9
+ deletingUserId: string | null;
10
+ nextCursor: string | null;
11
+ isLoadingMoreMembers: boolean;
12
+ onClose: () => void;
13
+ onOpenAddModal: () => void;
14
+ onSearch: (search: string) => Promise<void>;
15
+ onDeleteMember: (userId: string) => void;
16
+ /** Removes the whole tick-selection in one request, so the group gets one "đã xóa" notice
17
+ * instead of one per person. */
18
+ onDeleteMembers: (userIds: string[]) => Promise<boolean> | void;
19
+ onLoadMore: () => void;
20
+ /** Opens the account-info modal for a member. */
21
+ onOpenProfile: (userId: string) => void;
22
+ /** True when this is a verified group and the caller lacks hasUpdateGroupVerified —
23
+ * hides (not just disables) add-member and remove-member, the two actions that
24
+ * permission covers. */
25
+ updateLocked?: boolean;
26
+ }
27
+ /** "Thành viên" — quick-add row, member list with role subtitle + remove, load-more. */
28
+ export declare function MembersListModal({ members, memberCountLabel, currentUserId, isOwner, isLoadingMembers, membersError, deletingUserId, nextCursor, isLoadingMoreMembers, onClose, onOpenAddModal, onSearch, onDeleteMember, onDeleteMembers, onLoadMore, onOpenProfile, updateLocked, }: MembersListModalProps): import("react/jsx-runtime").JSX.Element;
29
+ export {};
@@ -0,0 +1,36 @@
1
+ import { ReactNode } from 'react';
2
+ import { ChatUser } from '../../../utils/chat';
3
+ interface SettingsModalProps {
4
+ title: string;
5
+ children: ReactNode;
6
+ onClose: () => void;
7
+ /** Widens the dialog for dense user lists (add-member / members list). */
8
+ medium?: boolean;
9
+ }
10
+ /** Shared dialog shell (backdrop + header + close button) for every settings modal.
11
+ * Portals to document.body — without this, a modal opened from inside another modal
12
+ * (e.g. the "Đã chọn" review opened from within the wide create-group modal) would
13
+ * stay a DOM descendant of it, and that ancestor's descendant CSS rules (its 2-column
14
+ * grid, width caps, etc.) would leak in and mangle this modal's own layout.
15
+ * The portaled tree is wrapped in its own `.med-chat-root` because `document.body` is
16
+ * outside the module's scoped root — without it, every `.med-chat-root .chat-modal-*`
17
+ * rule (mask, centering, compact user rows) would silently not apply. The `--portal`
18
+ * modifier renders the wrapper as `display: contents`, so it scopes the styles without
19
+ * adding a 100%-height box after `#root` (which would stretch the page and add a Y scroll). */
20
+ export declare function SettingsModal({ title, children, onClose, medium }: SettingsModalProps): import('react').ReactPortal;
21
+ interface UserIdentityProps {
22
+ user: ChatUser;
23
+ subtitle?: string;
24
+ }
25
+ /** Avatar (real photo when available, else initials) + name (+ optional subtitle below,
26
+ * e.g. "Trưởng nhóm" or a job title) shown in every member row. */
27
+ export declare function UserIdentity({ user, subtitle }: UserIdentityProps): import("react/jsx-runtime").JSX.Element;
28
+ interface RenameGroupDialogProps {
29
+ initialTitle: string;
30
+ isSaving: boolean;
31
+ onClose: () => void;
32
+ onRename: (title: string) => Promise<boolean>;
33
+ }
34
+ /** Shared "Đổi tên nhóm" dialog used from both the settings panel and the chat-header pencil. */
35
+ export declare function RenameGroupDialog({ initialTitle, isSaving, onClose, onRename }: RenameGroupDialogProps): import("react/jsx-runtime").JSX.Element;
36
+ export {};
@@ -0,0 +1,18 @@
1
+ interface UseMemberSelectionOptions {
2
+ /** Every member the current view could actually remove — the owner and the current user are
3
+ * already excluded by the caller. */
4
+ selectableUserIds: string[];
5
+ }
6
+ /**
7
+ * Tick-selection shared by the two member lists (the settings panel's list and the read-only
8
+ * one opened from the group header), so "Chọn tất cả" means the same thing in both.
9
+ */
10
+ export declare function useMemberSelection({ selectableUserIds }: UseMemberSelectionOptions): {
11
+ selectedUserIds: string[];
12
+ isAllSelected: boolean;
13
+ isSelected: (userId: string) => boolean;
14
+ toggle: (userId: string) => void;
15
+ toggleAll: () => void;
16
+ clear: () => void;
17
+ };
18
+ export {};
@@ -0,0 +1,13 @@
1
+ import { ConversationMember } from '../../utils/chat';
2
+ interface SearchPanelProps {
3
+ conversationId: string | undefined;
4
+ members: ConversationMember[] | undefined;
5
+ initialQuery?: string;
6
+ onClose: () => void;
7
+ onSelectMessage: (messageId: string) => Promise<boolean>;
8
+ }
9
+ /** Slide-over panel searching a conversation's messages and documents, with "Người gửi"/
10
+ * "Ngày gửi" filters and matched-term highlighting (from the backend's `ts_headline`
11
+ * snippet) in each result row. */
12
+ export declare function SearchPanel({ conversationId, members, initialQuery, onClose, onSelectMessage, }: SearchPanelProps): import("react/jsx-runtime").JSX.Element;
13
+ export {};
@@ -0,0 +1,92 @@
1
+ import { VirtuosoHandle } from 'react-virtuoso';
2
+ import { RefObject } from 'react';
3
+ import { MessageRemovedPayload } from '@medipha/chat-core';
4
+ import { ChatMessage, ChatUser, ConversationMember } from '../../../utils/chat';
5
+ import { PendingAttachment } from '../../../features/composer/pending-attachment';
6
+ interface MessageWindowForDetail {
7
+ messages: ChatMessage[];
8
+ firstItemIndex: number;
9
+ timelineRevision: number;
10
+ focusedMessageId: string | null;
11
+ loadOlder: () => void;
12
+ loadNewer: () => void;
13
+ updateAtBottom: (isAtBottom: boolean) => void;
14
+ isLoadingOlder: boolean;
15
+ isLoadingNewer: boolean;
16
+ hasUnseenMessages: boolean;
17
+ isAtBottom: boolean;
18
+ }
19
+ interface ReplyTargetForDetail {
20
+ replyTarget: ChatMessage | null;
21
+ startReply: (message: ChatMessage) => void;
22
+ clearReply: () => void;
23
+ }
24
+ export interface ConversationDetailHandle {
25
+ /** Closes the per-message "..." menu — used by the parent's scroll handler (scrolling
26
+ * away from a message shouldn't leave its menu floating over new content). */
27
+ closeMessageMenu: () => void;
28
+ }
29
+ interface ConversationDetailProps {
30
+ conversationId: string | undefined;
31
+ timelineRef: RefObject<HTMLDivElement>;
32
+ isLoading: boolean;
33
+ chatError: string;
34
+ isTimelineReady: boolean;
35
+ isFollowingLatest: boolean;
36
+ messageWindow: MessageWindowForDetail;
37
+ /** Rejects late realtime payloads (pin, etc.) for a message removed earlier this session. */
38
+ isRemovedMessageId: (messageId: string) => boolean;
39
+ virtuosoRef: RefObject<VirtuosoHandle>;
40
+ setScrollerRef: (element: unknown) => void;
41
+ handleTimelineScrolling: (scrolling: boolean) => void;
42
+ handleVisibleRangeChanged: (range: {
43
+ startIndex: number;
44
+ endIndex: number;
45
+ }) => void;
46
+ visibleDayLabel: string | null;
47
+ isNearBottomOfList: boolean;
48
+ onCatchUp: () => void;
49
+ members: ConversationMember[] | undefined;
50
+ isGroup: boolean;
51
+ /** Active members + self — only used to size the "seen by" modal's member picker. */
52
+ groupMemberCount: number;
53
+ currentUserId: string | null;
54
+ authUser: ChatUser | null;
55
+ composerError: string;
56
+ pendingAttachments: PendingAttachment[];
57
+ onRemovePendingAttachment: (index: number) => void;
58
+ typingNames: string[];
59
+ canModerateMessages: boolean;
60
+ canManagePins: boolean;
61
+ dayDividerKeys: Set<string>;
62
+ replyTargetState: ReplyTargetForDetail;
63
+ onForward: (message: ChatMessage) => void;
64
+ onJumpToReply: (replyMessageId: string) => void;
65
+ onOpenMentionCard: (userId: string) => void;
66
+ onOpenUserProfile: (userId: string) => void;
67
+ onOpenImageLightbox: (message: ChatMessage, urls: string[], index: number) => void;
68
+ onRetry: (message: ChatMessage) => void;
69
+ /** Failed sends never reached the server (no `id`), so removing one is a local-only drop. */
70
+ onDeleteFailed: (message: ChatMessage) => void;
71
+ /** Read synchronously (not via React state) so an image-load event mid-render doesn't
72
+ * scroll on stale data — see the hook's own note on why this stays a ref. */
73
+ stickToLatestRef: RefObject<boolean>;
74
+ readIsFollowingLatest: () => boolean;
75
+ scrollToLatest: (behavior?: 'auto' | 'smooth') => void;
76
+ /** Backs the pinned-messages bar's "jump to message" action. */
77
+ loadAround: (messageId: string) => Promise<boolean> | boolean;
78
+ /** A message was removed (this user's own confirm-flow, or a realtime event) — the
79
+ * parent applies its own share of the fallout (messageWindow tombstone, reply target,
80
+ * the page-level forward picker, the image lightbox); this view applies the rest
81
+ * (pin, reaction, menu, details) itself. */
82
+ onMessageRemoved: (removal: MessageRemovedPayload) => void;
83
+ }
84
+ /** Everything below the header: the pinned-messages bar, the scrollable message body
85
+ * (Virtuoso list, loading/empty states, day pill, catch-up buttons), the per-row
86
+ * MessageBubble wiring, and the transient strips right above the composer — reply
87
+ * banner, staged attachment previews, typing indicator. Owns every overlay that's
88
+ * exclusively its own concern (pin, reaction picker/modal, message menu, message
89
+ * details, removal) — only genuinely cross-cutting state (message window, reply
90
+ * target, typing...) still comes from `conversation/index.tsx`. */
91
+ export declare const ConversationDetail: import('react').ForwardRefExoticComponent<ConversationDetailProps & import('react').RefAttributes<ConversationDetailHandle>>;
92
+ export {};
@@ -0,0 +1,42 @@
1
+ import { RefObject } from 'react';
2
+ import { PendingAttachment } from '../../../features/composer/pending-attachment';
3
+ import { ChatMessage, ChatUser } from '../../../utils/chat';
4
+ interface PendingAttachmentsForFooter {
5
+ pendingAttachments: PendingAttachment[];
6
+ addFiles: (files: File[], options?: {
7
+ forceDocument?: boolean;
8
+ }) => boolean;
9
+ clear: () => void;
10
+ }
11
+ interface ReplyTargetForFooter {
12
+ replyTarget: ChatMessage | null;
13
+ clearReply: () => void;
14
+ }
15
+ interface ConversationFooterProps {
16
+ conversationId: string | undefined;
17
+ currentUserId: string | null;
18
+ conversationTitle: string;
19
+ mentionMembers: ChatUser[];
20
+ canMentionAll: boolean;
21
+ isGroup: boolean;
22
+ disabled: boolean;
23
+ pendingAttachmentsState: PendingAttachmentsForFooter;
24
+ replyTargetState: ReplyTargetForFooter;
25
+ emitOutgoingMessage: (message: ChatMessage) => void;
26
+ markMessageFailed: (message: ChatMessage, failureStage: 'upload' | 'send') => void;
27
+ mergeOutgoingMessage: (message: ChatMessage) => void;
28
+ stickToLatest: () => void;
29
+ onComposerError: (message: string) => void;
30
+ onAttach: () => void;
31
+ onAttachImages: () => void;
32
+ onTypingStart: () => void;
33
+ onTypingStop: () => void;
34
+ uploadInputRef: RefObject<HTMLInputElement>;
35
+ imageUploadInputRef: RefObject<HTMLInputElement>;
36
+ acceptAttribute: string;
37
+ }
38
+ /** The message composer itself — toolbar + input — plus its two hidden file inputs. Owns
39
+ * turning a submitted draft (text and/or staged attachments) into outgoing messages,
40
+ * including uploading any staged files before they're sent. */
41
+ export declare function ConversationFooter({ conversationId, currentUserId, conversationTitle, mentionMembers, canMentionAll, isGroup, disabled, pendingAttachmentsState, replyTargetState, emitOutgoingMessage, markMessageFailed, mergeOutgoingMessage, stickToLatest, onComposerError, onAttach, onAttachImages, onTypingStart, onTypingStop, uploadInputRef, imageUploadInputRef, acceptAttribute, }: ConversationFooterProps): import("react/jsx-runtime").JSX.Element;
42
+ export {};
@@ -0,0 +1,27 @@
1
+ import { ConversationMember } from '../../../utils/chat';
2
+ interface ConversationHeaderProps {
3
+ title: string;
4
+ avatarUrl?: string | null;
5
+ isGroup: boolean;
6
+ isVerified?: boolean;
7
+ groupMemberCount: number;
8
+ members?: ConversationMember[];
9
+ currentUserId?: string | null;
10
+ isOtherMemberOnline: boolean | undefined;
11
+ /** Opens the full SearchPanel directly. */
12
+ onOpenSearch: () => void;
13
+ onOpenMessageList: () => void;
14
+ isSettingsOpen: boolean;
15
+ onToggleSettings: () => void;
16
+ /** Group only: opens the member-list modal from the header member count. */
17
+ onOpenMembers?: () => void;
18
+ /** Clicking the avatar opens the partner's account info (direct chats) or a fullscreen
19
+ * preview of the group photo (groups). */
20
+ onOpenAvatarProfile?: () => void;
21
+ /** Group owner only: hovering the title reveals a rename button. */
22
+ canEditTitle?: boolean;
23
+ onEditTitle?: () => void;
24
+ }
25
+ /** Conversation title/status row plus the collapsible in-header search and toggle buttons. */
26
+ export declare function ConversationHeader({ title, avatarUrl, isGroup, isVerified, groupMemberCount, members, currentUserId, isOtherMemberOnline, onOpenSearch, onOpenMessageList, isSettingsOpen, onToggleSettings, onOpenMembers, canEditTitle, onEditTitle, onOpenAvatarProfile, }: ConversationHeaderProps): import("react/jsx-runtime").JSX.Element;
27
+ export {};
@@ -0,0 +1,14 @@
1
+ import { ChatUser } from '../../../utils/chat';
2
+ /**
3
+ * Owns the current user's membership info for a GROUP conversation — the active member
4
+ * list used for @mention autocomplete, and the current user's own role/status, used
5
+ * across permission checks (pin management, moderation, title editing). A no-op for
6
+ * DIRECT conversations, which have no membership roles to resolve.
7
+ */
8
+ export declare function useCurrentMembership(conversationId: string | undefined, conversationType: string, currentUserId: string | null, onError: (message: string) => void): {
9
+ mentionMembers: ChatUser[];
10
+ currentMemberRole: string;
11
+ currentMemberStatus: string;
12
+ reload: () => Promise<void>;
13
+ reset: () => void;
14
+ };