@droppii-org/chat-sdk 0.7.6 → 0.7.7

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.
@@ -0,0 +1,10 @@
1
+ import type { SearchPublicGroupInfo } from "@droppii-org/wasm-client-sdk";
2
+ interface ContactSearchCommunitySectionProps {
3
+ groups: SearchPublicGroupInfo[];
4
+ total: number;
5
+ onOpenGroup: (groupID: string) => void;
6
+ onViewAll: () => void;
7
+ }
8
+ declare const ContactSearchCommunitySection: ({ groups, total, onOpenGroup, onViewAll, }: ContactSearchCommunitySectionProps) => import("react/jsx-runtime").JSX.Element;
9
+ export default ContactSearchCommunitySection;
10
+ //# sourceMappingURL=ContactSearchCommunitySection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContactSearchCommunitySection.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-inbox/contact-search/ContactSearchCommunitySection.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAM1E,UAAU,kCAAkC;IAC1C,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAGD,QAAA,MAAM,6BAA6B,GAAI,4CAKpC,kCAAkC,4CAwBpC,CAAC;AAEF,eAAe,6BAA6B,CAAC"}
@@ -0,0 +1,15 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useTranslation } from "react-i18next";
4
+ import ContactSearchSectionHeader from "./ContactSearchSectionHeader";
5
+ import ContactSearchViewMoreBar from "./ContactSearchViewMoreBar";
6
+ import PublicGroupResultItem from "./PublicGroupResultItem";
7
+ import { CONTACT_SEARCH_PREVIEW_LIMIT } from "./constants";
8
+ // `Tất cả` preview of the `Cộng đồng` tab: same cards, capped like contacts.
9
+ const ContactSearchCommunitySection = ({ groups, total, onOpenGroup, onViewAll, }) => {
10
+ const { t } = useTranslation("conversation-inbox");
11
+ // Guards a short server total so a full page still gets its hand-off.
12
+ const matchCount = Math.max(total, groups.length);
13
+ return (_jsxs("section", { "data-cp-name": "ContactSearchCommunitySection", children: [_jsx(ContactSearchSectionHeader, { title: t("contact_search.tabs.community"), count: matchCount || undefined, onSelect: onViewAll }), groups.slice(0, CONTACT_SEARCH_PREVIEW_LIMIT).map((group) => (_jsx(PublicGroupResultItem, { group: group, onOpen: onOpenGroup }, group.groupID))), matchCount > CONTACT_SEARCH_PREVIEW_LIMIT && (_jsx(ContactSearchViewMoreBar, { onSelect: onViewAll }))] }));
14
+ };
15
+ export default ContactSearchCommunitySection;
@@ -1 +1 @@
1
- {"version":3,"file":"ContactSearchPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-inbox/contact-search/ContactSearchPanel.tsx"],"names":[],"mappings":"AA+BA,UAAU,uBAAuB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,kBAAkB,GAAI,mBAAmB,uBAAuB,4CA2TrE,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"ContactSearchPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-inbox/contact-search/ContactSearchPanel.tsx"],"names":[],"mappings":"AAgCA,UAAU,uBAAuB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,kBAAkB,GAAI,mBAAmB,uBAAuB,4CA2TrE,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -10,6 +10,7 @@ import { useChatNavigate } from "../../../hooks/conversation/useChatNavigate";
10
10
  import { useSearchContacts } from "../../../hooks/search/useSearchContacts";
11
11
  import { useSearchPublicGroups } from "../../../hooks/search/useSearchPublicGroups";
12
12
  import useConversationStore from "../../../store/conversation";
13
+ import ContactSearchCommunitySection from "./ContactSearchCommunitySection";
13
14
  import ContactSearchFilters from "./ContactSearchFilters";
14
15
  import ContactSearchResultItem from "./ContactSearchResultItem";
15
16
  import ContactSearchSectionHeader from "./ContactSearchSectionHeader";
@@ -46,9 +47,12 @@ const ContactSearchPanel = ({ open, onClose }) => {
46
47
  type: activeType,
47
48
  enabled: open,
48
49
  });
50
+ // `Tất cả` and `Cộng đồng` share one query, so switching tabs hits the cache.
49
51
  const publicGroups = useSearchPublicGroups({
50
52
  searchTerm: trimmedKeyword,
51
- enabled: open && activeTab === ContactSearchTab.Community,
53
+ enabled: open &&
54
+ (activeTab === ContactSearchTab.All ||
55
+ activeTab === ContactSearchTab.Community),
52
56
  });
53
57
  const handleOpenGroup = (groupID) => {
54
58
  const target = { groupId: groupID };
@@ -72,46 +76,53 @@ const ContactSearchPanel = ({ open, onClose }) => {
72
76
  setActiveType(undefined);
73
77
  };
74
78
  const renderContactItems = (items) => items.map((contact) => (_jsx(ContactSearchResultItem, { contact: contact, searchTerm: trimmedKeyword, onSelect: handleSelectContact }, contact.id)));
75
- // Every contact tab shares the same not-ready states before it can render
76
- // results; returns null once there is a list to show.
79
+ const renderInitialState = (testId = "contact-search-initial") => (_jsx(ContactSearchStateMessage, { testId: testId, children: t("contact_search.initial") }));
80
+ const renderLoadingState = () => (_jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Spin, {}) }));
81
+ const renderFailedState = (testId = "contact-search-failed") => (_jsx(ContactSearchStateMessage, { testId: testId, children: t("contact_search.failed") }));
82
+ const renderNoResultState = (testId = "contact-search-no-result") => (_jsxs(ContactSearchStateMessage, { testId: testId, children: [t("contact_search.no_result_title"), _jsx("br", {}), t("contact_search.no_result_hint")] }));
83
+ // The `Liên hệ` tab's not-ready states before it can render results;
84
+ // returns null once there is a list to show.
77
85
  const renderPlaceholderState = () => {
78
- if (!trimmedKeyword) {
79
- return (_jsx(ContactSearchStateMessage, { testId: "contact-search-initial", children: t("contact_search.initial") }));
80
- }
81
- if (isLoading) {
82
- return (_jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Spin, {}) }));
83
- }
84
- if (isError) {
85
- return (_jsx(ContactSearchStateMessage, { testId: "contact-search-failed", children: t("contact_search.failed") }));
86
- }
87
- if (contacts.length === 0) {
88
- return (_jsxs(ContactSearchStateMessage, { testId: "contact-search-no-result", children: [t("contact_search.no_result_title"), _jsx("br", {}), t("contact_search.no_result_hint")] }));
89
- }
86
+ if (!trimmedKeyword)
87
+ return renderInitialState();
88
+ if (isLoading)
89
+ return renderLoadingState();
90
+ if (isError)
91
+ return renderFailedState();
92
+ if (contacts.length === 0)
93
+ return renderNoResultState();
90
94
  return null;
91
95
  };
92
- // `Tất cả` only previews the contact section and hands off to `Liên hệ`;
93
- // the message section belongs to the message-search ticket.
96
+ // `Tất cả` previews contacts, then public groups; the message section
97
+ // belongs to the message-search ticket and goes between the two.
94
98
  const renderAllTab = () => {
95
- const placeholder = renderPlaceholderState();
96
- if (placeholder)
97
- return placeholder;
98
- const hasMore = contacts.length > CONTACT_SEARCH_PREVIEW_LIMIT || !!hasNextPage;
99
+ if (!trimmedKeyword)
100
+ return renderInitialState();
101
+ if (isLoading || publicGroups.isLoading)
102
+ return renderLoadingState();
103
+ if (isError && publicGroups.isError)
104
+ return renderFailedState();
105
+ // A source that failed drops its own section instead of the whole tab.
106
+ const showContacts = !isError && contacts.length > 0;
107
+ const showGroups = !publicGroups.isError && publicGroups.groups.length > 0;
108
+ if (!showContacts && !showGroups)
109
+ return renderNoResultState();
110
+ const hasMoreContacts = contacts.length > CONTACT_SEARCH_PREVIEW_LIMIT || !!hasNextPage;
99
111
  const openContactsTab = () => handleChangeTab(ContactSearchTab.Contacts);
100
- return (_jsxs("div", { className: "flex-1 overflow-y-auto", children: [_jsx(ContactSearchSectionHeader, { title: t("contact_search.tabs.contacts"), count: totalElements || undefined, onSelect: openContactsTab }), renderContactItems(contacts.slice(0, CONTACT_SEARCH_PREVIEW_LIMIT)), hasMore && _jsx(ContactSearchViewMoreBar, { onSelect: openContactsTab })] }));
112
+ return (_jsxs("div", { className: "flex-1 overflow-y-auto", children: [showContacts && (_jsxs("section", { children: [_jsx(ContactSearchSectionHeader, { title: t("contact_search.tabs.contacts"), count: totalElements || undefined, onSelect: openContactsTab }), renderContactItems(contacts.slice(0, CONTACT_SEARCH_PREVIEW_LIMIT)), hasMoreContacts && (_jsx(ContactSearchViewMoreBar, { onSelect: openContactsTab }))] })), showGroups && (_jsx(ContactSearchCommunitySection, { groups: publicGroups.groups, total: publicGroups.total, onOpenGroup: handleOpenGroup, onViewAll: () => handleChangeTab(ContactSearchTab.Community) }))] }));
101
113
  };
102
114
  const renderContactList = () => (_jsx("div", { id: SCROLL_TARGET_ID, className: "flex-1 overflow-y-auto", children: _jsx(InfiniteScroll, { dataLength: contacts.length, next: fetchNextPage, hasMore: !!hasNextPage, scrollableTarget: SCROLL_TARGET_ID, loader: isFetchingNextPage ? (_jsx("div", { className: "flex items-center justify-center py-3", children: _jsx(Spin, { size: "small" }) })) : null, children: renderContactItems(contacts) }) }));
103
115
  const renderCommunityTab = () => {
104
116
  if (!trimmedKeyword) {
105
- return (_jsx(ContactSearchStateMessage, { testId: "contact-search-community-initial", children: t("contact_search.initial") }));
106
- }
107
- if (publicGroups.isLoading) {
108
- return (_jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Spin, {}) }));
117
+ return renderInitialState("contact-search-community-initial");
109
118
  }
119
+ if (publicGroups.isLoading)
120
+ return renderLoadingState();
110
121
  if (publicGroups.isError) {
111
- return (_jsx(ContactSearchStateMessage, { testId: "contact-search-community-failed", children: t("contact_search.failed") }));
122
+ return renderFailedState("contact-search-community-failed");
112
123
  }
113
124
  if (publicGroups.groups.length === 0) {
114
- return (_jsxs(ContactSearchStateMessage, { testId: "contact-search-community-no-result", children: [t("contact_search.no_result_title"), _jsx("br", {}), t("contact_search.no_result_hint")] }));
125
+ return renderNoResultState("contact-search-community-no-result");
115
126
  }
116
127
  return (_jsx("div", { id: COMMUNITY_SCROLL_TARGET_ID, className: "flex-1 overflow-y-auto", children: _jsx(InfiniteScroll, { dataLength: publicGroups.groups.length, next: publicGroups.fetchNextPage, hasMore: !!publicGroups.hasNextPage, scrollableTarget: COMMUNITY_SCROLL_TARGET_ID, loader: publicGroups.isFetchingNextPage ? (_jsx("div", { className: "flex items-center justify-center py-3", children: _jsx(Spin, { size: "small" }) })) : null, children: publicGroups.groups.map((group) => (_jsx(PublicGroupResultItem, { group: group, onOpen: handleOpenGroup }, group.groupID))) }) }));
117
128
  };
@@ -2,8 +2,8 @@ import { ContactSearchType } from "../../../types/dto";
2
2
  export const CONTACT_SEARCH_DEBOUNCE_MS = 300;
3
3
  export const CONTACT_SEARCH_ICON_SIZE = 18;
4
4
  export const CONTACT_SEARCH_AVATAR_SIZE = 48;
5
- // `Tất cả` shows a capped preview of the contact section before handing the
6
- // seller over to the full `Liên hệ` tab.
5
+ // `Tất cả` caps each section's preview (contacts, public groups) before handing
6
+ // the seller over to the full `Liên hệ` / `Cộng đồng` tab.
7
7
  export const CONTACT_SEARCH_PREVIEW_LIMIT = 5;
8
8
  export var ContactSearchTab;
9
9
  (function (ContactSearchTab) {
@@ -6,6 +6,7 @@ interface UseSearchPublicGroupsParams {
6
6
  }
7
7
  export declare const useSearchPublicGroups: ({ searchTerm, enabled, pageSize, }: UseSearchPublicGroupsParams) => {
8
8
  groups: SearchPublicGroupInfo[];
9
+ total: number;
9
10
  fetchNextPage: (options?: import("@tanstack/react-query").FetchNextPageOptions) => Promise<import("@tanstack/react-query").InfiniteQueryObserverResult<import("@tanstack/react-query").InfiniteData<import("@droppii-org/wasm-client-sdk").SearchPublicGroupsResult, unknown>, Error>>;
10
11
  hasNextPage: boolean;
11
12
  isFetchingNextPage: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"useSearchPublicGroups.d.ts","sourceRoot":"","sources":["../../../src/hooks/search/useSearchPublicGroups.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,8BAA8B,CAAC;AAKtC,UAAU,2BAA2B;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAKD,eAAO,MAAM,qBAAqB,GAAI,oCAInC,2BAA2B;;;;;;;CAqD7B,CAAC"}
1
+ {"version":3,"file":"useSearchPublicGroups.d.ts","sourceRoot":"","sources":["../../../src/hooks/search/useSearchPublicGroups.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,8BAA8B,CAAC;AAKtC,UAAU,2BAA2B;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAKD,eAAO,MAAM,qBAAqB,GAAI,oCAInC,2BAA2B;;;;;;;;CA0D7B,CAAC"}
@@ -9,6 +9,7 @@ import { PAGE_SIZE } from "../../constants";
9
9
  // same call chat-mobile uses for its `Cộng đồng` tab, so both platforms return
10
10
  // the identical public-group set (joined and not yet joined).
11
11
  export const useSearchPublicGroups = ({ searchTerm, enabled = true, pageSize = PAGE_SIZE.medium, }) => {
12
+ var _a, _b, _c;
12
13
  const trimmedSearchTerm = searchTerm.trim();
13
14
  const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isError } = useInfiniteQuery({
14
15
  initialPageParam: 0,
@@ -29,6 +30,9 @@ export const useSearchPublicGroups = ({ searchTerm, enabled = true, pageSize = P
29
30
  return fetched < ((_a = lastPage === null || lastPage === void 0 ? void 0 : lastPage.total) !== null && _a !== void 0 ? _a : 0) ? fetched : undefined;
30
31
  },
31
32
  enabled: enabled && trimmedSearchTerm !== "",
33
+ // `Tất cả` waits for this query, so a failing engine must not hold it
34
+ // through the default three retries.
35
+ retry: 1,
32
36
  });
33
37
  // A group can repeat across offsets while the backing list shifts.
34
38
  const groups = useMemo(() => {
@@ -46,6 +50,8 @@ export const useSearchPublicGroups = ({ searchTerm, enabled = true, pageSize = P
46
50
  }, [data]);
47
51
  return {
48
52
  groups,
53
+ // Every page carries the same server-side total for the keyword.
54
+ total: (_c = (_b = (_a = data === null || data === void 0 ? void 0 : data.pages) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.total) !== null && _c !== void 0 ? _c : 0,
49
55
  fetchNextPage,
50
56
  hasNextPage,
51
57
  isFetchingNextPage,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@droppii-org/chat-sdk",
3
- "version": "0.7.6",
3
+ "version": "0.7.7",
4
4
  "description": "Droppii React Chat SDK",
5
5
  "repository": {
6
6
  "type": "git",