@droppii-org/chat-sdk 0.1.82 → 0.1.84
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/dist/assets/droppiiFontSelection.json +24523 -24523
- package/dist/assets/sdk/sql-wasm.wasm +0 -0
- package/dist/assets/svg/canned-response.tsx +48 -48
- package/dist/assets/svg/document.tsx +37 -37
- package/dist/assets/svg/empty-box.d.ts +2 -0
- package/dist/assets/svg/empty-box.d.ts.map +1 -0
- package/dist/assets/svg/empty-box.js +2 -0
- package/dist/assets/svg/empty-box.tsx +34 -0
- package/dist/assets/svg/file-pdf.d.ts +2 -0
- package/dist/assets/svg/file-pdf.d.ts.map +1 -0
- package/dist/assets/svg/file-pdf.js +2 -0
- package/dist/assets/svg/file-pdf.tsx +13 -0
- package/dist/assets/svg/index.d.ts +1 -0
- package/dist/assets/svg/index.d.ts.map +1 -1
- package/dist/assets/svg/index.js +1 -0
- package/dist/assets/svg/index.ts +3 -2
- package/dist/components/conversation-detail/ConversationDetailHeader.d.ts +2 -1
- package/dist/components/conversation-detail/ConversationDetailHeader.d.ts.map +1 -1
- package/dist/components/conversation-detail/ConversationDetailHeader.js +2 -2
- package/dist/components/conversation-detail/ThreadProfilePanel.d.ts +11 -0
- package/dist/components/conversation-detail/ThreadProfilePanel.d.ts.map +1 -0
- package/dist/components/conversation-detail/ThreadProfilePanel.js +21 -0
- package/dist/components/media-collection/FileCollection.d.ts.map +1 -1
- package/dist/components/media-collection/FileCollection.js +2 -17
- package/dist/components/media-collection/VideoCollection.d.ts.map +1 -1
- package/dist/components/media-collection/VideoCollection.js +12 -18
- package/dist/components/media-resources/FileResourceList.d.ts +3 -0
- package/dist/components/media-resources/FileResourceList.d.ts.map +1 -0
- package/dist/components/media-resources/FileResourceList.js +17 -0
- package/dist/components/media-resources/ImageResourceGrid.d.ts +3 -0
- package/dist/components/media-resources/ImageResourceGrid.d.ts.map +1 -0
- package/dist/components/media-resources/ImageResourceGrid.js +24 -0
- package/dist/components/media-resources/LinkResourceItem.d.ts +8 -0
- package/dist/components/media-resources/LinkResourceItem.d.ts.map +1 -0
- package/dist/components/media-resources/LinkResourceItem.js +22 -0
- package/dist/components/media-resources/LinkResourceList.d.ts +3 -0
- package/dist/components/media-resources/LinkResourceList.d.ts.map +1 -0
- package/dist/components/media-resources/LinkResourceList.js +25 -0
- package/dist/components/media-resources/MediaEntryCard.d.ts +7 -0
- package/dist/components/media-resources/MediaEntryCard.d.ts.map +1 -0
- package/dist/components/media-resources/MediaEntryCard.js +10 -0
- package/dist/components/media-resources/MediaResourcePanel.d.ts +9 -0
- package/dist/components/media-resources/MediaResourcePanel.d.ts.map +1 -0
- package/dist/components/media-resources/MediaResourcePanel.js +13 -0
- package/dist/components/media-resources/MediaResourceTabBar.d.ts +8 -0
- package/dist/components/media-resources/MediaResourceTabBar.d.ts.map +1 -0
- package/dist/components/media-resources/MediaResourceTabBar.js +13 -0
- package/dist/components/media-resources/MediaResourceTabs.d.ts +7 -0
- package/dist/components/media-resources/MediaResourceTabs.d.ts.map +1 -0
- package/dist/components/media-resources/MediaResourceTabs.js +14 -0
- package/dist/components/media-resources/ResourceEmptyState.d.ts +7 -0
- package/dist/components/media-resources/ResourceEmptyState.d.ts.map +1 -0
- package/dist/components/media-resources/ResourceEmptyState.js +10 -0
- package/dist/components/media-resources/ResourceScrollContainer.d.ts +16 -0
- package/dist/components/media-resources/ResourceScrollContainer.d.ts.map +1 -0
- package/dist/components/media-resources/ResourceScrollContainer.js +29 -0
- package/dist/components/media-resources/VideoPreviewModal.d.ts +10 -0
- package/dist/components/media-resources/VideoPreviewModal.d.ts.map +1 -0
- package/dist/components/media-resources/VideoPreviewModal.js +26 -0
- package/dist/components/media-resources/VideoResourceGrid.d.ts +3 -0
- package/dist/components/media-resources/VideoResourceGrid.d.ts.map +1 -0
- package/dist/components/media-resources/VideoResourceGrid.js +29 -0
- package/dist/components/media-resources/constants.d.ts +14 -0
- package/dist/components/media-resources/constants.d.ts.map +1 -0
- package/dist/components/media-resources/constants.js +39 -0
- package/dist/components/media-resources/index.d.ts +11 -0
- package/dist/components/media-resources/index.d.ts.map +1 -0
- package/dist/components/media-resources/index.js +10 -0
- package/dist/components/media-resources/useResourceTab.d.ts +14 -0
- package/dist/components/media-resources/useResourceTab.d.ts.map +1 -0
- package/dist/components/media-resources/useResourceTab.js +22 -0
- package/dist/components/message/item/FileMessage.d.ts.map +1 -1
- package/dist/components/message/item/FileMessage.js +2 -10
- package/dist/components/message/item/imagePreviewToolbar.d.ts.map +1 -1
- package/dist/components/message/item/imagePreviewToolbar.js +3 -6
- package/dist/components/video-player/VideoPlayerModal.d.ts +19 -0
- package/dist/components/video-player/VideoPlayerModal.d.ts.map +1 -0
- package/dist/components/video-player/VideoPlayerModal.js +45 -0
- package/dist/components/video-player/index.d.ts +3 -0
- package/dist/components/video-player/index.d.ts.map +1 -0
- package/dist/components/video-player/index.js +1 -0
- package/dist/hooks/conversation/useBotFlow.d.ts +1 -1
- package/dist/hooks/conversation/useBotFlow.d.ts.map +1 -1
- package/dist/hooks/conversation/useBotFlow.js +23 -11
- package/dist/hooks/message/useJumpToMessage.d.ts +5 -0
- package/dist/hooks/message/useJumpToMessage.d.ts.map +1 -0
- package/dist/hooks/message/useJumpToMessage.js +32 -0
- package/dist/hooks/message/useMessageResources.d.ts +17 -0
- package/dist/hooks/message/useMessageResources.d.ts.map +1 -0
- package/dist/hooks/message/useMessageResources.js +59 -0
- package/dist/hooks/search/useSearchMessage.d.ts +43 -43
- package/dist/hooks/search/useSearchMessage.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/locales/i18n.ts +19 -19
- package/dist/locales/vi/common.json +171 -162
- package/dist/locales/vi/conversation-inbox.json +53 -52
- package/dist/screens/conversation-detail/index.d.ts.map +1 -1
- package/dist/screens/conversation-detail/index.js +7 -1
- package/dist/services/query.d.ts +1 -0
- package/dist/services/query.d.ts.map +1 -1
- package/dist/services/query.js +1 -0
- package/dist/services/routes.d.ts +1 -0
- package/dist/services/routes.d.ts.map +1 -1
- package/dist/services/routes.js +1 -0
- package/dist/styles/global.css +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/dto.d.ts +16 -3
- package/dist/types/dto.d.ts.map +1 -1
- package/dist/types/sticker.d.ts +9 -0
- package/dist/types/sticker.d.ts.map +1 -0
- package/dist/types/sticker.js +5 -0
- package/dist/utils/common.d.ts +1 -0
- package/dist/utils/common.d.ts.map +1 -1
- package/dist/utils/common.js +11 -0
- package/dist/utils/download.d.ts +2 -0
- package/dist/utils/download.d.ts.map +1 -0
- package/dist/utils/download.js +16 -0
- package/package.json +132 -132
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import { Icon } from "../../components/icon";
|
|
6
|
+
import { useFetchUrlMetadata } from "../../hooks/common/useFetchUrlMetadata";
|
|
7
|
+
import { openLink } from "../../utils/openLink";
|
|
8
|
+
const stripProtocol = (url) => url.replace(/^https?:\/\//i, "");
|
|
9
|
+
const LinkResourceItem = ({ url, messageMetadata }) => {
|
|
10
|
+
const { t } = useTranslation("conversation-inbox");
|
|
11
|
+
const hasMessageMetadata = Boolean((messageMetadata === null || messageMetadata === void 0 ? void 0 : messageMetadata.title) || (messageMetadata === null || messageMetadata === void 0 ? void 0 : messageMetadata.image));
|
|
12
|
+
const { data: fetchedMetadata } = useFetchUrlMetadata(url, !hasMessageMetadata);
|
|
13
|
+
const title = (messageMetadata === null || messageMetadata === void 0 ? void 0 : messageMetadata.title) || (fetchedMetadata === null || fetchedMetadata === void 0 ? void 0 : fetchedMetadata.title) || "";
|
|
14
|
+
const imageUrl = (messageMetadata === null || messageMetadata === void 0 ? void 0 : messageMetadata.image) || (fetchedMetadata === null || fetchedMetadata === void 0 ? void 0 : fetchedMetadata.image) || "";
|
|
15
|
+
const [failedImageUrl, setFailedImageUrl] = useState("");
|
|
16
|
+
const shouldShowImage = Boolean(imageUrl) && failedImageUrl !== imageUrl;
|
|
17
|
+
const handleOpen = () => {
|
|
18
|
+
openLink(url, t);
|
|
19
|
+
};
|
|
20
|
+
return (_jsxs("div", { "data-testid": "link-resource-item", className: "flex w-full cursor-pointer items-center gap-2 rounded-[12px] bg-gray-50/20 p-3", onClick: handleOpen, children: [_jsx("div", { className: "flex h-[76px] w-[76px] shrink-0 items-center justify-center", children: shouldShowImage ? (_jsx("img", { src: imageUrl, alt: "", className: "h-full w-full rounded-[8px] object-cover", onError: () => setFailedImageUrl(imageUrl) })) : (_jsx("div", { className: "flex h-full w-full items-center justify-center rounded-[12px] bg-gray-200", children: _jsx(Icon, { icon: "link-b", size: 24, color: "white" }) })) }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col justify-center", children: [_jsx("p", { className: "truncate text-[14px] font-normal leading-[1.6] tracking-[-0.14px] text-gray-600", children: stripProtocol(url) }), _jsx("p", { className: "line-clamp-2 text-[16px] font-medium leading-[1.6] text-gray-900", children: title || url })] })] }));
|
|
21
|
+
};
|
|
22
|
+
export default LinkResourceItem;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LinkResourceList.d.ts","sourceRoot":"","sources":["../../../src/components/media-resources/LinkResourceList.tsx"],"names":[],"mappings":"AASA,QAAA,MAAM,gBAAgB,+CA2BrB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { parseMessageContent, parseUrlMetadata } from "../../utils/common";
|
|
4
|
+
import { MediaResourceTabKey } from "./constants";
|
|
5
|
+
import ResourceScrollContainer from "./ResourceScrollContainer";
|
|
6
|
+
import LinkResourceItem from "./LinkResourceItem";
|
|
7
|
+
import { useResourceTab } from "./useResourceTab";
|
|
8
|
+
const LinkResourceList = () => {
|
|
9
|
+
const { items, containerProps } = useResourceTab(MediaResourceTabKey.Link);
|
|
10
|
+
const linkRows = items.flatMap((item) => {
|
|
11
|
+
var _a, _b;
|
|
12
|
+
const urls = parseMessageContent((_a = item === null || item === void 0 ? void 0 : item.chatLog) === null || _a === void 0 ? void 0 : _a.content).urls || [];
|
|
13
|
+
const messageMetadata = parseUrlMetadata((_b = item === null || item === void 0 ? void 0 : item.chatLog) === null || _b === void 0 ? void 0 : _b.ex);
|
|
14
|
+
return urls.map((url, urlIndex) => {
|
|
15
|
+
var _a;
|
|
16
|
+
return ({
|
|
17
|
+
key: `${(_a = item.chatLog) === null || _a === void 0 ? void 0 : _a.clientMsgID}-${urlIndex}`,
|
|
18
|
+
url,
|
|
19
|
+
messageMetadata: urlIndex === 0 ? messageMetadata : undefined,
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
return (_jsx(ResourceScrollContainer, Object.assign({}, containerProps, { itemCount: linkRows.length, children: _jsx("div", { className: "flex flex-col gap-2 p-3", children: linkRows.map((row) => (_jsx(LinkResourceItem, { url: row.url, messageMetadata: row.messageMetadata }, row.key))) }) })));
|
|
24
|
+
};
|
|
25
|
+
export default LinkResourceList;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MediaResourceTabKey } from "./constants";
|
|
2
|
+
interface MediaEntryCardProps {
|
|
3
|
+
onSelect: (tabKey: MediaResourceTabKey) => void;
|
|
4
|
+
}
|
|
5
|
+
declare const MediaEntryCard: ({ onSelect }: MediaEntryCardProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default MediaEntryCard;
|
|
7
|
+
//# sourceMappingURL=MediaEntryCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MediaEntryCard.d.ts","sourceRoot":"","sources":["../../../src/components/media-resources/MediaEntryCard.tsx"],"names":[],"mappings":"AAIA,OAAO,EAEL,mBAAmB,EAGpB,MAAM,aAAa,CAAC;AAErB,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACjD;AAED,QAAA,MAAM,cAAc,GAAI,cAAc,mBAAmB,4CA4BxD,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useTranslation } from "react-i18next";
|
|
4
|
+
import { Icon } from "../../components/icon";
|
|
5
|
+
import { MEDIA_RESOURCE_TAB_ORDER, RESOURCE_ENTRY_ICON, RESOURCE_TAB_LABEL_KEY, } from "./constants";
|
|
6
|
+
const MediaEntryCard = ({ onSelect }) => {
|
|
7
|
+
const { t } = useTranslation();
|
|
8
|
+
return (_jsx("div", { "data-testid": "media-entry-card", className: "flex w-full items-center justify-between overflow-hidden rounded-[12px] bg-white", children: MEDIA_RESOURCE_TAB_ORDER.map((tabKey) => (_jsxs("button", { type: "button", "data-testid": `media-entry-${tabKey}`, className: "flex min-w-0 flex-1 cursor-pointer flex-col items-center justify-center gap-1 px-2 py-3", onClick: () => onSelect(tabKey), children: [_jsx(Icon, { icon: RESOURCE_ENTRY_ICON[tabKey], size: 24, className: "text-gray-900" }), _jsx("span", { className: "whitespace-nowrap text-center text-[12px] font-normal leading-[1.6] text-gray-900", children: t(RESOURCE_TAB_LABEL_KEY[tabKey]) })] }, tabKey))) }));
|
|
9
|
+
};
|
|
10
|
+
export default MediaEntryCard;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { MediaResourceTabKey } from "./constants";
|
|
2
|
+
interface MediaResourcePanelProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
initialTab: MediaResourceTabKey;
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
}
|
|
7
|
+
declare const MediaResourcePanel: ({ open, initialTab, onClose, }: MediaResourcePanelProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export default MediaResourcePanel;
|
|
9
|
+
//# sourceMappingURL=MediaResourcePanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MediaResourcePanel.d.ts","sourceRoot":"","sources":["../../../src/components/media-resources/MediaResourcePanel.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGlD,UAAU,uBAAuB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,mBAAmB,CAAC;IAChC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,kBAAkB,GAAI,gCAIzB,uBAAuB,4CAoCzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Drawer } from "antd";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import { Icon } from "../../components/icon";
|
|
6
|
+
import MediaResourceTabs from "./MediaResourceTabs";
|
|
7
|
+
const MediaResourcePanel = ({ open, initialTab, onClose, }) => {
|
|
8
|
+
const { t } = useTranslation();
|
|
9
|
+
return (_jsx(Drawer, { open: open, onClose: onClose, placement: "right", closeIcon: false, width: "100%", styles: {
|
|
10
|
+
body: { padding: 0 },
|
|
11
|
+
}, getContainer: false, destroyOnHidden: true, children: _jsxs("div", { className: "flex h-full flex-col bg-white", children: [_jsxs("div", { className: "flex w-full items-center p-2", children: [_jsx("button", { type: "button", "data-testid": "media-resource-back", className: "flex shrink-0 cursor-pointer items-center justify-center p-2 text-primary-400", onClick: onClose, children: _jsx(Icon, { icon: "arrow-left-o", size: 24 }) }), _jsx("p", { className: "min-w-0 flex-1 truncate px-2 text-[18px] font-bold leading-[1.6] text-gray-900", children: t("media_resources_title") })] }), _jsx("div", { className: "min-h-0 flex-1", children: _jsx(MediaResourceTabs, { initialTab: initialTab }) })] }) }));
|
|
12
|
+
};
|
|
13
|
+
export default MediaResourcePanel;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { MediaResourceTabKey } from "./constants";
|
|
2
|
+
interface MediaResourceTabBarProps {
|
|
3
|
+
activeKey: MediaResourceTabKey;
|
|
4
|
+
onChange: (key: MediaResourceTabKey) => void;
|
|
5
|
+
}
|
|
6
|
+
declare const MediaResourceTabBar: ({ activeKey, onChange, }: MediaResourceTabBarProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default MediaResourceTabBar;
|
|
8
|
+
//# sourceMappingURL=MediaResourceTabBar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MediaResourceTabBar.d.ts","sourceRoot":"","sources":["../../../src/components/media-resources/MediaResourceTabBar.tsx"],"names":[],"mappings":"AAIA,OAAO,EAEL,mBAAmB,EAEpB,MAAM,aAAa,CAAC;AAErB,UAAU,wBAAwB;IAChC,SAAS,EAAE,mBAAmB,CAAC;IAC/B,QAAQ,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAC9C;AAED,QAAA,MAAM,mBAAmB,GAAI,0BAG1B,wBAAwB,4CAwC1B,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import { MEDIA_RESOURCE_TAB_ORDER, RESOURCE_TAB_LABEL_KEY, } from "./constants";
|
|
6
|
+
const MediaResourceTabBar = ({ activeKey, onChange, }) => {
|
|
7
|
+
const { t } = useTranslation();
|
|
8
|
+
return (_jsx("div", { role: "tablist", "data-testid": "media-resource-tab-bar", className: "flex w-full items-end border-b border-gray-25 bg-white", children: MEDIA_RESOURCE_TAB_ORDER.map((tabKey) => {
|
|
9
|
+
const isActive = tabKey === activeKey;
|
|
10
|
+
return (_jsxs("button", { type: "button", role: "tab", "aria-selected": isActive, "data-testid": `media-resource-tab-${tabKey}`, className: "flex cursor-pointer flex-col items-center justify-center px-3 pt-0.5", onClick: () => onChange(tabKey), children: [_jsx("span", { className: clsx("text-center text-[14px] font-bold leading-[1.6] tracking-[0.14px]", isActive ? "text-primary-400" : "text-gray-400"), children: t(RESOURCE_TAB_LABEL_KEY[tabKey]) }), _jsx("span", { className: clsx("h-[3px] w-full rounded-[4px] bg-primary-400", !isActive && "opacity-0") })] }, tabKey));
|
|
11
|
+
}) }));
|
|
12
|
+
};
|
|
13
|
+
export default MediaResourceTabBar;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MediaResourceTabKey } from "./constants";
|
|
2
|
+
export interface MediaResourceTabsProps {
|
|
3
|
+
initialTab?: MediaResourceTabKey;
|
|
4
|
+
}
|
|
5
|
+
declare const MediaResourceTabs: ({ initialTab, }: MediaResourceTabsProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default MediaResourceTabs;
|
|
7
|
+
//# sourceMappingURL=MediaResourceTabs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MediaResourceTabs.d.ts","sourceRoot":"","sources":["../../../src/components/media-resources/MediaResourceTabs.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAOlD,MAAM,WAAW,sBAAsB;IACrC,UAAU,CAAC,EAAE,mBAAmB,CAAC;CAClC;AAED,QAAA,MAAM,iBAAiB,GAAI,iBAExB,sBAAsB,4CAcxB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { MediaResourceTabKey } from "./constants";
|
|
5
|
+
import MediaResourceTabBar from "./MediaResourceTabBar";
|
|
6
|
+
import ImageResourceGrid from "./ImageResourceGrid";
|
|
7
|
+
import VideoResourceGrid from "./VideoResourceGrid";
|
|
8
|
+
import FileResourceList from "./FileResourceList";
|
|
9
|
+
import LinkResourceList from "./LinkResourceList";
|
|
10
|
+
const MediaResourceTabs = ({ initialTab = MediaResourceTabKey.Image, }) => {
|
|
11
|
+
const [activeKey, setActiveKey] = useState(initialTab);
|
|
12
|
+
return (_jsxs("div", { className: "flex h-full flex-col bg-white", children: [_jsx(MediaResourceTabBar, { activeKey: activeKey, onChange: setActiveKey }), _jsxs("div", { className: "min-h-0 flex-1", children: [activeKey === MediaResourceTabKey.Image && _jsx(ImageResourceGrid, {}), activeKey === MediaResourceTabKey.Video && _jsx(VideoResourceGrid, {}), activeKey === MediaResourceTabKey.File && _jsx(FileResourceList, {}), activeKey === MediaResourceTabKey.Link && _jsx(LinkResourceList, {})] })] }));
|
|
13
|
+
};
|
|
14
|
+
export default MediaResourceTabs;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MediaResourceTabKey } from "./constants";
|
|
2
|
+
interface ResourceEmptyStateProps {
|
|
3
|
+
tabKey: MediaResourceTabKey;
|
|
4
|
+
}
|
|
5
|
+
declare const ResourceEmptyState: ({ tabKey }: ResourceEmptyStateProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default ResourceEmptyState;
|
|
7
|
+
//# sourceMappingURL=ResourceEmptyState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResourceEmptyState.d.ts","sourceRoot":"","sources":["../../../src/components/media-resources/ResourceEmptyState.tsx"],"names":[],"mappings":"AAIA,OAAO,EACL,mBAAmB,EAEpB,MAAM,aAAa,CAAC;AAErB,UAAU,uBAAuB;IAC/B,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AAED,QAAA,MAAM,kBAAkB,GAAI,YAAY,uBAAuB,4CAc9D,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useTranslation } from "react-i18next";
|
|
4
|
+
import { emptyBoxIcon } from "../../assets/svg";
|
|
5
|
+
import { RESOURCE_EMPTY_TEXT_KEY, } from "./constants";
|
|
6
|
+
const ResourceEmptyState = ({ tabKey }) => {
|
|
7
|
+
const { t } = useTranslation();
|
|
8
|
+
return (_jsxs("div", { "data-testid": "resource-empty-state", className: "flex h-full w-full flex-col items-center gap-3 bg-white px-8 py-[42px]", children: [_jsx("div", { className: "h-[120px] w-[120px] shrink-0", children: emptyBoxIcon }), _jsx("p", { className: "text-center text-[16px] font-normal leading-[1.6] tracking-[0.16px] text-gray-600", children: t(RESOURCE_EMPTY_TEXT_KEY[tabKey]) })] }));
|
|
9
|
+
};
|
|
10
|
+
export default ResourceEmptyState;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { MediaResourceTabKey } from "./constants";
|
|
3
|
+
export interface ResourceScrollContainerProps {
|
|
4
|
+
tabKey: MediaResourceTabKey;
|
|
5
|
+
itemCount: number;
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
isError: boolean;
|
|
8
|
+
hasNextPage: boolean;
|
|
9
|
+
isFetchingNextPage: boolean;
|
|
10
|
+
fetchNextPage: () => void;
|
|
11
|
+
onRetry: () => void;
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
declare const ResourceScrollContainer: ({ tabKey, itemCount, isLoading, isError, hasNextPage, isFetchingNextPage, fetchNextPage, onRetry, children, }: ResourceScrollContainerProps) => import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export default ResourceScrollContainer;
|
|
16
|
+
//# sourceMappingURL=ResourceScrollContainer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResourceScrollContainer.d.ts","sourceRoot":"","sources":["../../../src/components/media-resources/ResourceScrollContainer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAa,MAAM,OAAO,CAAC;AAI7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGlD,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,mBAAmB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAQD,QAAA,MAAM,uBAAuB,GAAI,+GAU9B,4BAA4B,4CAgD9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect } from "react";
|
|
4
|
+
import InfiniteScroll from "react-infinite-scroll-component";
|
|
5
|
+
import { Button, Spin } from "antd";
|
|
6
|
+
import { useTranslation } from "react-i18next";
|
|
7
|
+
import ResourceEmptyState from "./ResourceEmptyState";
|
|
8
|
+
const LoadingIndicator = () => (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }));
|
|
9
|
+
const ResourceScrollContainer = ({ tabKey, itemCount, isLoading, isError, hasNextPage, isFetchingNextPage, fetchNextPage, onRetry, children, }) => {
|
|
10
|
+
const { t } = useTranslation();
|
|
11
|
+
const isEmptyWithMorePages = itemCount === 0 && hasNextPage && !isError;
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (isEmptyWithMorePages && !isFetchingNextPage) {
|
|
14
|
+
fetchNextPage();
|
|
15
|
+
}
|
|
16
|
+
}, [isEmptyWithMorePages, isFetchingNextPage, fetchNextPage]);
|
|
17
|
+
if (isLoading || isEmptyWithMorePages) {
|
|
18
|
+
return (_jsx("div", { className: "flex h-full items-center justify-center py-6", children: _jsx(Spin, {}) }));
|
|
19
|
+
}
|
|
20
|
+
if (isError) {
|
|
21
|
+
return (_jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-3 px-8 py-6", children: [_jsx("p", { className: "text-center text-[16px] leading-[1.6] text-gray-600", children: t("resource_load_error") }), _jsx(Button, { onClick: onRetry, children: t("retry") })] }));
|
|
22
|
+
}
|
|
23
|
+
if (itemCount === 0) {
|
|
24
|
+
return _jsx(ResourceEmptyState, { tabKey: tabKey });
|
|
25
|
+
}
|
|
26
|
+
const scrollableId = `resource-scroll-${tabKey}`;
|
|
27
|
+
return (_jsx("div", { id: scrollableId, className: "h-full overflow-auto", children: _jsx(InfiniteScroll, { dataLength: itemCount, next: fetchNextPage, hasMore: hasNextPage, loader: _jsx(LoadingIndicator, {}), scrollableTarget: scrollableId, children: children }) }));
|
|
28
|
+
};
|
|
29
|
+
export default ResourceScrollContainer;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { MessageResourcesItem } from "../../types/dto";
|
|
2
|
+
interface VideoPreviewModalProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
items: MessageResourcesItem[];
|
|
5
|
+
initialIndex: number;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
}
|
|
8
|
+
declare const VideoPreviewModal: ({ open, items, initialIndex, onClose, }: VideoPreviewModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export default VideoPreviewModal;
|
|
10
|
+
//# sourceMappingURL=VideoPreviewModal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoPreviewModal.d.ts","sourceRoot":"","sources":["../../../src/components/media-resources/VideoPreviewModal.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAInD,UAAU,sBAAsB;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAKD,QAAA,MAAM,iBAAiB,GAAI,yCAKxB,sBAAsB,4CAmDxB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Button, Carousel, Modal } from "antd";
|
|
5
|
+
import { useTranslation } from "react-i18next";
|
|
6
|
+
import { Icon } from "../../components/icon";
|
|
7
|
+
import { downloadFileFromUrl } from "../../utils/download";
|
|
8
|
+
const parseVideoContent = (item) => { var _a; return JSON.parse(((_a = item === null || item === void 0 ? void 0 : item.chatLog) === null || _a === void 0 ? void 0 : _a.content) || "{}"); };
|
|
9
|
+
const VideoPreviewModal = ({ open, items, initialIndex, onClose, }) => {
|
|
10
|
+
const { t } = useTranslation();
|
|
11
|
+
const [currentIndex, setCurrentIndex] = useState(initialIndex);
|
|
12
|
+
const currentVideoContent = parseVideoContent(items[currentIndex]);
|
|
13
|
+
const handleDownload = () => {
|
|
14
|
+
downloadFileFromUrl(currentVideoContent.videoUrl || "");
|
|
15
|
+
};
|
|
16
|
+
return (_jsxs(Modal, { open: open, onCancel: onClose, footer: null, centered: true, destroyOnClose: true, styles: {
|
|
17
|
+
content: {
|
|
18
|
+
padding: 0,
|
|
19
|
+
},
|
|
20
|
+
}, children: [_jsx(Carousel, { dots: true, infinite: false, initialSlide: initialIndex, afterChange: (index) => setCurrentIndex(index), children: items.map((item, index) => {
|
|
21
|
+
var _a;
|
|
22
|
+
const videoContent = parseVideoContent(item);
|
|
23
|
+
return (_jsx("div", { className: "text-center", children: _jsx("video", { src: videoContent.videoUrl, controls: true, autoPlay: index === currentIndex, className: "max-h-[80vh] w-full" }) }, (_a = item.chatLog) === null || _a === void 0 ? void 0 : _a.clientMsgID));
|
|
24
|
+
}) }), _jsx("div", { className: "flex items-center justify-end p-2", children: _jsxs(Button, { type: "text", onClick: handleDownload, children: [_jsx(Icon, { icon: "download-o", size: 20 }), t("download")] }) })] }));
|
|
25
|
+
};
|
|
26
|
+
export default VideoPreviewModal;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoResourceGrid.d.ts","sourceRoot":"","sources":["../../../src/components/media-resources/VideoResourceGrid.tsx"],"names":[],"mappings":"AAeA,QAAA,MAAM,iBAAiB,+CA8DtB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo, useState } from "react";
|
|
4
|
+
import { Image } from "antd";
|
|
5
|
+
import { useBoolean } from "ahooks";
|
|
6
|
+
import { images } from "../../constants/images";
|
|
7
|
+
import { Icon } from "../../components/icon";
|
|
8
|
+
import VideoPlayerModal, { toVideoPlayerSources, } from "../../components/video-player/VideoPlayerModal";
|
|
9
|
+
import { formatDuration } from "../../utils/common";
|
|
10
|
+
import { MediaResourceTabKey, MILLISECONDS_PER_SECOND } from "./constants";
|
|
11
|
+
import ResourceScrollContainer from "./ResourceScrollContainer";
|
|
12
|
+
import { useResourceTab } from "./useResourceTab";
|
|
13
|
+
const VideoResourceGrid = () => {
|
|
14
|
+
const { items, containerProps } = useResourceTab(MediaResourceTabKey.Video);
|
|
15
|
+
const [isPreviewOpen, { setTrue: openPreview, setFalse: closePreview }] = useBoolean(false);
|
|
16
|
+
const [previewIndex, setPreviewIndex] = useState(0);
|
|
17
|
+
const videoSources = useMemo(() => toVideoPlayerSources(items), [items]);
|
|
18
|
+
const handleOpenPreview = (index) => {
|
|
19
|
+
setPreviewIndex(index);
|
|
20
|
+
openPreview();
|
|
21
|
+
};
|
|
22
|
+
return (_jsxs(ResourceScrollContainer, Object.assign({}, containerProps, { itemCount: videoSources.length, children: [_jsx("div", { className: "grid grid-cols-3 gap-2 p-3", children: videoSources.map((video, index) => {
|
|
23
|
+
const durationLabel = video.duration
|
|
24
|
+
? formatDuration(video.duration / MILLISECONDS_PER_SECOND)
|
|
25
|
+
: "";
|
|
26
|
+
return (_jsxs("div", { className: "relative aspect-square w-full cursor-pointer overflow-hidden rounded-[8px]", onClick: () => handleOpenPreview(index), children: [_jsx(Image, { rootClassName: "w-full h-full pointer-events-none", className: "w-full !h-full object-cover", src: video.snapshotUrl, alt: "video thumbnail", preview: false, fallback: images.imageFailed }), _jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/30", children: _jsx(Icon, { icon: "play-b", size: 24, color: "white" }) }), durationLabel && (_jsx("div", { className: "absolute bottom-1 right-1 flex items-center justify-center rounded-[12px] bg-black/70 px-1", children: _jsx("span", { className: "text-[10px] font-normal leading-[1.6] text-white", children: durationLabel }) }))] }, video.id));
|
|
27
|
+
}) }), isPreviewOpen && (_jsx(VideoPlayerModal, { open: isPreviewOpen, videos: videoSources, initialIndex: previewIndex, onClose: closePreview }))] })));
|
|
28
|
+
};
|
|
29
|
+
export default VideoResourceGrid;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MessageType } from "@openim/wasm-client-sdk";
|
|
2
|
+
export declare enum MediaResourceTabKey {
|
|
3
|
+
Image = "image",
|
|
4
|
+
Video = "video",
|
|
5
|
+
File = "file",
|
|
6
|
+
Link = "link"
|
|
7
|
+
}
|
|
8
|
+
export declare const MEDIA_RESOURCE_TAB_ORDER: MediaResourceTabKey[];
|
|
9
|
+
export declare const RESOURCE_CONTENT_TYPE: Record<MediaResourceTabKey, MessageType>;
|
|
10
|
+
export declare const RESOURCE_TAB_LABEL_KEY: Record<MediaResourceTabKey, string>;
|
|
11
|
+
export declare const RESOURCE_EMPTY_TEXT_KEY: Record<MediaResourceTabKey, string>;
|
|
12
|
+
export declare const RESOURCE_ENTRY_ICON: Record<MediaResourceTabKey, string>;
|
|
13
|
+
export declare const MILLISECONDS_PER_SECOND = 1000;
|
|
14
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/components/media-resources/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD,oBAAY,mBAAmB;IAC7B,KAAK,UAAU;IACf,KAAK,UAAU;IACf,IAAI,SAAS;IACb,IAAI,SAAS;CACd;AAED,eAAO,MAAM,wBAAwB,EAAE,mBAAmB,EAKzD,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,mBAAmB,EAAE,WAAW,CAK1E,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAKtE,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAKvE,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAKnE,CAAC;AAEF,eAAO,MAAM,uBAAuB,OAAO,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { MessageType } from "@openim/wasm-client-sdk";
|
|
2
|
+
export var MediaResourceTabKey;
|
|
3
|
+
(function (MediaResourceTabKey) {
|
|
4
|
+
MediaResourceTabKey["Image"] = "image";
|
|
5
|
+
MediaResourceTabKey["Video"] = "video";
|
|
6
|
+
MediaResourceTabKey["File"] = "file";
|
|
7
|
+
MediaResourceTabKey["Link"] = "link";
|
|
8
|
+
})(MediaResourceTabKey || (MediaResourceTabKey = {}));
|
|
9
|
+
export const MEDIA_RESOURCE_TAB_ORDER = [
|
|
10
|
+
MediaResourceTabKey.Image,
|
|
11
|
+
MediaResourceTabKey.Video,
|
|
12
|
+
MediaResourceTabKey.File,
|
|
13
|
+
MediaResourceTabKey.Link,
|
|
14
|
+
];
|
|
15
|
+
export const RESOURCE_CONTENT_TYPE = {
|
|
16
|
+
[MediaResourceTabKey.Image]: MessageType.PictureMessage,
|
|
17
|
+
[MediaResourceTabKey.Video]: MessageType.VideoMessage,
|
|
18
|
+
[MediaResourceTabKey.File]: MessageType.FileMessage,
|
|
19
|
+
[MediaResourceTabKey.Link]: MessageType.UrlTextMessage,
|
|
20
|
+
};
|
|
21
|
+
export const RESOURCE_TAB_LABEL_KEY = {
|
|
22
|
+
[MediaResourceTabKey.Image]: "image",
|
|
23
|
+
[MediaResourceTabKey.Video]: "video",
|
|
24
|
+
[MediaResourceTabKey.File]: "file",
|
|
25
|
+
[MediaResourceTabKey.Link]: "link",
|
|
26
|
+
};
|
|
27
|
+
export const RESOURCE_EMPTY_TEXT_KEY = {
|
|
28
|
+
[MediaResourceTabKey.Image]: "no_resource_images",
|
|
29
|
+
[MediaResourceTabKey.Video]: "no_resource_videos",
|
|
30
|
+
[MediaResourceTabKey.File]: "no_resource_files",
|
|
31
|
+
[MediaResourceTabKey.Link]: "no_resource_links",
|
|
32
|
+
};
|
|
33
|
+
export const RESOURCE_ENTRY_ICON = {
|
|
34
|
+
[MediaResourceTabKey.Image]: "image-o",
|
|
35
|
+
[MediaResourceTabKey.Video]: "video-o",
|
|
36
|
+
[MediaResourceTabKey.File]: "folder-o",
|
|
37
|
+
[MediaResourceTabKey.Link]: "link-alt-o",
|
|
38
|
+
};
|
|
39
|
+
export const MILLISECONDS_PER_SECOND = 1000;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { default as MediaResourceTabs } from "./MediaResourceTabs";
|
|
2
|
+
export { default as MediaResourceTabBar } from "./MediaResourceTabBar";
|
|
3
|
+
export { default as MediaResourcePanel } from "./MediaResourcePanel";
|
|
4
|
+
export { default as MediaEntryCard } from "./MediaEntryCard";
|
|
5
|
+
export { default as ImageResourceGrid } from "./ImageResourceGrid";
|
|
6
|
+
export { default as VideoResourceGrid } from "./VideoResourceGrid";
|
|
7
|
+
export { default as FileResourceList } from "./FileResourceList";
|
|
8
|
+
export { default as LinkResourceList } from "./LinkResourceList";
|
|
9
|
+
export { default as ResourceEmptyState } from "./ResourceEmptyState";
|
|
10
|
+
export * from "./constants";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/media-resources/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACrE,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { default as MediaResourceTabs } from "./MediaResourceTabs";
|
|
2
|
+
export { default as MediaResourceTabBar } from "./MediaResourceTabBar";
|
|
3
|
+
export { default as MediaResourcePanel } from "./MediaResourcePanel";
|
|
4
|
+
export { default as MediaEntryCard } from "./MediaEntryCard";
|
|
5
|
+
export { default as ImageResourceGrid } from "./ImageResourceGrid";
|
|
6
|
+
export { default as VideoResourceGrid } from "./VideoResourceGrid";
|
|
7
|
+
export { default as FileResourceList } from "./FileResourceList";
|
|
8
|
+
export { default as LinkResourceList } from "./LinkResourceList";
|
|
9
|
+
export { default as ResourceEmptyState } from "./ResourceEmptyState";
|
|
10
|
+
export * from "./constants";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MediaResourceTabKey } from "./constants";
|
|
2
|
+
export declare const useResourceTab: (tabKey: MediaResourceTabKey) => {
|
|
3
|
+
items: import("../../types/dto").MessageResultItem[];
|
|
4
|
+
containerProps: {
|
|
5
|
+
tabKey: MediaResourceTabKey;
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
isError: boolean;
|
|
8
|
+
hasNextPage: boolean;
|
|
9
|
+
isFetchingNextPage: boolean;
|
|
10
|
+
fetchNextPage: (options?: import("@tanstack/react-query").FetchNextPageOptions) => Promise<import("@tanstack/react-query").InfiniteQueryObserverResult<import("@tanstack/react-query").InfiniteData<import("../../types/dto").PagedMessageResponse, unknown>, Error>>;
|
|
11
|
+
onRetry: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<import("@tanstack/react-query").InfiniteData<import("../../types/dto").PagedMessageResponse, unknown>, Error>>;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=useResourceTab.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useResourceTab.d.ts","sourceRoot":"","sources":["../../../src/components/media-resources/useResourceTab.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAyB,MAAM,aAAa,CAAC;AAEzE,eAAO,MAAM,cAAc,GAAI,QAAQ,mBAAmB;;;;;;;;;;;CA8BzD,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import useConversationStore from "../../store/conversation";
|
|
2
|
+
import { useMessageResources } from "../../hooks/message/useMessageResources";
|
|
3
|
+
import { RESOURCE_CONTENT_TYPE } from "./constants";
|
|
4
|
+
export const useResourceTab = (tabKey) => {
|
|
5
|
+
const selectedSourceId = useConversationStore((state) => state.selectedSourceId);
|
|
6
|
+
const { items, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isError, refetch, } = useMessageResources({
|
|
7
|
+
recvID: selectedSourceId,
|
|
8
|
+
contentType: RESOURCE_CONTENT_TYPE[tabKey],
|
|
9
|
+
});
|
|
10
|
+
return {
|
|
11
|
+
items,
|
|
12
|
+
containerProps: {
|
|
13
|
+
tabKey,
|
|
14
|
+
isLoading,
|
|
15
|
+
isError,
|
|
16
|
+
hasNextPage,
|
|
17
|
+
isFetchingNextPage,
|
|
18
|
+
fetchNextPage,
|
|
19
|
+
onRetry: refetch,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/FileMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"FileMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/FileMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,yBAAyB,CAAC;AAOrE,UAAU,oBAAoB;IAC5B,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,QAAA,MAAM,eAAe,GAAI,OAAO,oBAAoB,4CAyDnD,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -4,6 +4,7 @@ import { Spin } from "antd";
|
|
|
4
4
|
import { Icon } from "../../../components/icon";
|
|
5
5
|
import { documentIcon } from "../../../assets/svg";
|
|
6
6
|
import { renderFileSize, shortenFileName } from "../../../utils/common";
|
|
7
|
+
import { downloadFileFromUrl } from "../../../utils/download";
|
|
7
8
|
const FileMessageItem = (props) => {
|
|
8
9
|
var _a;
|
|
9
10
|
const { message } = props;
|
|
@@ -19,16 +20,7 @@ const FileMessageItem = (props) => {
|
|
|
19
20
|
window.open(sourceUrl, "_blank", "noopener,noreferrer");
|
|
20
21
|
};
|
|
21
22
|
const handleDownload = () => {
|
|
22
|
-
|
|
23
|
-
return;
|
|
24
|
-
const link = document.createElement("a");
|
|
25
|
-
link.href = sourceUrl;
|
|
26
|
-
link.setAttribute("download", fileName);
|
|
27
|
-
link.setAttribute("target", "_blank");
|
|
28
|
-
link.setAttribute("rel", "noopener noreferrer");
|
|
29
|
-
document.body.appendChild(link);
|
|
30
|
-
link.click();
|
|
31
|
-
link.remove();
|
|
23
|
+
downloadFileFromUrl(sourceUrl, fileName);
|
|
32
24
|
};
|
|
33
25
|
return (_jsx(Spin, { spinning: isSending, children: _jsxs("div", { className: "flex h-[51px] w-[292px] items-center gap-3 rounded-xl border border-gray-200/20 bg-white px-3 py-1", children: [_jsx("div", { className: "flex h-6 w-6 shrink-0 items-center justify-center overflow-hidden [&>svg]:h-6 [&>svg]:w-6", children: documentIcon }), _jsxs("div", { className: "flex min-w-0 w-[168px] flex-col justify-center", children: [_jsx("span", { className: "truncate text-[14px] font-medium leading-[160%] text-gray-900", children: shortenFileName(fileName) }), _jsx("span", { className: "truncate text-[13px] leading-[160%] text-gray-600", children: renderFileSize(fileSize) })] }), _jsxs("div", { className: "flex w-[52px] shrink-0 items-center justify-end gap-3", children: [_jsx("button", { type: "button", onClick: handleView, disabled: !hasUrl, "aria-label": "Xem file", className: !hasUrl ? "opacity-30" : undefined, children: _jsx(Icon, { icon: "eye-o", size: 20, className: "text-gray-900" }) }), _jsx("button", { type: "button", onClick: handleDownload, disabled: !hasUrl, "aria-label": "T\u1EA3i xu\u1ED1ng", className: !hasUrl ? "opacity-30" : undefined, children: _jsx(Icon, { icon: "download-o", size: 20, className: "text-gray-900" }) })] })] }) }));
|
|
34
26
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imagePreviewToolbar.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/imagePreviewToolbar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"imagePreviewToolbar.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/imagePreviewToolbar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAKzC,eAAO,MAAM,aAAa,GAAI,WAAW,MAAM,SAI9C,CAAC;AAEF,eAAO,MAAM,yBAAyB,GACpC,cAAc,SAAS,EACvB,UAAU,MAAM,GAAG,SAAS,EAC5B,GAAG,SAAS,KACX,SAcF,CAAC"}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Button } from "antd";
|
|
3
3
|
import { Icon } from "../../../components/icon";
|
|
4
|
+
import { downloadFileFromUrl } from "../../../utils/download";
|
|
4
5
|
export const downloadImage = (imageUrl) => {
|
|
5
6
|
if (!imageUrl)
|
|
6
7
|
return;
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
link.download = imageUrl.substring(imageUrl.lastIndexOf("/") + 1);
|
|
10
|
-
document.body.appendChild(link);
|
|
11
|
-
link.click();
|
|
12
|
-
document.body.removeChild(link);
|
|
8
|
+
const fileName = imageUrl.substring(imageUrl.lastIndexOf("/") + 1);
|
|
9
|
+
downloadFileFromUrl(imageUrl, fileName);
|
|
13
10
|
};
|
|
14
11
|
export const renderImagePreviewToolbar = (originalNode, imageUrl, t) => (_jsxs("div", { className: "flex flex-col justify-center gap-2", children: [originalNode, imageUrl && (_jsx(Button, { type: "primary", icon: _jsx(Icon, { icon: "download-o", size: 16, className: "text-white" }), className: "self-center", onClick: () => downloadImage(imageUrl), children: t("download") }))] }));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { MessageResultItem } from "../../types/dto";
|
|
2
|
+
export interface VideoPlayerSource {
|
|
3
|
+
id: string;
|
|
4
|
+
videoUrl?: string;
|
|
5
|
+
snapshotUrl?: string;
|
|
6
|
+
snapshotWidth?: number;
|
|
7
|
+
snapshotHeight?: number;
|
|
8
|
+
duration?: number;
|
|
9
|
+
}
|
|
10
|
+
interface VideoPlayerModalProps {
|
|
11
|
+
open: boolean;
|
|
12
|
+
videos: VideoPlayerSource[];
|
|
13
|
+
initialIndex: number;
|
|
14
|
+
onClose: () => void;
|
|
15
|
+
}
|
|
16
|
+
export declare const toVideoPlayerSources: (items: MessageResultItem[]) => VideoPlayerSource[];
|
|
17
|
+
declare const VideoPlayerModal: ({ open, videos, initialIndex, onClose, }: VideoPlayerModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export default VideoPlayerModal;
|
|
19
|
+
//# sourceMappingURL=VideoPlayerModal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoPlayerModal.d.ts","sourceRoot":"","sources":["../../../src/components/video-player/VideoPlayerModal.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGhD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,qBAAqB;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAQD,eAAO,MAAM,oBAAoB,GAC/B,OAAO,iBAAiB,EAAE,KACzB,iBAAiB,EAWhB,CAAC;AAEL,QAAA,MAAM,gBAAgB,GAAI,0CAKvB,qBAAqB,4CAqFvB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Modal } from "antd";
|
|
5
|
+
import clsx from "clsx";
|
|
6
|
+
import { useTranslation } from "react-i18next";
|
|
7
|
+
import { Icon } from "../../components/icon";
|
|
8
|
+
import { parseMessageContent } from "../../utils/common";
|
|
9
|
+
const NAVIGATION_BUTTON_CLASS = "absolute top-1/2 flex h-11 w-11 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-black/50 text-white transition-colors hover:bg-black/70";
|
|
10
|
+
const CLOSE_BUTTON_CLASS = "flex h-8 w-8 items-center justify-center rounded-full bg-black/50 text-white transition-colors hover:bg-black/70";
|
|
11
|
+
export const toVideoPlayerSources = (items) => items.map((item) => {
|
|
12
|
+
var _a, _b;
|
|
13
|
+
const videoContent = parseMessageContent((_a = item === null || item === void 0 ? void 0 : item.chatLog) === null || _a === void 0 ? void 0 : _a.content);
|
|
14
|
+
return {
|
|
15
|
+
id: (_b = item === null || item === void 0 ? void 0 : item.chatLog) === null || _b === void 0 ? void 0 : _b.clientMsgID,
|
|
16
|
+
videoUrl: videoContent.videoUrl,
|
|
17
|
+
snapshotUrl: videoContent.snapshotUrl,
|
|
18
|
+
snapshotWidth: videoContent.snapshotWidth,
|
|
19
|
+
snapshotHeight: videoContent.snapshotHeight,
|
|
20
|
+
duration: videoContent.duration,
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
const VideoPlayerModal = ({ open, videos, initialIndex, onClose, }) => {
|
|
24
|
+
const { t } = useTranslation();
|
|
25
|
+
const [currentIndex, setCurrentIndex] = useState(initialIndex);
|
|
26
|
+
const hasPreviousVideo = currentIndex > 0;
|
|
27
|
+
const hasNextVideo = currentIndex < videos.length - 1;
|
|
28
|
+
const goToPreviousVideo = () => {
|
|
29
|
+
setCurrentIndex((index) => (index > 0 ? index - 1 : index));
|
|
30
|
+
};
|
|
31
|
+
const goToNextVideo = () => {
|
|
32
|
+
setCurrentIndex((index) => (index < videos.length - 1 ? index + 1 : index));
|
|
33
|
+
};
|
|
34
|
+
const currentVideo = videos[currentIndex];
|
|
35
|
+
const { snapshotWidth = 0, snapshotHeight = 0 } = currentVideo || {};
|
|
36
|
+
const hasSnapshotSize = snapshotWidth > 0 && snapshotHeight > 0;
|
|
37
|
+
return (_jsx(Modal, { open: open, onCancel: onClose, footer: null, centered: true, destroyOnHidden: true, getContainer: false, closeIcon: _jsx("span", { className: CLOSE_BUTTON_CLASS, children: _jsx(Icon, { icon: "close-b", size: 18 }) }), styles: {
|
|
38
|
+
content: {
|
|
39
|
+
padding: 0,
|
|
40
|
+
},
|
|
41
|
+
}, children: _jsxs("div", { className: "relative overflow-hidden rounded-[8px] bg-black", children: [_jsx("video", { src: currentVideo === null || currentVideo === void 0 ? void 0 : currentVideo.videoUrl, poster: currentVideo === null || currentVideo === void 0 ? void 0 : currentVideo.snapshotUrl, controls: true, autoPlay: true, className: clsx("block max-h-[80vh] w-full object-contain", !hasSnapshotSize && "aspect-video"), style: hasSnapshotSize
|
|
42
|
+
? { aspectRatio: `${snapshotWidth} / ${snapshotHeight}` }
|
|
43
|
+
: undefined }, currentVideo === null || currentVideo === void 0 ? void 0 : currentVideo.id), videos.length > 1 && (_jsxs("span", { className: "absolute left-3 top-3 rounded-full bg-black/60 px-2 py-0.5 text-[12px] font-medium leading-[1.6] text-white", children: [currentIndex + 1, " / ", videos.length] })), hasPreviousVideo && (_jsx("button", { type: "button", "aria-label": t("previous_video"), "data-testid": "video-player-previous", className: clsx(NAVIGATION_BUTTON_CLASS, "left-2"), onClick: goToPreviousVideo, children: _jsx(Icon, { icon: "angle-left-o", size: 24 }) })), hasNextVideo && (_jsx("button", { type: "button", "aria-label": t("next_video"), "data-testid": "video-player-next", className: clsx(NAVIGATION_BUTTON_CLASS, "right-2"), onClick: goToNextVideo, children: _jsx(Icon, { icon: "angle-right-o", size: 24 }) }))] }) }));
|
|
44
|
+
};
|
|
45
|
+
export default VideoPlayerModal;
|