@droppii-org/chat-sdk 0.7.8 → 0.7.9

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.
@@ -9,7 +9,9 @@ interface ThreadProfilePanelProps {
9
9
  showActions?: boolean;
10
10
  /** Wires the "Tìm tin nhắn" quick action. */
11
11
  onSearch?: () => void;
12
+ /** Drops "Tìm tin nhắn" where the host has search but this viewer can't use it. */
13
+ hideSearch?: boolean;
12
14
  }
13
- declare const ThreadProfilePanel: ({ open, conversation, onClose, onSelectResource, showActions, onSearch, }: ThreadProfilePanelProps) => import("react/jsx-runtime").JSX.Element;
15
+ declare const ThreadProfilePanel: ({ open, conversation, onClose, onSelectResource, showActions, onSearch, hideSearch, }: ThreadProfilePanelProps) => import("react/jsx-runtime").JSX.Element;
14
16
  export default ThreadProfilePanel;
15
17
  //# sourceMappingURL=ThreadProfilePanel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadProfilePanel.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ThreadProfilePanel.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAc7E,UAAU,uBAAuB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,gBAAgB,CAAC;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACxD,iEAAiE;IACjE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAID,QAAA,MAAM,kBAAkB,GAAI,2EAOzB,uBAAuB,4CAmJzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"ThreadProfilePanel.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ThreadProfilePanel.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAc7E,UAAU,uBAAuB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,gBAAgB,CAAC;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACxD,iEAAiE;IACjE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,mFAAmF;IACnF,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAID,QAAA,MAAM,kBAAkB,GAAI,uFAQzB,uBAAuB,4CAoJzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -17,7 +17,7 @@ import ThreadProfileQuickActions from "./profile/ThreadProfileQuickActions";
17
17
  import ThreadProfileActionRows from "./profile/ThreadProfileActionRows";
18
18
  const PROFILE_AVATAR_SIZE = 96;
19
19
  const PERSON_PEER_TYPES = [PeerType.USER, PeerType.CUSTOMER];
20
- const ThreadProfilePanel = ({ open, conversation, onClose, onSelectResource, showActions = false, onSearch, }) => {
20
+ const ThreadProfilePanel = ({ open, conversation, onClose, onSelectResource, showActions = false, onSearch, hideSearch = false, }) => {
21
21
  var _a;
22
22
  const { t } = useTranslation("conversation-inbox");
23
23
  const { displayName, avatarUrl, peerType, badgeLabel } = useConversationPeerIdentity(conversation);
@@ -33,6 +33,6 @@ const ThreadProfilePanel = ({ open, conversation, onClose, onSelectResource, sho
33
33
  });
34
34
  return (_jsxs(Drawer, { open: open, onClose: onClose, placement: "right", closeIcon: false, width: "100%", "data-cp-name": "ThreadProfilePanel", styles: {
35
35
  body: { padding: 0 },
36
- }, getContainer: false, forceRender: true, destroyOnHidden: true, children: [_jsxs("div", { className: clsx("flex h-full flex-col bg-gray-25", showActions && "overflow-y-auto"), children: [_jsxs("div", { className: clsx("flex w-full items-center bg-white p-2", showActions && "sticky top-0 z-10 shrink-0"), children: [_jsx("button", { type: "button", "data-testid": "thread-profile-back", className: "flex shrink-0 cursor-pointer items-center justify-center p-2 text-[var(--dc-color-nav-icon)]", 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("thread_detail.options_title") })] }), _jsxs("div", { className: "flex flex-col items-center gap-2 bg-white px-0 py-2", children: [_jsxs("div", { className: "relative h-24 w-24 shrink-0", children: [_jsx(Avatar, { size: PROFILE_AVATAR_SIZE, src: avatarUrl || undefined, alt: displayName, className: "uppercase", children: (displayName === null || displayName === void 0 ? void 0 : displayName.charAt(0)) || "A" }), badgeLabel && (_jsx("span", { className: clsx("absolute bottom-1 right-0 flex h-[17px] items-center justify-center rounded-md border border-white px-1 text-[10px] font-semibold leading-3 text-white shadow-badge-inset", resolvePeerBadgeColor(peerType)), children: badgeLabel }))] }), _jsx("p", { className: "w-full truncate text-center text-[18px] font-bold leading-[1.6] text-gray-900", children: displayName }), showActions && (_jsx(ThreadProfileQuickActions, { conversation: conversation, isViewOnly: isViewOnly, onSearch: onSearch }, conversation.conversationID))] }), _jsx("div", { className: "flex flex-col px-3 py-1.5", children: _jsx(MediaEntryCard, { onSelect: onSelectResource }) }), showActions && (_jsx(ThreadProfileActionRows, { conversation: conversation, displayName: displayName, showPersonRows: showPersonRows, isTargetActive: isTargetActive, isViewOnly: isViewOnly, canLeaveGroup: canLeaveGroup, onLeaveGroup: openLeaveGroup }, conversation.conversationID)), !showActions && canLeaveGroup && (_jsx("div", { className: "flex flex-col px-3 py-1.5", children: _jsx("div", { className: "overflow-hidden rounded-xl bg-white", children: _jsxs("button", { type: "button", "data-testid": "thread-profile-leave-group", className: "flex w-full cursor-pointer items-center gap-2 p-4 text-danger-400", onClick: openLeaveGroup, children: [_jsx(Icon, { icon: "logout-o", size: 24 }), _jsx("span", { className: "text-[16px] font-medium leading-[1.6]", children: t("thread_detail.leave_group.action") })] }) }) }))] }), _jsx(LeaveGroupConfirmModal, { open: isConfirmOpen, loading: isLeaving, isPublicGroup: isPublicGroup, onCancel: closeConfirm, onConfirm: confirmLeaveGroup }), _jsx(LeaveGroupBlockedModal, { open: isBlockedOpen, onClose: closeBlocked })] }));
36
+ }, getContainer: false, forceRender: true, destroyOnHidden: true, children: [_jsxs("div", { className: clsx("flex h-full flex-col bg-gray-25", showActions && "overflow-y-auto"), children: [_jsxs("div", { className: clsx("flex w-full items-center bg-white p-2", showActions && "sticky top-0 z-10 shrink-0"), children: [_jsx("button", { type: "button", "data-testid": "thread-profile-back", className: "flex shrink-0 cursor-pointer items-center justify-center p-2 text-[var(--dc-color-nav-icon)]", 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("thread_detail.options_title") })] }), _jsxs("div", { className: "flex flex-col items-center gap-2 bg-white px-0 py-2", children: [_jsxs("div", { className: "relative h-24 w-24 shrink-0", children: [_jsx(Avatar, { size: PROFILE_AVATAR_SIZE, src: avatarUrl || undefined, alt: displayName, className: "uppercase", children: (displayName === null || displayName === void 0 ? void 0 : displayName.charAt(0)) || "A" }), badgeLabel && (_jsx("span", { className: clsx("absolute bottom-1 right-0 flex h-[17px] items-center justify-center rounded-md border border-white px-1 text-[10px] font-semibold leading-3 text-white shadow-badge-inset", resolvePeerBadgeColor(peerType)), children: badgeLabel }))] }), _jsx("p", { className: "w-full truncate text-center text-[18px] font-bold leading-[1.6] text-gray-900", children: displayName }), showActions && (_jsx(ThreadProfileQuickActions, { conversation: conversation, isViewOnly: isViewOnly, onSearch: onSearch, hideSearch: hideSearch }, conversation.conversationID))] }), _jsx("div", { className: "flex flex-col px-3 py-1.5", children: _jsx(MediaEntryCard, { onSelect: onSelectResource }) }), showActions && (_jsx(ThreadProfileActionRows, { conversation: conversation, displayName: displayName, showPersonRows: showPersonRows, isTargetActive: isTargetActive, isViewOnly: isViewOnly, canLeaveGroup: canLeaveGroup, onLeaveGroup: openLeaveGroup }, conversation.conversationID)), !showActions && canLeaveGroup && (_jsx("div", { className: "flex flex-col px-3 py-1.5", children: _jsx("div", { className: "overflow-hidden rounded-xl bg-white", children: _jsxs("button", { type: "button", "data-testid": "thread-profile-leave-group", className: "flex w-full cursor-pointer items-center gap-2 p-4 text-danger-400", onClick: openLeaveGroup, children: [_jsx(Icon, { icon: "logout-o", size: 24 }), _jsx("span", { className: "text-[16px] font-medium leading-[1.6]", children: t("thread_detail.leave_group.action") })] }) }) }))] }), _jsx(LeaveGroupConfirmModal, { open: isConfirmOpen, loading: isLeaving, isPublicGroup: isPublicGroup, onCancel: closeConfirm, onConfirm: confirmLeaveGroup }), _jsx(LeaveGroupBlockedModal, { open: isBlockedOpen, onClose: closeBlocked })] }));
37
37
  };
38
38
  export default ThreadProfilePanel;
@@ -5,7 +5,9 @@ interface ThreadProfileQuickActionsProps {
5
5
  isViewOnly: boolean;
6
6
  /** Opens search in this thread; without it the entry stays UI-only. */
7
7
  onSearch?: () => void;
8
+ /** The host has thread search but this viewer can't use it here: drop the entry. */
9
+ hideSearch?: boolean;
8
10
  }
9
- declare const ThreadProfileQuickActions: ({ conversation, isViewOnly, onSearch, }: ThreadProfileQuickActionsProps) => import("react/jsx-runtime").JSX.Element | null;
11
+ declare const ThreadProfileQuickActions: ({ conversation, isViewOnly, onSearch, hideSearch, }: ThreadProfileQuickActionsProps) => import("react/jsx-runtime").JSX.Element | null;
10
12
  export default ThreadProfileQuickActions;
11
13
  //# sourceMappingURL=ThreadProfileQuickActions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadProfileQuickActions.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-detail/profile/ThreadProfileQuickActions.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AA6CrE,UAAU,8BAA8B;IACtC,YAAY,EAAE,gBAAgB,CAAC;IAC/B,2FAA2F;IAC3F,UAAU,EAAE,OAAO,CAAC;IACpB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,QAAA,MAAM,yBAAyB,GAAI,yCAIhC,8BAA8B,mDAkDhC,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
1
+ {"version":3,"file":"ThreadProfileQuickActions.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-detail/profile/ThreadProfileQuickActions.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AA6CrE,UAAU,8BAA8B;IACtC,YAAY,EAAE,gBAAgB,CAAC;IAC/B,2FAA2F;IAC3F,UAAU,EAAE,OAAO,CAAC;IACpB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,oFAAoF;IACpF,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,QAAA,MAAM,yBAAyB,GAAI,qDAKhC,8BAA8B,mDAoDhC,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
@@ -9,7 +9,7 @@ import { isConversationMuted, useMuteConversation, } from "../../../hooks/sessio
9
9
  const QuickAction = ({ icon, label, active, testId, onClick, }) => (_jsxs("button", { type: "button", "data-testid": testId, className: "flex w-[104px] cursor-pointer flex-col items-center gap-1 px-3 py-2", onClick: onClick, children: [_jsx("span", { className: clsx("flex h-10 w-10 items-center justify-center rounded-full", active
10
10
  ? "bg-[var(--dc-color-primary)] text-white"
11
11
  : "bg-gray-25 text-gray-900"), children: _jsx(Icon, { icon: icon, size: 18 }) }), _jsx("span", { className: "whitespace-nowrap text-center text-[13px] font-medium leading-[1.6] text-gray-900", children: label })] }));
12
- const ThreadProfileQuickActions = ({ conversation, isViewOnly, onSearch, }) => {
12
+ const ThreadProfileQuickActions = ({ conversation, isViewOnly, onSearch, hideSearch = false, }) => {
13
13
  const { t } = useTranslation("conversation-inbox");
14
14
  const { muteConversation, unmuteConversation, isPending } = useMuteConversation();
15
15
  const [isConfirmOpen, setIsConfirmOpen] = useState(false);
@@ -29,7 +29,7 @@ const ThreadProfileQuickActions = ({ conversation, isViewOnly, onSearch, }) => {
29
29
  };
30
30
  if (isViewOnly)
31
31
  return null;
32
- return (_jsxs("div", { className: "flex items-center justify-center gap-2", children: [_jsx(QuickAction, { icon: "search-o", label: t("thread_detail.profile.search"), testId: "thread-profile-search", onClick: onSearch }), _jsx(QuickAction, { icon: isMuted ? "bell-b" : "bell-off-o", label: t(isMuted
32
+ return (_jsxs("div", { className: "flex items-center justify-center gap-2", children: [!hideSearch && (_jsx(QuickAction, { icon: "search-o", label: t("thread_detail.profile.search"), testId: "thread-profile-search", onClick: onSearch })), _jsx(QuickAction, { icon: isMuted ? "bell-b" : "bell-off-o", label: t(isMuted
33
33
  ? "thread_card.mute.unmute_action"
34
34
  : "thread_card.mute.mute_action"), active: isMuted, testId: "thread-profile-mute", onClick: handleMuteClick }), _jsx(MuteConversationConfirmModal, { open: isConfirmOpen, loading: isPending, onCancel: () => setIsConfirmOpen(false), onConfirm: handleConfirmMute })] }));
35
35
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useGlobalEvent.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useGlobalEvent.ts"],"names":[],"mappings":"AAiDA,eAAO,MAAM,cAAc,YAqnB1B,CAAC"}
1
+ {"version":3,"file":"useGlobalEvent.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useGlobalEvent.ts"],"names":[],"mappings":"AAiDA,eAAO,MAAM,cAAc,YAwnB1B,CAAC"}
@@ -431,6 +431,9 @@ export const useGlobalEvent = () => {
431
431
  });
432
432
  void refreshCurrentGroupState();
433
433
  }
434
+ void queryClient.invalidateQueries({
435
+ queryKey: [QUERY_KEYS.IS_JOINED_GROUP, data.groupID],
436
+ });
434
437
  };
435
438
  const setIMListener = () => {
436
439
  //account
@@ -1 +1 @@
1
- {"version":3,"file":"useJoinPublicGroup.d.ts","sourceRoot":"","sources":["../../../src/hooks/group/useJoinPublicGroup.ts"],"names":[],"mappings":"AAOA,UAAU,oBAAoB;IAC5B,OAAO,EAAE,MAAM,CAAC;CACjB;AAID,eAAO,MAAM,kBAAkB,wGAa9B,CAAC"}
1
+ {"version":3,"file":"useJoinPublicGroup.d.ts","sourceRoot":"","sources":["../../../src/hooks/group/useJoinPublicGroup.ts"],"names":[],"mappings":"AAQA,UAAU,oBAAoB;IAC5B,OAAO,EAAE,MAAM,CAAC;CACjB;AAID,eAAO,MAAM,kBAAkB,wGAoB9B,CAAC"}
@@ -1,11 +1,13 @@
1
1
  "use client";
2
- import { useMutation } from "@tanstack/react-query";
2
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
3
3
  import { GroupJoinSource } from "@droppii-org/wasm-client-sdk";
4
4
  import { DChatSDK } from "../../constants/sdk";
5
+ import { QUERY_KEYS } from "../../services/query";
5
6
  import emitter from "../../utils/events";
6
7
  // Joining straight from search results, matching chat-mobile's GroupItem —
7
8
  // GroupJoinSource.Search is what BE uses to attribute the join.
8
9
  export const useJoinPublicGroup = () => {
10
+ const queryClient = useQueryClient();
9
11
  return useMutation({
10
12
  mutationFn: async ({ groupID }) => {
11
13
  const { data } = await DChatSDK.joinGroup({
@@ -16,5 +18,11 @@ export const useJoinPublicGroup = () => {
16
18
  emitter.emit("GROUP_MEMBERS_CHANGED");
17
19
  return data;
18
20
  },
21
+ // Joining a group already cached locally fires no join event, so member-only UI reads this.
22
+ onSuccess: async (_data, { groupID }) => {
23
+ const queryKey = [QUERY_KEYS.IS_JOINED_GROUP, groupID];
24
+ await queryClient.cancelQueries({ queryKey });
25
+ queryClient.setQueryData(queryKey, true);
26
+ },
19
27
  });
20
28
  };
@@ -23,6 +23,7 @@ export declare const useMessage: (conversationId?: string, searchClientMsgID?: s
23
23
  moreNewLoading: boolean;
24
24
  getMoreNewMessages: (loadMore?: any) => Promise<void>;
25
25
  refreshLatestMessages: () => Promise<void>;
26
+ notifyRevokedTarget: (clientMsgID: string) => void;
26
27
  };
27
28
  export declare const pushNewMessage: (message: MessageItem) => void;
28
29
  export declare const updateOneMessage: (message: MessageItem) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"useMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useMessage.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,WAAW,EACX,WAAW,EAEZ,MAAM,8BAA8B,CAAC;AAkBtC,eAAO,MAAM,kBAAkB,eAkB9B,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,SAAS,WAAW,EACpB,OAAO,OAAO,EACd,eAAe;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5E,eAAe,MAAM,KACpB,OA4BF,CAAC;AAEF,eAAO,MAAM,UAAU,GACrB,iBAAiB,MAAM,EACvB,oBAAoB,MAAM,EAC1B,iBAAc,EACd,uBAAqB,EAGrB,yBAAyB,MAAM,IAAI;;;;;qBAed,WAAW,EAAE;;;;;;qBAAb,WAAW,EAAE;;;;;;;CAoZnC,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,SAAS,WAAW,SACpB,CAAC;AAChC,eAAO,MAAM,gBAAgB,GAAI,SAAS,WAAW,SACpB,CAAC;AAClC,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,SACf,CAAC;AAEtC,eAAO,MAAM,kBAAkB,GAC7B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,EAC1B,YAAY,CAAC,SAAS,EAAE,WAAW,KAAK,OAAO,KAC9C,WAAW,GAAG,SAqBhB,CAAC;AAMF,eAAO,MAAM,oBAAoB,GAC/B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,KACzB,WAAW,GAAG,SAQhB,CAAC;AAmBF,eAAO,MAAM,mBAAmB,GAC9B,SAAS,WAAW,EAAE,EACtB,SAAS,WAAW,EAAE,kBAgBvB,CAAC"}
1
+ {"version":3,"file":"useMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useMessage.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,WAAW,EACX,WAAW,EAEZ,MAAM,8BAA8B,CAAC;AAkBtC,eAAO,MAAM,kBAAkB,eAkB9B,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,SAAS,WAAW,EACpB,OAAO,OAAO,EACd,eAAe;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5E,eAAe,MAAM,KACpB,OA4BF,CAAC;AAEF,eAAO,MAAM,UAAU,GACrB,iBAAiB,MAAM,EACvB,oBAAoB,MAAM,EAC1B,iBAAc,EACd,uBAAqB,EAGrB,yBAAyB,MAAM,IAAI;;;;;qBAed,WAAW,EAAE;;;;;;qBAAb,WAAW,EAAE;;;;;;;uCAwBlB,MAAM;CAmYvB,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,SAAS,WAAW,SACpB,CAAC;AAChC,eAAO,MAAM,gBAAgB,GAAI,SAAS,WAAW,SACpB,CAAC;AAClC,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,SACf,CAAC;AAEtC,eAAO,MAAM,kBAAkB,GAC7B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,EAC1B,YAAY,CAAC,SAAS,EAAE,WAAW,KAAK,OAAO,KAC9C,WAAW,GAAG,SAqBhB,CAAC;AAMF,eAAO,MAAM,oBAAoB,GAC/B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,KACzB,WAAW,GAAG,SAQhB,CAAC;AAmBF,eAAO,MAAM,mBAAmB,GAC9B,SAAS,WAAW,EAAE,EACtB,SAAS,WAAW,EAAE,kBAgBvB,CAAC"}
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef, useState } from "react";
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { ViewType, MessageType, } from "@droppii-org/wasm-client-sdk";
3
3
  import { DChatSDK } from "../../constants/sdk";
4
4
  import { PAGE_SIZE } from "../../constants";
@@ -87,6 +87,17 @@ onSearchMessageRevoked) => {
87
87
  // without this the user sees it stacked. Keyed by conversation + target so
88
88
  // jumping to a different revoked message still notifies.
89
89
  const toastedRevokedTargetRef = useRef(null);
90
+ // Also used by the scroll guard for a search hit recalled since the results came back.
91
+ const notifyRevokedTarget = useCallback((clientMsgID) => {
92
+ const revokedTargetKey = `${conversationIdRef.current}:${clientMsgID}`;
93
+ if (toastedRevokedTargetRef.current === revokedTargetKey)
94
+ return;
95
+ toastedRevokedTargetRef.current = revokedTargetKey;
96
+ if (onSearchMessageRevoked)
97
+ onSearchMessageRevoked();
98
+ else
99
+ antdMessage.info(t("message_revoked_or_deleted"));
100
+ }, [conversationIdRef, onSearchMessageRevoked, t]);
90
101
  const { loading: moreOldLoading, runAsync: getMoreOldMessages } = useRequest(async (loadMore = true) => {
91
102
  var _a, _b, _c, _d, _e;
92
103
  const reqConversationID = conversationId;
@@ -208,14 +219,7 @@ onSearchMessageRevoked) => {
208
219
  // list with a window centred on a tombstone is worse than not moving.
209
220
  const targetMessage = currentMessages.find((item) => item.clientMsgID === searchClientMsgID);
210
221
  if ((targetMessage === null || targetMessage === void 0 ? void 0 : targetMessage.contentType) === MessageType.RevokeMessage) {
211
- const revokedTargetKey = `${reqConversationID}:${searchClientMsgID}`;
212
- if (toastedRevokedTargetRef.current !== revokedTargetKey) {
213
- toastedRevokedTargetRef.current = revokedTargetKey;
214
- if (onSearchMessageRevoked)
215
- onSearchMessageRevoked();
216
- else
217
- antdMessage.info(t("message_revoked_or_deleted"));
218
- }
222
+ notifyRevokedTarget(searchClientMsgID);
219
223
  // Deliberately outside the guard: a repeat run still needs to leave
220
224
  // the user with a usable list. The fallback is a no-op once the first
221
225
  // one has populated it, and skipping it here would strand an empty
@@ -397,6 +401,7 @@ onSearchMessageRevoked) => {
397
401
  moreNewLoading,
398
402
  getMoreNewMessages,
399
403
  refreshLatestMessages,
404
+ notifyRevokedTarget,
400
405
  };
401
406
  };
402
407
  export const pushNewMessage = (message) => emit("PUSH_NEW_MSG", message);
@@ -19,8 +19,10 @@ interface UseMessageListScrollParams {
19
19
  getMoreNewMessages: () => void | Promise<unknown>;
20
20
  /** Holds mark-as-read back while the first-unread lookup is still pending. */
21
21
  markAsReadBlocked?: boolean;
22
+ /** Tells the user a search jump target was recalled (deduped with the search load). */
23
+ notifyRevokedTarget?: (clientMsgID: string) => void;
22
24
  }
23
- export declare const useMessageListScroll: ({ conversationId, userId, isGroupChat, loadState, latestLoadState, moreOldLoading, moreNewLoading, getMoreOldMessages, getMoreNewMessages, markAsReadBlocked, }: UseMessageListScrollParams) => {
25
+ export declare const useMessageListScroll: ({ conversationId, userId, isGroupChat, loadState, latestLoadState, moreOldLoading, moreNewLoading, getMoreOldMessages, getMoreNewMessages, markAsReadBlocked, notifyRevokedTarget, }: UseMessageListScrollParams) => {
24
26
  scrollRef: RefObject<HTMLDivElement>;
25
27
  handleMarkConversationMessageAsRead: () => void;
26
28
  loadMoreOldMessage: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"useMessageListScroll.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useMessageListScroll.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAkC,MAAM,OAAO,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAY3D,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,WAAW,EAAE,CAAC;CAC5B;AAED,UAAU,0BAA0B;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,oBAAoB,CAAC;IAChC,eAAe,EAAE,SAAS,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IACxD,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;IACxB,kBAAkB,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,kBAAkB,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,eAAO,MAAM,oBAAoB,GAAI,iKAWlC,0BAA0B;;;;8BAqErB,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC;;;mCA0Ce,MAAM;CAyDzD,CAAC"}
1
+ {"version":3,"file":"useMessageListScroll.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useMessageListScroll.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAkC,MAAM,OAAO,CAAC;AAClE,OAAO,EAAE,WAAW,EAAe,MAAM,8BAA8B,CAAC;AAYxE,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,WAAW,EAAE,CAAC;CAC5B;AAED,UAAU,0BAA0B;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,oBAAoB,CAAC;IAChC,eAAe,EAAE,SAAS,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IACxD,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;IACxB,kBAAkB,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,kBAAkB,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,uFAAuF;IACvF,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,eAAO,MAAM,oBAAoB,GAAI,sLAYlC,0BAA0B;;;;8BAsErB,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC;;;mCA0Ce,MAAM;CAyEzD,CAAC"}
@@ -1,15 +1,17 @@
1
1
  "use client";
2
2
  import { useCallback, useEffect, useRef } from "react";
3
- import { useDebounceFn } from "ahooks";
3
+ import { MessageType } from "@droppii-org/wasm-client-sdk";
4
+ import { useDebounceFn, useLatest } from "ahooks";
4
5
  import { isNumber } from "lodash";
5
6
  import { markConversationMessageAsRead } from "../../hooks/conversation/useConversation";
6
7
  import { MSG_ITEM_BUBBLE_GROUP_PREFIX, MSG_ITEM_CONTENT_PREFIX, MSG_ITEM_PREFIX, } from "../../constants";
7
8
  import useConversationStore from "../../store/conversation";
8
9
  import { holdMessageHighlight } from "./holdMessageHighlight";
9
10
  export const MESSAGE_LIST_BOTTOM_THRESHOLD = -5;
10
- export const useMessageListScroll = ({ conversationId, userId, isGroupChat, loadState, latestLoadState, moreOldLoading, moreNewLoading, getMoreOldMessages, getMoreNewMessages, markAsReadBlocked = false, }) => {
11
+ export const useMessageListScroll = ({ conversationId, userId, isGroupChat, loadState, latestLoadState, moreOldLoading, moreNewLoading, getMoreOldMessages, getMoreNewMessages, markAsReadBlocked = false, notifyRevokedTarget, }) => {
11
12
  const scrollRef = useRef(null);
12
13
  const autoFillAttemptedRef = useRef(false);
14
+ const notifyRevokedTargetRef = useLatest(notifyRevokedTarget);
13
15
  useEffect(() => {
14
16
  autoFillAttemptedRef.current = false;
15
17
  }, [conversationId]);
@@ -99,7 +101,22 @@ export const useMessageListScroll = ({ conversationId, userId, isGroupChat, load
99
101
  });
100
102
  }, [handleMarkConversationMessageAsRead]);
101
103
  const scrollToMessage = useCallback((clientMsgID) => {
104
+ // A search hit recalled since the results came back: say so instead of landing on its placeholder.
105
+ // Checked before the scroll and again before the highlight, since the list reloads in between.
106
+ const dropRevokedSearchTarget = () => {
107
+ var _a, _b, _c, _d;
108
+ const state = useConversationStore.getState();
109
+ const isRevoked = state.heldHighlightClientMsgID === clientMsgID &&
110
+ ((_c = (_b = (_a = latestLoadState.current) === null || _a === void 0 ? void 0 : _a.messageList) === null || _b === void 0 ? void 0 : _b.find((message) => message.clientMsgID === clientMsgID)) === null || _c === void 0 ? void 0 : _c.contentType) === MessageType.RevokeMessage;
111
+ if (!isRevoked)
112
+ return false;
113
+ state.setHeldHighlightClientMsgID("");
114
+ (_d = notifyRevokedTargetRef.current) === null || _d === void 0 ? void 0 : _d.call(notifyRevokedTargetRef, clientMsgID);
115
+ return true;
116
+ };
102
117
  setTimeout(() => {
118
+ if (dropRevokedSearchTarget())
119
+ return;
103
120
  const targetElement = document.getElementById(`${MSG_ITEM_PREFIX}${clientMsgID}`);
104
121
  targetElement === null || targetElement === void 0 ? void 0 : targetElement.scrollIntoView({
105
122
  behavior: "auto",
@@ -107,6 +124,8 @@ export const useMessageListScroll = ({ conversationId, userId, isGroupChat, load
107
124
  });
108
125
  setTimeout(() => {
109
126
  var _a;
127
+ if (dropRevokedSearchTarget())
128
+ return;
110
129
  const targetContentElement = document.getElementById(`${MSG_ITEM_CONTENT_PREFIX}${clientMsgID}`);
111
130
  // The border hugs the bubble; the pulse runs on the wrapper that also
112
131
  // holds the pin badge, or the badge stays put while the bubble scales.
@@ -134,7 +153,7 @@ export const useMessageListScroll = ({ conversationId, userId, isGroupChat, load
134
153
  });
135
154
  }, 500);
136
155
  }, 200);
137
- }, []);
156
+ }, [latestLoadState, notifyRevokedTargetRef]);
138
157
  return {
139
158
  scrollRef,
140
159
  handleMarkConversationMessageAsRead,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/screens/conversation-detail/index.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AAYrF,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,6CAA6C,CAAC;AASrD,MAAM,WAAW,4BACf,SAAQ,4BAA4B,EAClC,4BAA4B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;IACpC,oHAAoH;IACpH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,6GAA6G;IAC7G,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,QAAA,MAAM,uBAAuB,GAAI,oLAgB9B,4BAA4B,mDAyS9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/screens/conversation-detail/index.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AAarF,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,6CAA6C,CAAC;AASrD,MAAM,WAAW,4BACf,SAAQ,4BAA4B,EAClC,4BAA4B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;IACpC,oHAAoH;IACpH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,6GAA6G;IAC7G,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,QAAA,MAAM,uBAAuB,GAAI,oLAgB9B,4BAA4B,mDAwT9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
@@ -11,7 +11,7 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  return t;
12
12
  };
13
13
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
14
- import { useEffect, useState } from "react";
14
+ import { useEffect, useRef, useState } from "react";
15
15
  import { useTranslation } from "react-i18next";
16
16
  import { useChatContext } from "../../context/ChatContext";
17
17
  import { useChatNavigate } from "../../hooks/conversation/useChatNavigate";
@@ -30,6 +30,7 @@ import TypingIndicatorBar from "../../components/conversation-detail/TypingIndic
30
30
  import { BotButtonMessage, BotInactiveNotice, BotMenuSheet, BotStartButton, BotSuggest, CsSessionConfirmModal, } from "../../components/conversation-detail/bot";
31
31
  import { useBotFlow } from "../../hooks/conversation/useBotFlow";
32
32
  import { useCanPinMessage } from "../../hooks/message/useCanPinMessage";
33
+ import { useIsJoinedGroup } from "../../hooks/session/useIsJoinedGroup";
33
34
  import { BOT_TYPE } from "../../constants/botFlow";
34
35
  import { useResolveConversation, } from "../../hooks/conversation/useResolveConversation";
35
36
  import { nextConsultantReturnTarget, useConsultantChatNavigation, } from "../../hooks/conversation/useConsultantChatNavigation";
@@ -75,14 +76,24 @@ const DChatConversationDetail = (_a) => {
75
76
  closeProfile();
76
77
  openSearch();
77
78
  };
78
- // The search API only serves members, so a view-only viewer of a public group gets none.
79
- // Same group check as ThreadProfilePanel: the store flag can lag on a non-group thread.
80
- const isViewOnlyGroup = isViewOnlyMember && (conversation === null || conversation === void 0 ? void 0 : conversation.peerType) === PeerType.GROUP;
81
- const canSearch = showThreadSearch && !isViewOnlyGroup;
79
+ // The search API only serves members: hide it while the check says no or is pending.
80
+ // A failed check keeps it (the API still gates); a 1-1 exposes no groupID and is the viewer's own.
81
+ const { isJoined, isError: isJoinCheckFailed, refetch: refetchIsJoined, } = useIsJoinedGroup(showThreadSearch ? conversation === null || conversation === void 0 ? void 0 : conversation.groupID : undefined);
82
+ const isNonMember = (isViewOnlyMember && (conversation === null || conversation === void 0 ? void 0 : conversation.peerType) === PeerType.GROUP) ||
83
+ (!!(conversation === null || conversation === void 0 ? void 0 : conversation.groupID) && !isJoinCheckFailed && isJoined !== true);
84
+ const canSearch = showThreadSearch && !isNonMember;
85
+ // Being added to a group already open view-only syncs as an update, not a join event: re-check.
86
+ const wasViewOnlyRef = useRef(isViewOnlyMember);
87
+ useEffect(() => {
88
+ if (wasViewOnlyRef.current && !isViewOnlyMember && showThreadSearch) {
89
+ void refetchIsJoined();
90
+ }
91
+ wasViewOnlyRef.current = isViewOnlyMember;
92
+ }, [isViewOnlyMember, showThreadSearch, refetchIsJoined]);
82
93
  // Search belongs to one thread; another thread opening in place (or losing membership) closes it.
83
94
  useEffect(() => {
84
95
  closeSearch();
85
- }, [conversation === null || conversation === void 0 ? void 0 : conversation.conversationID, isViewOnlyGroup, closeSearch]);
96
+ }, [conversation === null || conversation === void 0 ? void 0 : conversation.conversationID, isNonMember, closeSearch]);
86
97
  const { returnTarget: consultantReturnTarget, returnToBotThread, clearReturnTarget, setReturnTarget, } = useConsultantChatNavigation();
87
98
  useEffect(() => {
88
99
  const next = nextConsultantReturnTarget(consultantReturnTarget, conversation);
@@ -156,6 +167,6 @@ const DChatConversationDetail = (_a) => {
156
167
  !botFlow.flowIsEnd &&
157
168
  botFlow.shouldShowButtons &&
158
169
  botFlow.currentButtonsBlock ? (_jsx(BotButtonMessage, { buttons: botFlow.currentButtonsBlock.data.buttons, disabled: botFlow.isProcessing, onSelect: botFlow.selectButton, onRestart: botFlow.restart })) : null;
159
- return (_jsxs("div", { "data-testid": "conversation-detail-root", className: clsx("relative flex h-full w-full flex-col overflow-hidden bg-white", className), children: [_jsx(ConversationDetailHeader, { conversation: conversation, onBack: handleBack, onProfileClick: openProfile, onProfileIconClick: showProfileActions ? openProfile : undefined, onSearchClick: canSearch ? openSearch : undefined }), _jsx(PinnedMessageBar, { canUnpin: canPinMessage }), _jsxs("div", { [COMPOSER_DISMISS_ZONE_ATTR]: "", className: "relative flex min-h-0 flex-1 flex-col", children: [children !== null && children !== void 0 ? children : (_jsx(ConversationMessageList, { bottomSlot: botButtonsSlot, onCopyMessageLink: onCopyMessageLink, onSearchMessageRevoked: onSearchMessageRevoked })), _jsx(TypingIndicatorBar, { conversationId: conversation.conversationID })] }), footer, isBotThread && (_jsxs(_Fragment, { children: [_jsx(BotMenuSheet, { open: isMenuOpen, items: botFlow.menuItems, disabled: botFlow.isProcessing, onClose: closeMenu, onSelect: botFlow.selectMenuOption }), _jsx(CsSessionConfirmModal, { open: botFlow.isCsConfirmOpen, loading: botFlow.isProcessing, onCancel: botFlow.cancelMenuOption, onConfirm: botFlow.confirmEndCsSession })] })), _jsx(ThreadProfilePanel, { open: isProfileOpen, conversation: conversation, onClose: closeProfile, onSelectResource: setResourceTab, showActions: showProfileActions, onSearch: canSearch ? openSearchFromProfile : undefined }), showThreadSearch && (_jsx(ThreadSearchPanel, { open: isSearchOpen, conversation: conversation, onClose: closeSearch, onSearchMessageRevoked: onSearchMessageRevoked }, conversation.conversationID)), _jsx(MediaResourcePanel, { open: resourceTab !== null, initialTab: resourceTab !== null && resourceTab !== void 0 ? resourceTab : MediaResourceTabKey.Image, onClose: () => setResourceTab(null) })] }));
170
+ return (_jsxs("div", { "data-testid": "conversation-detail-root", className: clsx("relative flex h-full w-full flex-col overflow-hidden bg-white", className), children: [_jsx(ConversationDetailHeader, { conversation: conversation, onBack: handleBack, onProfileClick: openProfile, onProfileIconClick: showProfileActions ? openProfile : undefined, onSearchClick: canSearch ? openSearch : undefined }), _jsx(PinnedMessageBar, { canUnpin: canPinMessage }), _jsxs("div", { [COMPOSER_DISMISS_ZONE_ATTR]: "", className: "relative flex min-h-0 flex-1 flex-col", children: [children !== null && children !== void 0 ? children : (_jsx(ConversationMessageList, { bottomSlot: botButtonsSlot, onCopyMessageLink: onCopyMessageLink, onSearchMessageRevoked: onSearchMessageRevoked })), _jsx(TypingIndicatorBar, { conversationId: conversation.conversationID })] }), footer, isBotThread && (_jsxs(_Fragment, { children: [_jsx(BotMenuSheet, { open: isMenuOpen, items: botFlow.menuItems, disabled: botFlow.isProcessing, onClose: closeMenu, onSelect: botFlow.selectMenuOption }), _jsx(CsSessionConfirmModal, { open: botFlow.isCsConfirmOpen, loading: botFlow.isProcessing, onCancel: botFlow.cancelMenuOption, onConfirm: botFlow.confirmEndCsSession })] })), _jsx(ThreadProfilePanel, { open: isProfileOpen, conversation: conversation, onClose: closeProfile, onSelectResource: setResourceTab, showActions: showProfileActions, onSearch: canSearch ? openSearchFromProfile : undefined, hideSearch: showThreadSearch && !canSearch }), showThreadSearch && (_jsx(ThreadSearchPanel, { open: isSearchOpen && canSearch, conversation: conversation, onClose: closeSearch, onSearchMessageRevoked: onSearchMessageRevoked }, conversation.conversationID)), _jsx(MediaResourcePanel, { open: resourceTab !== null, initialTab: resourceTab !== null && resourceTab !== void 0 ? resourceTab : MediaResourceTabKey.Image, onClose: () => setResourceTab(null) })] }));
160
171
  };
161
172
  export default DChatConversationDetail;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@droppii-org/chat-sdk",
3
- "version": "0.7.8",
3
+ "version": "0.7.9",
4
4
  "description": "Droppii React Chat SDK",
5
5
  "repository": {
6
6
  "type": "git",