@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.
- package/README.md +103 -0
- package/dist/api/client.d.ts +5 -0
- package/dist/features/composer/attachment-file-helpers.d.ts +22 -0
- package/dist/features/composer/attachments/attachment-buttons.d.ts +8 -0
- package/dist/features/composer/conversation-composer.d.ts +20 -0
- package/dist/features/composer/editor/font-size-extension.d.ts +18 -0
- package/dist/features/composer/editor/mention-suggestion-list.d.ts +15 -0
- package/dist/features/composer/editor/paste-text-style.d.ts +5 -0
- package/dist/features/composer/emoji/emoji-picker-button.d.ts +9 -0
- package/dist/features/composer/emoji/emoji-shortcode-extension.d.ts +9 -0
- package/dist/features/composer/emoji/emoji-shortcodes.d.ts +2 -0
- package/dist/features/composer/format-bar/format-bar.d.ts +14 -0
- package/dist/features/composer/more-menu/more-menu.d.ts +4 -0
- package/dist/features/composer/pending-attachment.d.ts +10 -0
- package/dist/features/composer/reply/composer-reply-banner.d.ts +11 -0
- package/dist/features/composer/reply/use-reply-target.d.ts +12 -0
- package/dist/features/composer/stickers.d.ts +6 -0
- package/dist/features/composer/toolbar/composer-toolbar.d.ts +19 -0
- package/dist/features/composer/typing/use-typing-notifier.d.ts +11 -0
- package/dist/features/composer/use-attachment-upload.d.ts +4 -0
- package/dist/features/composer/use-pending-attachments.d.ts +16 -0
- package/dist/features/create-conversation/create-conversation-panel.d.ts +9 -0
- package/dist/features/create-conversation/tabs/create-group-tab.d.ts +13 -0
- package/dist/features/create-conversation/tabs/direct-chat-tab.d.ts +12 -0
- package/dist/features/create-conversation/tabs/selected-members-modal.d.ts +11 -0
- package/dist/features/files/file-attachment.d.ts +24 -0
- package/dist/features/forward/forward-dialog.d.ts +11 -0
- package/dist/features/forward/use-forward-message.d.ts +21 -0
- package/dist/features/media-preview/media-preview-modal.d.ts +23 -0
- package/dist/features/media-preview/use-image-lightbox.d.ts +16 -0
- package/dist/features/message-list/bubble/message-actions/message-actions-menu.d.ts +27 -0
- package/dist/features/message-list/bubble/message-bubble.d.ts +53 -0
- package/dist/features/message-list/bubble/message-content-body.d.ts +12 -0
- package/dist/features/message-list/bubble/message-reactions.d.ts +18 -0
- package/dist/features/message-list/bubble/rich-message-content.d.ts +8 -0
- package/dist/features/message-list/overlays/message-details/message-details-modal.d.ts +16 -0
- package/dist/features/message-list/overlays/message-details/use-message-details-modal.d.ts +14 -0
- package/dist/features/message-list/overlays/message-menu/use-message-menu.d.ts +21 -0
- package/dist/features/message-list/overlays/reaction/reaction-modal.d.ts +22 -0
- package/dist/features/message-list/overlays/reaction/use-reaction-modal.d.ts +23 -0
- package/dist/features/message-list/overlays/reaction/use-reaction-picker.d.ts +8 -0
- package/dist/features/message-list/overlays/system-members/system-members-modal.d.ts +22 -0
- package/dist/features/message-list/overlays/system-members/use-system-members-modal.d.ts +18 -0
- package/dist/features/message-list/use-message-removal.d.ts +12 -0
- package/dist/features/message-list/use-message-timeline-scroll.d.ts +25 -0
- package/dist/features/message-window/use-message-window.d.ts +46 -0
- package/dist/features/pinned-messages/pinned-messages-bar.d.ts +18 -0
- package/dist/features/pinned-messages/use-pinned-messages.d.ts +21 -0
- package/dist/features/typing/use-typing-indicator.d.ts +12 -0
- package/dist/index.cjs +278 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +66322 -0
- package/dist/module/chat-module.d.ts +3 -0
- package/dist/module/chat-module.types.d.ts +11 -0
- package/dist/module/chat-routes.d.ts +4 -0
- package/dist/module/chat-runtime-context.d.ts +5 -0
- package/dist/pages/Setting/archive/archive-attachment-preview.d.ts +13 -0
- package/dist/pages/Setting/archive/archive-file-list.d.ts +15 -0
- package/dist/pages/Setting/archive/archive-helpers.d.ts +37 -0
- package/dist/pages/Setting/archive/archive-media-grid.d.ts +14 -0
- package/dist/pages/Setting/archive/archive-view.d.ts +19 -0
- package/dist/pages/Setting/archive/hooks/use-conversation-archive.d.ts +22 -0
- package/dist/pages/Setting/conversation-settings-panel.d.ts +20 -0
- package/dist/pages/Setting/hooks/use-conversation-settings.d.ts +41 -0
- package/dist/pages/Setting/hooks/use-search-panel.d.ts +7 -0
- package/dist/pages/Setting/index.d.ts +39 -0
- package/dist/pages/Setting/members/add-member-modal.d.ts +11 -0
- package/dist/pages/Setting/members/group-members-modal.d.ts +21 -0
- package/dist/pages/Setting/members/hooks/use-rename-group-dialog.d.ts +15 -0
- package/dist/pages/Setting/members/member-selection-footer.d.ts +12 -0
- package/dist/pages/Setting/members/members-list-modal.d.ts +29 -0
- package/dist/pages/Setting/members/settings-modal.d.ts +36 -0
- package/dist/pages/Setting/members/use-member-selection.d.ts +18 -0
- package/dist/pages/Setting/search-panel.d.ts +13 -0
- package/dist/pages/conversation/conversation-detail/conversation-detail.d.ts +92 -0
- package/dist/pages/conversation/conversation-footer/conversation-footer.d.ts +42 -0
- package/dist/pages/conversation/conversation-header/conversation-header.d.ts +27 -0
- package/dist/pages/conversation/hooks/use-current-membership.d.ts +14 -0
- package/dist/pages/conversation/index.d.ts +28 -0
- package/dist/pages/conversations/empty-state/conversation-empty-state.d.ts +3 -0
- package/dist/pages/conversations/index.d.ts +4 -0
- package/dist/pages/sidebar/hooks/use-conversations.d.ts +17 -0
- package/dist/pages/sidebar/index.d.ts +4 -0
- package/dist/pages/sidebar/sidbar-conversation-list/sidbar-conversation-list.d.ts +13 -0
- package/dist/pages/sidebar/sidebar-filter/sidebar-filter.d.ts +13 -0
- package/dist/pages/sidebar/sidebar-header-infor/sidebar-header-infor.d.ts +4 -0
- package/dist/shared/components/banner-carousel.d.ts +9 -0
- package/dist/shared/components/conversation-avatar.d.ts +19 -0
- package/dist/shared/components/date-range-filter-pill.d.ts +17 -0
- package/dist/shared/components/download-action.d.ts +11 -0
- package/dist/shared/components/filter-pill.d.ts +15 -0
- package/dist/shared/components/image-not-available.d.ts +12 -0
- package/dist/shared/components/image-with-fallback.d.ts +13 -0
- package/dist/shared/components/load-more-sentinel.d.ts +15 -0
- package/dist/shared/components/quote-icon.d.ts +5 -0
- package/dist/shared/components/search-empty-state.d.ts +8 -0
- package/dist/shared/components/shared-icon.d.ts +5 -0
- package/dist/shared/components/user-avatar.d.ts +12 -0
- package/dist/shared/components/user-profile-modal.d.ts +13 -0
- package/dist/shared/components/verified-badge.d.ts +5 -0
- package/dist/shared/helper/dayjs-date-picker.d.ts +14 -0
- package/dist/shared/hooks/use-directory-filter.d.ts +13 -0
- package/dist/shared/hooks/use-user-profile-modal.d.ts +9 -0
- package/dist/socket/chatSocket.d.ts +6 -0
- package/dist/store/authStore.d.ts +34 -0
- package/dist/store/connectionStore.d.ts +8 -0
- package/dist/store/conversationsStore.d.ts +38 -0
- package/dist/store/forwardStore.d.ts +14 -0
- package/dist/store/pinnedMessagesStore.d.ts +16 -0
- package/dist/store/presenceStore.d.ts +11 -0
- package/dist/store/verifiedPermissionsStore.d.ts +20 -0
- package/dist/styles.css +2 -0
- package/dist/utils/chat-display.d.ts +44 -0
- package/dist/utils/chat-member-resolvers.d.ts +9 -0
- package/dist/utils/chat-toast.d.ts +18 -0
- package/dist/utils/chat.d.ts +282 -0
- package/dist/utils/token.d.ts +0 -0
- package/dist/utils/work-status.d.ts +15 -0
- package/package.json +96 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ChatUser, Conversation } from '../../utils/chat';
|
|
2
|
+
export interface ConversationGroupHandle {
|
|
3
|
+
/** Jumps the message timeline to the given message — used by the page-level SearchPanel,
|
|
4
|
+
* which lives outside this group but needs to drive its message window. */
|
|
5
|
+
loadAround: (messageId: string) => Promise<boolean> | boolean;
|
|
6
|
+
}
|
|
7
|
+
interface ConversationGroupProps {
|
|
8
|
+
conversationId: string | undefined;
|
|
9
|
+
conversation: Conversation | null;
|
|
10
|
+
isLoadingConversation: boolean;
|
|
11
|
+
currentUserId: string | null;
|
|
12
|
+
authUser: ChatUser | null;
|
|
13
|
+
accessToken: string | undefined;
|
|
14
|
+
isSettingsOpen: boolean;
|
|
15
|
+
onToggleSettings: () => void;
|
|
16
|
+
onOpenSearch: () => void;
|
|
17
|
+
onEditTitle: () => void;
|
|
18
|
+
onOpenMembers: () => void;
|
|
19
|
+
onOpenUserProfile: (userId: string) => void;
|
|
20
|
+
onNavigateBack: () => void;
|
|
21
|
+
}
|
|
22
|
+
/** Header + detail + footer for the currently open conversation. Owns only what's
|
|
23
|
+
* genuinely shared across those three (message window, membership, reply target,
|
|
24
|
+
* pending attachments, typing, the shared image lightbox) — the nearest common ancestor
|
|
25
|
+
* for exactly the state they need. Everything exclusive to one of them (pin, reaction,
|
|
26
|
+
* message menu/details — all detail-only) lives inside that component instead. */
|
|
27
|
+
export declare const ConversationGroup: import('react').ForwardRefExoticComponent<ConversationGroupProps & import('react').RefAttributes<ConversationGroupHandle>>;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Full-viewport chat workspace: sidebar plus the active conversation. Owns the
|
|
2
|
+
* conversation object itself (shared by both the conversation group and the Setting
|
|
3
|
+
* group) and the handful of dialogs/state that outlive either (forward, user profile). */
|
|
4
|
+
export declare function ConversationScreen(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Conversation } from '../../../utils/chat';
|
|
2
|
+
interface UseConversationsResult {
|
|
3
|
+
conversations: Conversation[];
|
|
4
|
+
isLoading: boolean;
|
|
5
|
+
error: string;
|
|
6
|
+
togglePin: (conversationId: string, pinned: boolean) => Promise<void>;
|
|
7
|
+
reload: () => Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Loads the current user's conversations and keeps the list live via socket events.
|
|
11
|
+
* Backed by the shared `conversationsStore` (not local state) — the page-level "currently
|
|
12
|
+
* open conversation" reads and gets patched through the same store, so the two never
|
|
13
|
+
* drift out of sync on the same realtime events.
|
|
14
|
+
* `activeConversationId` is accepted for call-site compatibility (currently unused here).
|
|
15
|
+
*/
|
|
16
|
+
export declare function useConversations(activeConversationId?: string): UseConversationsResult;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Left rail listing conversations with search, unread filter, and creation entry. Owns the
|
|
2
|
+
* search/filter state shared by its filter row and list, and the conversation feed itself —
|
|
3
|
+
* its 3 sub-parts (header-infor, filter, conversation-list) are otherwise independent. */
|
|
4
|
+
export declare function ConversationSidebar(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Conversation } from '../../../utils/chat';
|
|
2
|
+
interface SidbarConversationListProps {
|
|
3
|
+
conversations: Conversation[];
|
|
4
|
+
isLoading: boolean;
|
|
5
|
+
error: string;
|
|
6
|
+
currentUserId: string | null;
|
|
7
|
+
activeConversationId: string | undefined;
|
|
8
|
+
buildConversationPath: (targetId: string) => string;
|
|
9
|
+
onTogglePin: (conversationId: string, nextPinned: boolean) => void;
|
|
10
|
+
}
|
|
11
|
+
/** The scrollable list of conversation rows — already-filtered input in, Link rows out. */
|
|
12
|
+
export declare function SidbarConversationList({ conversations, isLoading, error, currentUserId, activeConversationId, buildConversationPath, onTogglePin, }: SidbarConversationListProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type SidebarConversationFilter = 'all' | 'unread' | 'verified';
|
|
2
|
+
interface SidebarFilterProps {
|
|
3
|
+
searchTerm: string;
|
|
4
|
+
onSearchTermChange: (value: string) => void;
|
|
5
|
+
activeFilter: SidebarConversationFilter;
|
|
6
|
+
onActiveFilterChange: (filter: SidebarConversationFilter) => void;
|
|
7
|
+
onOpenCreate: () => void;
|
|
8
|
+
}
|
|
9
|
+
/** Sidebar search input, "Tạo cuộc trò chuyện" entry point, and the Tất cả/Chưa đọc/Tích
|
|
10
|
+
* xanh filter row. Controlled — the actual conversation list it filters lives in the
|
|
11
|
+
* sibling sidbar-conversation-list component, driven from the same state one level up. */
|
|
12
|
+
export declare function SidebarFilter({ searchTerm, onSearchTermChange, activeFilter, onActiveFilterChange, onOpenCreate, }: SidebarFilterProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Current-user identity block at the top of the sidebar: avatar, name, work info, and
|
|
2
|
+
* the entry point into the user's own profile modal. Fully self-contained — reads the
|
|
3
|
+
* authenticated user straight from the auth store, no props needed. */
|
|
4
|
+
export declare function SidebarHeaderInfor(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface BannerCarouselProps {
|
|
2
|
+
images: string[];
|
|
3
|
+
className?: string;
|
|
4
|
+
}
|
|
5
|
+
/** Zalo-style auto-sliding banner carousel — fixed interval, prev/next arrows, and dot
|
|
6
|
+
* indicators (click a dot to jump directly) — used on the "no conversation selected"
|
|
7
|
+
* default screen. */
|
|
8
|
+
export declare function BannerCarousel({ images, className }: BannerCarouselProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ConversationMember } from '../../utils/chat';
|
|
2
|
+
interface ConversationAvatarProps {
|
|
3
|
+
/** Resolved avatar photo URL (absolute); when absent the initials fallback is shown. */
|
|
4
|
+
avatarUrl?: string | null;
|
|
5
|
+
/** Name used for the initials fallback when there is no photo. */
|
|
6
|
+
name: string;
|
|
7
|
+
isGroup: boolean;
|
|
8
|
+
/** Group members used to build the composite avatar; empty for direct chats. */
|
|
9
|
+
members?: ConversationMember[];
|
|
10
|
+
/** Total member count for the group composite; falls back to members.length. */
|
|
11
|
+
memberCount?: number;
|
|
12
|
+
currentUserId?: string | null;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Conversation avatar: photo (group or direct) → direct-chat partner photo → initials.
|
|
16
|
+
* Groups without their own photo render a 2×2 composite of 3 member avatars (excluding
|
|
17
|
+
* self) plus a circle with the total member count, once there are 4+ members. */
|
|
18
|
+
export declare function ConversationAvatar({ avatarUrl, name, isGroup, members, memberCount, currentUserId, className, }: ConversationAvatarProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Dayjs } from 'dayjs';
|
|
2
|
+
export interface DateFilterValue {
|
|
3
|
+
from: Dayjs | null;
|
|
4
|
+
to: Dayjs | null;
|
|
5
|
+
}
|
|
6
|
+
export declare const DEFAULT_DATE_FILTER: DateFilterValue;
|
|
7
|
+
/** Matches when `dateInput` falls within [from, to] — either bound may be unset. */
|
|
8
|
+
export declare function matchesDateFilter(filter: DateFilterValue, dateInput: string): boolean;
|
|
9
|
+
interface DateRangeFilterPillProps {
|
|
10
|
+
value: DateFilterValue;
|
|
11
|
+
onChange: (value: DateFilterValue) => void;
|
|
12
|
+
}
|
|
13
|
+
/** "Ngày gửi ⌄" filter pill: two independent single-month date pickers ("Từ ngày" /
|
|
14
|
+
* "Đến ngày", neither ever a future date) — not antd's combined two-month RangePicker.
|
|
15
|
+
* Confirmed with Hủy/Xác nhận. */
|
|
16
|
+
export declare function DateRangeFilterPill({ value, onChange }: DateRangeFilterPillProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { MouseEvent } from 'react';
|
|
2
|
+
interface DownloadActionProps {
|
|
3
|
+
url: string;
|
|
4
|
+
name?: string | null;
|
|
5
|
+
className?: string;
|
|
6
|
+
onClick?: (event: MouseEvent<HTMLAnchorElement>) => void;
|
|
7
|
+
}
|
|
8
|
+
/** Shared "Tải xuống" action — same download button/behavior everywhere a file or image
|
|
9
|
+
* can be downloaded (chat bubble, kho lưu trữ, settings media preview). */
|
|
10
|
+
export declare function DownloadAction({ url, name, className, onClick, }: DownloadActionProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface FilterPillOption<T extends string> {
|
|
2
|
+
value: T;
|
|
3
|
+
label: string;
|
|
4
|
+
}
|
|
5
|
+
export interface FilterPillProps<T extends string> {
|
|
6
|
+
label: string;
|
|
7
|
+
value: T;
|
|
8
|
+
options: Array<FilterPillOption<T>>;
|
|
9
|
+
onChange: (value: T) => void;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** Small dropdown pill ("Loại ⌄", "Người gửi ⌄", "Ngày gửi ⌄", ...) — shows `label` while
|
|
13
|
+
* the first (default) option is selected, otherwise turns into an active chip showing the
|
|
14
|
+
* picked option with a clear ("x") button in place of the chevron. */
|
|
15
|
+
export declare function FilterPill<T extends string>({ label, value, options, onChange, disabled, }: FilterPillProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface ImageNotAvailableProps {
|
|
2
|
+
className?: string;
|
|
3
|
+
/** Icon only, no label — for boxes too small to fit "Không tìm thấy ảnh" (e.g. a 64px
|
|
4
|
+
* thumbnail rail). */
|
|
5
|
+
compact?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/** Placeholder shown in place of an image that failed to load (e.g. a 404'd attachment) —
|
|
8
|
+
* icon + "Không tìm thấy ảnh" instead of the browser's bare broken-image glyph. Sized to
|
|
9
|
+
* fill whatever box it's given (see .image-not-available), so it drops into the same
|
|
10
|
+
* fixed-size frame the real `<img>` would have used. */
|
|
11
|
+
export declare function ImageNotAvailable({ className, compact }: ImageNotAvailableProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ImgHTMLAttributes } from 'react';
|
|
2
|
+
interface ImageWithFallbackProps extends ImgHTMLAttributes<HTMLImageElement> {
|
|
3
|
+
/** Applied to the ImageNotAvailable placeholder instead of the <img> once it fails —
|
|
4
|
+
* pass the same sizing class the <img> would have gotten so the placeholder fills the
|
|
5
|
+
* same box. */
|
|
6
|
+
fallbackClassName?: string;
|
|
7
|
+
/** Forwarded to ImageNotAvailable — icon only, no label, for boxes too small to fit it. */
|
|
8
|
+
fallbackCompact?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** Drop-in `<img>` replacement that swaps to the ImageNotAvailable placeholder on error
|
|
11
|
+
* (a 404'd attachment, most often) instead of the browser's bare broken-image glyph. */
|
|
12
|
+
export declare function ImageWithFallback({ fallbackClassName, fallbackCompact, onError, ...imgProps }: ImageWithFallbackProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface LoadMoreSentinelProps {
|
|
2
|
+
/** Whether a next page exists to load. */
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
/** True while the next page is being fetched (shows "Đang tải..." and gates refires). */
|
|
5
|
+
isLoading: boolean;
|
|
6
|
+
onLoadMore: () => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Invisible marker at the bottom of a paginated list that triggers `onLoadMore` as soon
|
|
10
|
+
* as it scrolls into view. Because it uses IntersectionObserver (rooted at the scrollable
|
|
11
|
+
* list), it also fires immediately when a fresh page doesn't fill the list — the case a
|
|
12
|
+
* plain onScroll handler misses, since there is no scrollbar to produce scroll events.
|
|
13
|
+
*/
|
|
14
|
+
export declare function LoadMoreSentinel({ enabled, isLoading, onLoadMore }: LoadMoreSentinelProps): import("react/jsx-runtime").JSX.Element | null;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Custom "reply" icon (assets/icons/quote.svg), replacing antd's RollbackOutlined
|
|
2
|
+
* everywhere the reply action appears. Colored like an antd icon (currentColor) and sized
|
|
3
|
+
* relative to it (0.8em — the source artwork reads visually heavier/bigger than antd's
|
|
4
|
+
* icons at a literal 1em) so it drops into the same buttons without CSS changes. */
|
|
5
|
+
export declare function QuoteIcon(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface SearchEmptyStateProps {
|
|
2
|
+
message: string;
|
|
3
|
+
className?: string;
|
|
4
|
+
}
|
|
5
|
+
/** Illustration + message shown whenever a search or filter yields zero results — shared
|
|
6
|
+
* across the message search panel, conversation list, member/user pickers, and archive. */
|
|
7
|
+
export declare function SearchEmptyState({ message, className }: SearchEmptyStateProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Custom "share/forward" icon (assets/icons/sheared.svg), replacing antd's ShareAltOutlined
|
|
2
|
+
* everywhere the forward/share action appears. Colored like an antd icon (currentColor) and
|
|
3
|
+
* sized relative to it (0.8em — the source artwork reads visually heavier/bigger than
|
|
4
|
+
* antd's icons at a literal 1em) so it drops into the same buttons without CSS changes. */
|
|
5
|
+
export declare function SharedIcon(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface UserAvatarProps {
|
|
2
|
+
/** Resolved avatar photo URL (absolute); when absent the initials fallback is shown. */
|
|
3
|
+
avatarUrl?: string | null;
|
|
4
|
+
/** Name used for the initials fallback when there is no photo. */
|
|
5
|
+
name: string;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Circular avatar: shows the user's photo when available, otherwise their initials —
|
|
9
|
+
* also the fallback when the photo URL 404s (e.g. a deleted file) instead of the
|
|
10
|
+
* browser's broken-image icon. */
|
|
11
|
+
export declare function UserAvatar({ avatarUrl, name, className }: UserAvatarProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface UserProfileModalProps {
|
|
2
|
+
userId: string;
|
|
3
|
+
fallbackName?: string;
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
/** Opens (or reuses) a direct conversation with this user. */
|
|
6
|
+
onStartChat?: (userId: string) => void;
|
|
7
|
+
}
|
|
8
|
+
/** Zalo-style "Thông tin tài khoản" dialog, opened from a user avatar click: avatar on
|
|
9
|
+
* top, then contact details (họ tên, ngày sinh, email, số điện thoại) and a second
|
|
10
|
+
* group with the directory info (chi nhánh, phòng ban, chức vụ). Portals to body with
|
|
11
|
+
* the layered mask so it stacks above the member-list modal it may be opened from. */
|
|
12
|
+
export declare function UserProfileModal({ userId, fallbackName, onClose, onStartChat }: UserProfileModalProps): import('react').ReactPortal;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Themed "verified" check (--chat-primary) with a scalloped (sawtooth) badge outline, shown next to a
|
|
2
|
+
* verified group's name or in the sidebar row's top-right corner. */
|
|
3
|
+
export declare function VerifiedBadge({ className }: {
|
|
4
|
+
className?: string;
|
|
5
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Dayjs } from 'dayjs';
|
|
2
|
+
declare const DayjsDatePicker: import('antd/es/date-picker/generatePicker/interface').PickerComponentClass<import('antd/es/date-picker/generatePicker').PickerProps<Dayjs> & {
|
|
3
|
+
status?: "" | "warning" | "error" | undefined;
|
|
4
|
+
dropdownClassName?: string | undefined;
|
|
5
|
+
popupClassName?: string | undefined;
|
|
6
|
+
}, unknown> & {
|
|
7
|
+
WeekPicker: import('antd/es/date-picker/generatePicker/interface').PickerComponentClass<Omit<import('antd/es/date-picker/generatePicker').PickerDateProps<Dayjs>, "picker">, unknown>;
|
|
8
|
+
MonthPicker: import('antd/es/date-picker/generatePicker/interface').PickerComponentClass<Omit<import('antd/es/date-picker/generatePicker').PickerDateProps<Dayjs>, "picker">, unknown>;
|
|
9
|
+
YearPicker: import('antd/es/date-picker/generatePicker/interface').PickerComponentClass<Omit<import('antd/es/date-picker/generatePicker').PickerDateProps<Dayjs>, "picker">, unknown>;
|
|
10
|
+
RangePicker: import('antd/es/date-picker/generatePicker/interface').PickerComponentClass<import('antd/es/date-picker/generatePicker').RangePickerProps<Dayjs>, unknown>;
|
|
11
|
+
TimePicker: import('antd/es/date-picker/generatePicker/interface').PickerComponentClass<Omit<import('antd/es/date-picker/generatePicker').PickerTimeProps<Dayjs>, "picker">, unknown>;
|
|
12
|
+
QuarterPicker: import('antd/es/date-picker/generatePicker/interface').PickerComponentClass<Omit<import('antd/es/date-picker/generatePicker').PickerTimeProps<Dayjs>, "picker">, unknown>;
|
|
13
|
+
};
|
|
14
|
+
export default DayjsDatePicker;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FilterPillOption } from '../components/filter-pill';
|
|
2
|
+
export interface DirectoryFilter {
|
|
3
|
+
branchId: string;
|
|
4
|
+
departmentId: string;
|
|
5
|
+
branchOptions: Array<FilterPillOption<string>>;
|
|
6
|
+
departmentOptions: Array<FilterPillOption<string>>;
|
|
7
|
+
handleBranchChange: (branchId: string) => void;
|
|
8
|
+
setDepartmentId: (departmentId: string) => void;
|
|
9
|
+
}
|
|
10
|
+
/** Branch/department pickers shared by the user-directory surfaces (create conversation,
|
|
11
|
+
* add members): branch options come from the org-tree's branch-level departments (via
|
|
12
|
+
* the zero-UUID branch), and department options follow the selected branch. */
|
|
13
|
+
export declare function useDirectoryFilter(): DirectoryFilter;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks which user's profile modal is open. A shared trigger point — opened from
|
|
3
|
+
* mention clicks, avatar clicks, member/reaction list rows, and direct-chat entry.
|
|
4
|
+
*/
|
|
5
|
+
export declare function useUserProfileModal(): {
|
|
6
|
+
profileUserId: string | null;
|
|
7
|
+
openProfile: (userId: string | null) => void;
|
|
8
|
+
closeProfile: () => void;
|
|
9
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated Import realtime functions from `@medipha/chat-core` in new code.
|
|
3
|
+
* This compatibility re-export keeps the web UI's existing imports unchanged.
|
|
4
|
+
*/
|
|
5
|
+
export { configureSocket, connectSocket, subscribeSocketHandlers, getSocket, emitCreateConversation, subscribeConversation, unsubscribeConversation, emitMessageSend, emitMessageReactionApply, emitMessageReactionRemove, emitMessagePin, emitMessageForward, emitConversationRead, emitTypingStart, emitTypingStop, disconnectSocket, } from '@medipha/chat-core';
|
|
6
|
+
export type { GetMessageReactionsQuery, MessageReactionUpdatedEvent, ReactionSummaryPayload, ReactionType, ReactionUserItem, ReactionUsersPage, SocketEmitResult, SocketHandlers, } from '@medipha/chat-core';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Minimal authenticated user shape; extra ERP-provided fields are preserved. */
|
|
2
|
+
export interface AuthUser {
|
|
3
|
+
userId?: string;
|
|
4
|
+
[key: string]: unknown;
|
|
5
|
+
}
|
|
6
|
+
interface AuthState {
|
|
7
|
+
user: AuthUser | null;
|
|
8
|
+
userId: string | null;
|
|
9
|
+
hasHydrated: boolean;
|
|
10
|
+
setUser: (user: AuthUser | null) => void;
|
|
11
|
+
setUserId: (userId: string | null) => void;
|
|
12
|
+
setHasHydrated: (hasHydrated: boolean) => void;
|
|
13
|
+
logout: () => void;
|
|
14
|
+
}
|
|
15
|
+
export declare const useAuthStore: import('zustand').UseBoundStore<Omit<import('zustand').StoreApi<AuthState>, "setState" | "persist"> & {
|
|
16
|
+
setState(partial: AuthState | Partial<AuthState> | ((state: AuthState) => AuthState | Partial<AuthState>), replace?: false | undefined): unknown;
|
|
17
|
+
setState(state: AuthState | ((state: AuthState) => AuthState), replace: true): unknown;
|
|
18
|
+
persist: {
|
|
19
|
+
setOptions: (options: Partial<import('zustand/middleware').PersistOptions<AuthState, {
|
|
20
|
+
user: AuthUser | null;
|
|
21
|
+
userId: string | null;
|
|
22
|
+
}, unknown>>) => void;
|
|
23
|
+
clearStorage: () => void;
|
|
24
|
+
rehydrate: () => Promise<void> | void;
|
|
25
|
+
hasHydrated: () => boolean;
|
|
26
|
+
onHydrate: (fn: (state: AuthState) => void) => () => void;
|
|
27
|
+
onFinishHydration: (fn: (state: AuthState) => void) => () => void;
|
|
28
|
+
getOptions: () => Partial<import('zustand/middleware').PersistOptions<AuthState, {
|
|
29
|
+
user: AuthUser | null;
|
|
30
|
+
userId: string | null;
|
|
31
|
+
}, unknown>>;
|
|
32
|
+
};
|
|
33
|
+
}>;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface ConnectionState {
|
|
2
|
+
/** Whether the realtime socket is currently connected. Starts true (optimistic) so the
|
|
3
|
+
* banner doesn't flash before the socket has had a chance to connect. */
|
|
4
|
+
isConnected: boolean;
|
|
5
|
+
setConnected: (isConnected: boolean) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare const useConnectionStore: import('zustand').UseBoundStore<import('zustand').StoreApi<ConnectionState>>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Conversation } from '../utils/chat';
|
|
2
|
+
interface ConversationsState {
|
|
3
|
+
conversations: Conversation[];
|
|
4
|
+
isLoading: boolean;
|
|
5
|
+
error: string;
|
|
6
|
+
setConversations: (conversations: Conversation[]) => void;
|
|
7
|
+
setLoading: (isLoading: boolean) => void;
|
|
8
|
+
setError: (error: string) => void;
|
|
9
|
+
/** Adds (or repositions) a conversation that just appeared for this user — a brand-new
|
|
10
|
+
* chat, or one they were just added back into. Repositions by pin/recency, same as any
|
|
11
|
+
* other realtime list event. */
|
|
12
|
+
upsertConversation: (incoming: unknown, currentUserId: string | null) => void;
|
|
13
|
+
/** Merges a partial update into one entry in place (no repositioning) — used for
|
|
14
|
+
* page-driven edits (rename, avatar change) that only carry the changed fields. */
|
|
15
|
+
patchConversation: (conversationId: string, update: Partial<Conversation>) => void;
|
|
16
|
+
/** Replaces one entry with a freshly REST-fetched full detail (richer member roster than
|
|
17
|
+
* the list's own summary shape) — merged in place if already present (the page opening
|
|
18
|
+
* a conversation shouldn't bump its position in the list), appended if this is the
|
|
19
|
+
* first the store has heard of it (e.g. a deep link opened before the list loaded). */
|
|
20
|
+
setFullConversation: (conversation: Conversation) => void;
|
|
21
|
+
removeConversation: (conversationId: string) => void;
|
|
22
|
+
applyIncomingMessage: (incoming: unknown, currentUserId: string | null) => void;
|
|
23
|
+
applyReadMarker: (payload: unknown) => void;
|
|
24
|
+
applyUnreadCount: (payload: unknown) => void;
|
|
25
|
+
applyMessageRemoved: (payload: unknown) => void;
|
|
26
|
+
applyConversationUpdated: (payload: unknown) => void;
|
|
27
|
+
applyPinnedAt: (conversationId: string, userId: string, pinnedAt: string | null) => void;
|
|
28
|
+
applyConversationPinned: (payload: unknown) => void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Shared conversation-summary list — the single source sidebar rows AND the page-level
|
|
32
|
+
* "currently open conversation" both read, instead of each independently patching its own
|
|
33
|
+
* copy on the same realtime events (title/avatar/verified changes, read markers, unread
|
|
34
|
+
* counts...). Pin ordering and per-conversation patch rules are ported unchanged from the
|
|
35
|
+
* sidebar's own hook, which now just wraps this store.
|
|
36
|
+
*/
|
|
37
|
+
export declare const useConversationsStore: import('zustand').UseBoundStore<import('zustand').StoreApi<ConversationsState>>;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ChatMessage } from '../utils/chat';
|
|
2
|
+
interface ForwardState {
|
|
3
|
+
/** The message currently staged to forward — drives whether ForwardDialog is open. */
|
|
4
|
+
forwardSource: ChatMessage | null;
|
|
5
|
+
openForward: (message: ChatMessage) => void;
|
|
6
|
+
closeForward: () => void;
|
|
7
|
+
clearIfTarget: (messageId: string) => void;
|
|
8
|
+
}
|
|
9
|
+
/** Shared "Chuyển tiếp" (forward) state — triggered from message bubbles inside
|
|
10
|
+
* conversation-detail and from the archive's share button inside Setting, two
|
|
11
|
+
* independent branches with no other common ground. Either can call `openForward`
|
|
12
|
+
* directly; no ref/handle needed to reach across. */
|
|
13
|
+
export declare const useForwardStore: import('zustand').UseBoundStore<import('zustand').StoreApi<ForwardState>>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ChatMessage } from '../utils/chat';
|
|
2
|
+
interface PinnedMessagesState {
|
|
3
|
+
/** Only the currently open conversation's pinned list is ever held — a stray update for
|
|
4
|
+
* a conversation the user has since navigated away from is dropped, not applied. */
|
|
5
|
+
conversationId: string | undefined;
|
|
6
|
+
pinnedMessages: ChatMessage[];
|
|
7
|
+
setForConversation: (conversationId: string | undefined, messages: ChatMessage[]) => void;
|
|
8
|
+
applyPinnedUpdate: (conversationId: string | undefined, message: ChatMessage) => void;
|
|
9
|
+
clearIfTarget: (conversationId: string | undefined, messageId: string) => void;
|
|
10
|
+
reset: () => void;
|
|
11
|
+
}
|
|
12
|
+
/** Shared pinned-messages state for the open conversation — read by both the pinned-bar
|
|
13
|
+
* (the list itself) and every message bubble (per-message pin icon), so a pin/unpin from
|
|
14
|
+
* either place is reflected in the other immediately, with no prop drilling between them. */
|
|
15
|
+
export declare const usePinnedMessagesStore: import('zustand').UseBoundStore<import('zustand').StoreApi<PinnedMessagesState>>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface PresenceState {
|
|
2
|
+
/** userId -> online, for every user presence has been reported for since connecting. */
|
|
3
|
+
onlineByUserId: Record<string, boolean>;
|
|
4
|
+
setOnline: (userId: string, isOnline: boolean) => void;
|
|
5
|
+
}
|
|
6
|
+
/** Global presence map fed by a single `presence:changed` socket subscription (wired up
|
|
7
|
+
* once in ChatBootstrap) — every place that needs a user's online dot (sidebar rows,
|
|
8
|
+
* the conversation header) reads it straight from here instead of each patching its own
|
|
9
|
+
* local copy of the same data. */
|
|
10
|
+
export declare const usePresenceStore: import('zustand').UseBoundStore<import('zustand').StoreApi<PresenceState>>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** The three ERP-granted permissions gating actions on a verified (tích xanh) group —
|
|
2
|
+
* decided by the host app's own RBAC, passed in once as ChatModule props. Stored here
|
|
3
|
+
* (not just in ChatRuntimeContext) so code outside the component tree — socket handlers,
|
|
4
|
+
* plain utility functions — can read the current value via `.getState()`, the same
|
|
5
|
+
* reason useAuthStore/usePresenceStore exist alongside the context. */
|
|
6
|
+
interface VerifiedPermissionsState {
|
|
7
|
+
/** Permission to add a new verified group. */
|
|
8
|
+
hasAddGroupVerified: boolean;
|
|
9
|
+
/** Permission to update a verified group: avatar, name, add members, remove members. */
|
|
10
|
+
hasUpdateGroupVerified: boolean;
|
|
11
|
+
/** Permission to delete a verified group. */
|
|
12
|
+
hasDeleteGroupVerified: boolean;
|
|
13
|
+
setVerifiedPermissions: (permissions: {
|
|
14
|
+
hasAddGroupVerified: boolean;
|
|
15
|
+
hasUpdateGroupVerified: boolean;
|
|
16
|
+
hasDeleteGroupVerified: boolean;
|
|
17
|
+
}) => void;
|
|
18
|
+
}
|
|
19
|
+
export declare const useVerifiedPermissionsStore: import('zustand').UseBoundStore<import('zustand').StoreApi<VerifiedPermissionsState>>;
|
|
20
|
+
export {};
|