@droppii-org/chat-sdk 0.8.1 → 0.8.2
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/components/canned-response/cannedTrigger.d.ts +2 -0
- package/dist/components/canned-response/cannedTrigger.d.ts.map +1 -0
- package/dist/components/canned-response/cannedTrigger.js +3 -0
- package/dist/components/canned-response/insertCannedContent.js +2 -2
- package/dist/components/conversation/ConversationByGroupItem.js +1 -1
- package/dist/components/conversation/ConversationBySessionItem.js +4 -4
- package/dist/components/conversation/internalNotePreview.d.ts +1 -1
- package/dist/components/conversation/internalNotePreview.d.ts.map +1 -1
- package/dist/components/conversation/internalNotePreview.js +22 -3
- package/dist/components/conversation/rowTime.d.ts +2 -2
- package/dist/components/conversation/rowTime.d.ts.map +1 -1
- package/dist/components/conversation/rowTime.js +2 -4
- package/dist/components/conversation-detail/ConversationDetailInput.d.ts.map +1 -1
- package/dist/components/conversation-detail/ConversationDetailInput.js +8 -3
- package/dist/components/conversation-detail/ConversationMessageList.d.ts.map +1 -1
- package/dist/components/conversation-detail/ConversationMessageList.js +6 -3
- package/dist/components/conversation-detail/bot/BotMenuSheet.d.ts.map +1 -1
- package/dist/components/conversation-detail/bot/BotMenuSheet.js +1 -1
- package/dist/components/conversation-inbox/ConversationInboxList.d.ts.map +1 -1
- package/dist/components/conversation-inbox/ConversationInboxList.js +4 -0
- package/dist/components/conversation-inbox/InboxBootStatus.d.ts.map +1 -1
- package/dist/components/conversation-inbox/InboxBootStatus.js +10 -2
- package/dist/components/group/CreateGroupForm.d.ts.map +1 -1
- package/dist/components/group/CreateGroupForm.js +2 -1
- package/dist/components/group/CreateGroupFormCrmView.d.ts.map +1 -1
- package/dist/components/group/CreateGroupFormCrmView.js +1 -1
- package/dist/components/group/GroupMemberPickerCrm.d.ts.map +1 -1
- package/dist/components/group/GroupMemberPickerCrm.js +1 -1
- package/dist/components/media-collection/FileCollection.d.ts.map +1 -1
- package/dist/components/media-collection/FileCollection.js +2 -1
- package/dist/components/media-collection/ImageCollection.d.ts.map +1 -1
- package/dist/components/media-collection/ImageCollection.js +2 -1
- package/dist/components/media-collection/LinkCollection.d.ts.map +1 -1
- package/dist/components/media-collection/LinkCollection.js +14 -26
- package/dist/components/media-collection/VideoCollection.d.ts.map +1 -1
- package/dist/components/media-collection/VideoCollection.js +2 -1
- package/dist/components/media-resources/ResourceScrollContainer.d.ts.map +1 -1
- package/dist/components/media-resources/ResourceScrollContainer.js +34 -2
- package/dist/components/message/GroupMembersDrawer.d.ts.map +1 -1
- package/dist/components/message/GroupMembersDrawer.js +2 -3
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +15 -4
- package/dist/components/message/footer/ActionBar.d.ts.map +1 -1
- package/dist/components/message/footer/ActionBar.js +10 -44
- package/dist/components/message/footer/CannedResponsePlugin.d.ts.map +1 -1
- package/dist/components/message/footer/CannedResponsePlugin.js +3 -2
- package/dist/components/message/footer/EnterHandler.d.ts.map +1 -1
- package/dist/components/message/footer/EnterHandler.js +10 -47
- package/dist/components/message/footer/FilePreview.d.ts.map +1 -1
- package/dist/components/message/footer/FilePreview.js +2 -1
- package/dist/components/message/footer/LinkInputPreview.d.ts +1 -1
- package/dist/components/message/footer/LinkInputPreview.d.ts.map +1 -1
- package/dist/components/message/footer/LinkInputPreview.js +8 -1
- package/dist/components/message/footer/MediaUploadButtons.d.ts.map +1 -1
- package/dist/components/message/footer/MediaUploadButtons.js +4 -7
- package/dist/components/message/footer/index.d.ts.map +1 -1
- package/dist/components/message/footer/index.js +57 -16
- package/dist/components/message/footer/mention/useMentionOptions.d.ts.map +1 -1
- package/dist/components/message/footer/mention/useMentionOptions.js +2 -6
- package/dist/components/message/footer/resetEditorAfterSend.d.ts +7 -0
- package/dist/components/message/footer/resetEditorAfterSend.d.ts.map +1 -0
- package/dist/components/message/footer/resetEditorAfterSend.js +62 -0
- package/dist/components/message/groupMemberMenuCrm.d.ts +4 -4
- package/dist/components/message/groupMemberMenuCrm.d.ts.map +1 -1
- package/dist/components/message/groupMemberMenuCrm.js +73 -60
- package/dist/components/message/item/SystemLogMessage.d.ts.map +1 -1
- package/dist/components/message/item/SystemLogMessage.js +5 -2
- package/dist/components/search-conversation/SearchConversationAsGroups.js +1 -1
- package/dist/components/search-conversation/SearchConversationAsMessages.js +1 -1
- package/dist/components/search-conversation/SearchConversationMyInbox.js +1 -1
- package/dist/components/thread/GroupInfoBodyCrm.d.ts.map +1 -1
- package/dist/components/thread/GroupInfoBodyCrm.js +2 -2
- package/dist/components/thread/GroupThreadInfo.d.ts.map +1 -1
- package/dist/components/thread/GroupThreadInfo.js +2 -6
- package/dist/components/thread/ManualAssignPopover.d.ts.map +1 -1
- package/dist/components/thread/ManualAssignPopover.js +6 -8
- package/dist/components/thread/SessionDetailCard.d.ts.map +1 -1
- package/dist/components/thread/SessionDetailCard.js +3 -3
- package/dist/components/thread/SessionSection.d.ts.map +1 -1
- package/dist/components/thread/SessionSection.js +3 -9
- package/dist/components/thread/SessionTagPicker.d.ts +3 -1
- package/dist/components/thread/SessionTagPicker.d.ts.map +1 -1
- package/dist/components/thread/SessionTagPicker.js +2 -2
- package/dist/components/thread/sessionDateFormat.d.ts +2 -0
- package/dist/components/thread/sessionDateFormat.d.ts.map +1 -1
- package/dist/components/thread/sessionDateFormat.js +6 -0
- package/dist/constants/dateFormat.d.ts +2 -0
- package/dist/constants/dateFormat.d.ts.map +1 -0
- package/dist/constants/dateFormat.js +2 -0
- package/dist/hooks/common/useFetchUrlMetadata.d.ts +1 -0
- package/dist/hooks/common/useFetchUrlMetadata.d.ts.map +1 -1
- package/dist/hooks/conversation/useBotFlow.d.ts.map +1 -1
- package/dist/hooks/conversation/useBotFlow.js +64 -12
- package/dist/hooks/conversation/useCloseBotSession.d.ts +2 -0
- package/dist/hooks/conversation/useCloseBotSession.d.ts.map +1 -1
- package/dist/hooks/conversation/useCloseBotSession.js +17 -1
- package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
- package/dist/hooks/global/useGlobalEvent.js +148 -16
- package/dist/hooks/global/useLoginHealth.d.ts +3 -0
- package/dist/hooks/global/useLoginHealth.d.ts.map +1 -0
- package/dist/hooks/global/useLoginHealth.js +55 -0
- package/dist/hooks/group/useCreateGroup.d.ts.map +1 -1
- package/dist/hooks/group/useCreateGroup.js +75 -6
- package/dist/hooks/init/useChatBootPhase.d.ts +2 -0
- package/dist/hooks/init/useChatBootPhase.d.ts.map +1 -1
- package/dist/hooks/init/useChatBootPhase.js +11 -6
- package/dist/hooks/message/useMessage.d.ts.map +1 -1
- package/dist/hooks/message/useMessage.js +97 -28
- package/dist/hooks/message/usePinnedMessages.d.ts.map +1 -1
- package/dist/hooks/message/usePinnedMessages.js +13 -2
- package/dist/hooks/message/useRevokeMessage.d.ts.map +1 -1
- package/dist/hooks/message/useRevokeMessage.js +25 -0
- package/dist/hooks/message/useSendMessage.d.ts +15 -12
- package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
- package/dist/hooks/message/useSendMessage.js +199 -133
- package/dist/hooks/search/useSearchMessage.d.ts.map +1 -1
- package/dist/hooks/search/useSearchMessage.js +57 -3
- package/dist/hooks/session/useConversationSessionState.d.ts.map +1 -1
- package/dist/hooks/session/useConversationSessionState.js +4 -1
- package/dist/hooks/session/useSessionLatestPreview.d.ts.map +1 -1
- package/dist/hooks/session/useSessionLatestPreview.js +34 -4
- package/dist/hooks/user/useAuth.d.ts +1 -1
- package/dist/hooks/user/useAuth.d.ts.map +1 -1
- package/dist/hooks/user/useAuth.js +31 -15
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/locales/vi/boot.json +5 -1
- package/dist/locales/vi/common.json +34 -15
- package/dist/locales/vi/conversation-inbox.json +2 -1
- package/dist/screens/conversation-detail/index.d.ts.map +1 -1
- package/dist/screens/conversation-detail/index.js +6 -0
- package/dist/screens/desk-message/DeskBootCard.d.ts.map +1 -1
- package/dist/screens/desk-message/DeskBootCard.js +6 -4
- package/dist/store/boot.d.ts +5 -0
- package/dist/store/boot.d.ts.map +1 -1
- package/dist/store/boot.js +11 -1
- package/dist/store/conversation.d.ts.map +1 -1
- package/dist/store/conversation.js +5 -1
- package/dist/store/type.d.ts +3 -1
- package/dist/store/type.d.ts.map +1 -1
- package/dist/styles/global.css +1 -1
- package/dist/types/chat.d.ts +1 -1
- package/dist/types/chat.d.ts.map +1 -1
- package/dist/utils/common.d.ts.map +1 -1
- package/dist/utils/common.js +4 -1
- package/dist/utils/fileValidation.d.ts +7 -0
- package/dist/utils/fileValidation.d.ts.map +1 -1
- package/dist/utils/fileValidation.js +37 -13
- package/dist/utils/latestMessagePreviewable.d.ts +2 -0
- package/dist/utils/latestMessagePreviewable.d.ts.map +1 -0
- package/dist/utils/latestMessagePreviewable.js +18 -0
- package/dist/utils/mentionMembers.d.ts +3 -0
- package/dist/utils/mentionMembers.d.ts.map +1 -0
- package/dist/utils/mentionMembers.js +29 -0
- package/dist/utils/messageLog.d.ts +2 -2
- package/dist/utils/messageLog.d.ts.map +1 -1
- package/dist/utils/messageLog.js +129 -40
- package/dist/utils/notifiableMessage.d.ts +7 -0
- package/dist/utils/notifiableMessage.d.ts.map +1 -0
- package/dist/utils/notifiableMessage.js +26 -0
- package/dist/utils/withSdkTimeout.d.ts +11 -0
- package/dist/utils/withSdkTimeout.d.ts.map +1 -0
- package/dist/utils/withSdkTimeout.js +30 -0
- package/package.json +1 -1
|
@@ -12,7 +12,7 @@ import { LAYOUT_MEDIA } from "../../constants/layout";
|
|
|
12
12
|
const OPTION_HEIGHT = 36;
|
|
13
13
|
const MAX_VISIBLE_OPTIONS = 8;
|
|
14
14
|
// The "Thêm thẻ" row of the tag box. Its list opens inline, so it pushes the card instead of floating over it.
|
|
15
|
-
const SessionTagPicker = ({ labelId, available, selectedIds, onChange, }) => {
|
|
15
|
+
const SessionTagPicker = ({ labelId, available, selectedIds, selectedNames = [], onChange, }) => {
|
|
16
16
|
const { t } = useTranslation("common");
|
|
17
17
|
const isTouch = useMediaQuery(LAYOUT_MEDIA.touch);
|
|
18
18
|
const [open, setOpen] = useState(false);
|
|
@@ -50,7 +50,7 @@ const SessionTagPicker = ({ labelId, available, selectedIds, onChange, }) => {
|
|
|
50
50
|
event.stopPropagation();
|
|
51
51
|
close();
|
|
52
52
|
}
|
|
53
|
-
}, children: [_jsxs("button", { ref: toggleRef, id: labelId, type: "button", "aria-label": t("session_add_tag"), "aria-expanded": open, "aria-controls": listId, className: "flex h-9 touch:min-h-10 w-full items-center gap-1 px-3 text-[13px] text-gray-600 hover:text-gray-900", onClick: () => (open ? close() : setOpen(true)), children: [_jsx(
|
|
53
|
+
}, children: [_jsxs("button", { ref: toggleRef, id: labelId, type: "button", "aria-label": t("session_add_tag"), "aria-expanded": open, "aria-controls": listId, className: "group flex min-h-9 touch:min-h-10 w-full items-center gap-1 px-3 py-1 text-left text-[13px] text-gray-600 hover:text-gray-900", onClick: () => (open ? close() : setOpen(true)), children: [selectedNames.length === 0 ? (_jsx("span", { className: "min-w-0 flex-1 truncate", children: t("session_add_tag_placeholder") })) : (_jsx("span", { className: "flex min-w-0 flex-1 flex-wrap gap-1", children: selectedNames.map((name, index) => (_jsx("span", { className: "inline-flex h-5 max-w-full items-center truncate rounded-md bg-[var(--dc-color-neutral-surface)] px-2 text-xs font-medium text-gray-900", children: name }, `${name}-${index}`))) })), _jsx(StrokeIcon, { name: "plus", size: 14, className: "shrink-0 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100 touch:opacity-100" })] }), open && (_jsxs("div", { ref: panelRef, className: "mx-2 mb-2 flex flex-col gap-1 rounded-lg border border-gray-50 p-1", children: [_jsx(Input, { autoFocus: !isTouch, "aria-label": t("session_search_tag_placeholder"), "aria-controls": listId, placeholder: t("session_search_tag_placeholder"), value: keyword, onChange: (event) => setKeyword(event.target.value), prefix: _jsx(StrokeIcon, { name: "search", size: 16, className: "text-gray-400" }), className: "rounded-lg border-gray-300 text-[13px] focus-within:border-chat-focus focus-within:shadow-chat-focus-ring", allowClear: true }), _jsxs("ul", { id: listId, role: "listbox", "aria-multiselectable": true, "aria-labelledby": labelId, className: "overflow-y-auto", style: { height: listHeight }, children: [isLoading && (_jsx("li", { className: "flex justify-center py-2", children: _jsx(Spin, { size: "small" }) })), !isLoading && options.length === 0 && (_jsx("li", { className: "px-2 py-1.5 text-[13px] text-gray-600", children: t("session_no_tags") })), options.map((label) => {
|
|
54
54
|
const isSelected = selectedIds.includes(label.id);
|
|
55
55
|
return (_jsx("li", { role: "option", "aria-selected": isSelected, children: _jsxs("button", { type: "button", className: `flex h-9 w-full items-center justify-between rounded px-2 text-left text-[13px] text-gray-900 hover:bg-gray-5 ${isSelected ? "bg-primary-25" : ""}`, onClick: () => toggle(label.id), children: [label.name, isSelected && _jsx(StrokeIcon, { name: "check", size: 14 })] }) }, label.id));
|
|
56
56
|
})] })] }))] }));
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export declare const formatSessionTime: (dateString?: string | null) => string;
|
|
2
2
|
export declare const formatSessionDate: (dateString?: string | null) => string;
|
|
3
|
+
/** "HH:mm dd/MM - HH:mm dd/MM" (start - end); an open session has no end, so only the start. */
|
|
4
|
+
export declare const formatSessionRange: (startDate?: string | null, endDate?: string | null) => string;
|
|
3
5
|
//# sourceMappingURL=sessionDateFormat.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionDateFormat.d.ts","sourceRoot":"","sources":["../../../src/components/thread/sessionDateFormat.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB,GAAI,aAAa,MAAM,GAAG,IAAI,WAI3D,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,aAAa,MAAM,GAAG,IAAI,WAI3D,CAAC"}
|
|
1
|
+
{"version":3,"file":"sessionDateFormat.d.ts","sourceRoot":"","sources":["../../../src/components/thread/sessionDateFormat.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB,GAAI,aAAa,MAAM,GAAG,IAAI,WAI3D,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,aAAa,MAAM,GAAG,IAAI,WAI3D,CAAC;AAEF,gGAAgG;AAChG,eAAO,MAAM,kBAAkB,GAC7B,YAAY,MAAM,GAAG,IAAI,EACzB,UAAU,MAAM,GAAG,IAAI,WAKxB,CAAC"}
|
|
@@ -11,3 +11,9 @@ export const formatSessionDate = (dateString) => {
|
|
|
11
11
|
const date = new Date(dateString);
|
|
12
12
|
return `${formatSessionTime(dateString)} ${pad(date.getDate())}/${pad(date.getMonth() + 1)}`;
|
|
13
13
|
};
|
|
14
|
+
/** "HH:mm dd/MM - HH:mm dd/MM" (start - end); an open session has no end, so only the start. */
|
|
15
|
+
export const formatSessionRange = (startDate, endDate) => {
|
|
16
|
+
const start = formatSessionDate(startDate);
|
|
17
|
+
const end = formatSessionDate(endDate);
|
|
18
|
+
return end ? `${start} - ${end}` : start;
|
|
19
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dateFormat.d.ts","sourceRoot":"","sources":["../../src/constants/dateFormat.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,0BAA0B,eAAe,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFetchUrlMetadata.d.ts","sourceRoot":"","sources":["../../../src/hooks/common/useFetchUrlMetadata.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"useFetchUrlMetadata.d.ts","sourceRoot":"","sources":["../../../src/hooks/common/useFetchUrlMetadata.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,eAAO,MAAM,gBAAgB,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAY9E,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,KAAK,MAAM,EAAE,iBAAc,8EAO3D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useBotFlow.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useBotFlow.ts"],"names":[],"mappings":"AAMA,OAAO,EAKL,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACrB,MAAM,sBAAsB,CAAC;AAgB9B,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,aAAa,CAAC;AAMtD,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;CACtB;
|
|
1
|
+
{"version":3,"file":"useBotFlow.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useBotFlow.ts"],"names":[],"mappings":"AAMA,OAAO,EAKL,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACrB,MAAM,sBAAsB,CAAC;AAgB9B,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,aAAa,CAAC;AAMtD,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;CACtB;AAoCD,eAAO,MAAM,UAAU,GAAI,kCAGxB,gBAAgB;;;;;;;;;;;;6BA8KqB,MAAM;;6BAwFN,eAAe;;;CA+DtD,CAAC"}
|
|
@@ -21,6 +21,17 @@ const INITIAL_BOT_FLOW_STATE = {
|
|
|
21
21
|
flowCurrentStep: null,
|
|
22
22
|
flowIsEnd: false,
|
|
23
23
|
};
|
|
24
|
+
// Safety net: a group whose message never arrives must not hide buttons forever.
|
|
25
|
+
const PENDING_GROUP_TIMEOUT_MS = 8000;
|
|
26
|
+
// Only these blocks make the server push a chat message carrying the group id.
|
|
27
|
+
const MESSAGE_BLOCK_TYPES = [
|
|
28
|
+
BotBlockType.BubbleText,
|
|
29
|
+
BotBlockType.BubbleImage,
|
|
30
|
+
];
|
|
31
|
+
const hasMessageBlock = (step) => {
|
|
32
|
+
var _a, _b;
|
|
33
|
+
return !!((_b = (_a = step === null || step === void 0 ? void 0 : step.data) === null || _a === void 0 ? void 0 : _a.children) === null || _b === void 0 ? void 0 : _b.some((child) => MESSAGE_BLOCK_TYPES.includes(child.type)));
|
|
34
|
+
};
|
|
24
35
|
const parseMessageFlowGroupId = (ex) => {
|
|
25
36
|
var _a;
|
|
26
37
|
try {
|
|
@@ -97,6 +108,12 @@ export const useBotFlow = ({ conversationId, isBotThread, }) => {
|
|
|
97
108
|
emitter.off("PUSH_NEW_MSG", handleNewMessage);
|
|
98
109
|
};
|
|
99
110
|
}, [authUserID]);
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
if (!pendingGroupId)
|
|
113
|
+
return;
|
|
114
|
+
const timer = setTimeout(() => setPendingGroupId(null), PENDING_GROUP_TIMEOUT_MS);
|
|
115
|
+
return () => clearTimeout(timer);
|
|
116
|
+
}, [pendingGroupId]);
|
|
100
117
|
const activeStep = isActiveConversation ? flowCurrentStep : null;
|
|
101
118
|
const inputButtonsBlocks = (_d = activeStep === null || activeStep === void 0 ? void 0 : activeStep.data.children.filter((child) => child.type === BotBlockType.InputButtons)) !== null && _d !== void 0 ? _d : [];
|
|
102
119
|
const currentButtonsBlock = inputButtonsBlocks.length > 0
|
|
@@ -138,6 +155,7 @@ export const useBotFlow = ({ conversationId, isBotThread, }) => {
|
|
|
138
155
|
});
|
|
139
156
|
const nextGroupId = (_a = result.flowNextStep) === null || _a === void 0 ? void 0 : _a.id;
|
|
140
157
|
setPendingGroupId(result.havingBotMessage &&
|
|
158
|
+
hasMessageBlock(result.flowNextStep) &&
|
|
141
159
|
nextGroupId &&
|
|
142
160
|
latestBotFlowGroupIdRef.current !== nextGroupId
|
|
143
161
|
? nextGroupId
|
|
@@ -187,24 +205,46 @@ export const useBotFlow = ({ conversationId, isBotThread, }) => {
|
|
|
187
205
|
await runStep(resetDetail.flowCurrentStep);
|
|
188
206
|
});
|
|
189
207
|
};
|
|
208
|
+
// The CS may have closed the session on the desk while this thread stayed
|
|
209
|
+
// open, so confirm against the server before showing the "end session" modal.
|
|
210
|
+
const checkCsSession = async () => {
|
|
211
|
+
var _a, _b;
|
|
212
|
+
if (!isInActiveCsSession)
|
|
213
|
+
return { active: false, step: activeStep };
|
|
214
|
+
const fresh = await detailQuery.refetch();
|
|
215
|
+
// A missing step means the session is gone; the stale CS step must not win.
|
|
216
|
+
const step = (_b = (_a = fresh.data) === null || _a === void 0 ? void 0 : _a.flowCurrentStep) !== null && _b !== void 0 ? _b : null;
|
|
217
|
+
return { active: isInCsSession({ flowCurrentStep: step }), step };
|
|
218
|
+
};
|
|
219
|
+
// The session refetch is async; a second tap meanwhile would run the same
|
|
220
|
+
// check-then-act again, so claim the slot before awaiting anything.
|
|
221
|
+
const checkingRef = useRef(false);
|
|
190
222
|
const restart = async () => {
|
|
191
|
-
if (
|
|
192
|
-
setPendingCsAction({ type: "restart" });
|
|
223
|
+
if (checkingRef.current)
|
|
193
224
|
return;
|
|
225
|
+
checkingRef.current = true;
|
|
226
|
+
try {
|
|
227
|
+
if ((await checkCsSession()).active) {
|
|
228
|
+
setPendingCsAction({ type: "restart" });
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
await performRestart();
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
checkingRef.current = false;
|
|
194
235
|
}
|
|
195
|
-
await performRestart();
|
|
196
236
|
};
|
|
197
|
-
const runMenuOption = async (item) => {
|
|
198
|
-
if (!
|
|
237
|
+
const runMenuOption = async (item, step = activeStep) => {
|
|
238
|
+
if (!step)
|
|
199
239
|
return;
|
|
200
240
|
await whileChoosing(async () => {
|
|
201
241
|
await sendMergeMessage({
|
|
202
242
|
plainText: item.label,
|
|
203
243
|
richText: "",
|
|
204
244
|
isInternal: false,
|
|
205
|
-
flow: { groupId:
|
|
245
|
+
flow: { groupId: step.id, childId: null, actionId: item.id },
|
|
206
246
|
});
|
|
207
|
-
await runStep(
|
|
247
|
+
await runStep(step, item.id);
|
|
208
248
|
});
|
|
209
249
|
};
|
|
210
250
|
const selectMenuOption = async (item) => {
|
|
@@ -212,13 +252,23 @@ export const useBotFlow = ({ conversationId, isBotThread, }) => {
|
|
|
212
252
|
await restart();
|
|
213
253
|
return;
|
|
214
254
|
}
|
|
215
|
-
if (
|
|
216
|
-
setPendingCsAction({ type: "menu", item });
|
|
255
|
+
if (checkingRef.current)
|
|
217
256
|
return;
|
|
257
|
+
checkingRef.current = true;
|
|
258
|
+
try {
|
|
259
|
+
const { active, step } = await checkCsSession();
|
|
260
|
+
if (active) {
|
|
261
|
+
setPendingCsAction({ type: "menu", item });
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
await runMenuOption(item, step);
|
|
265
|
+
}
|
|
266
|
+
finally {
|
|
267
|
+
checkingRef.current = false;
|
|
218
268
|
}
|
|
219
|
-
await runMenuOption(item);
|
|
220
269
|
};
|
|
221
270
|
const confirmEndCsSession = async () => {
|
|
271
|
+
var _a, _b;
|
|
222
272
|
if (!pendingCsAction)
|
|
223
273
|
return;
|
|
224
274
|
const action = pendingCsAction;
|
|
@@ -229,10 +279,12 @@ export const useBotFlow = ({ conversationId, isBotThread, }) => {
|
|
|
229
279
|
await performRestart();
|
|
230
280
|
}
|
|
231
281
|
else {
|
|
232
|
-
|
|
282
|
+
// The server resets the flow step on close; run the menu item on it.
|
|
283
|
+
const fresh = await detailQuery.refetch();
|
|
284
|
+
await runMenuOption(action.item, (_b = (_a = fresh.data) === null || _a === void 0 ? void 0 : _a.flowCurrentStep) !== null && _b !== void 0 ? _b : activeStep);
|
|
233
285
|
}
|
|
234
286
|
}
|
|
235
|
-
catch (
|
|
287
|
+
catch (_c) {
|
|
236
288
|
toastMessage.error(t("thread_detail.bot.cs_confirm.close_failed"));
|
|
237
289
|
setPendingCsAction(null);
|
|
238
290
|
}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
+
export declare const SESSION_ALREADY_CLOSED_CODE = 2005;
|
|
2
|
+
export declare const isSessionAlreadyClosedError: (error: unknown) => boolean;
|
|
1
3
|
export declare const useCloseBotSession: () => import("@tanstack/react-query").UseMutationResult<void, Error, string, unknown>;
|
|
2
4
|
//# sourceMappingURL=useCloseBotSession.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCloseBotSession.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useCloseBotSession.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useCloseBotSession.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useCloseBotSession.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAEhD,eAAO,MAAM,2BAA2B,GAAI,OAAO,OAAO,KAAG,OAQ5D,CAAC;AAEF,eAAO,MAAM,kBAAkB,uFAc9B,CAAC"}
|
|
@@ -3,11 +3,27 @@ import { useMutation } from "@tanstack/react-query";
|
|
|
3
3
|
import { apiInstance } from "../../services/api";
|
|
4
4
|
import { ENDPOINTS } from "../../services/routes";
|
|
5
5
|
import { QUERY_KEYS } from "../../services/query";
|
|
6
|
+
// Backend code for "session already closed" (e.g. CS ended it on the desk).
|
|
7
|
+
export const SESSION_ALREADY_CLOSED_CODE = 2005;
|
|
8
|
+
export const isSessionAlreadyClosedError = (error) => {
|
|
9
|
+
var _a;
|
|
10
|
+
const data = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data;
|
|
11
|
+
if (!data)
|
|
12
|
+
return false;
|
|
13
|
+
return [data.code, data.errorCode, data.errCode, data.statusCode].some((value) => Number(value) === SESSION_ALREADY_CLOSED_CODE);
|
|
14
|
+
};
|
|
6
15
|
export const useCloseBotSession = () => {
|
|
7
16
|
return useMutation({
|
|
8
17
|
mutationKey: [QUERY_KEYS.CLOSE_BOT_SESSION],
|
|
9
18
|
mutationFn: async (conversationId) => {
|
|
10
|
-
|
|
19
|
+
try {
|
|
20
|
+
await apiInstance.post(ENDPOINTS.chatService.closeBotSession(conversationId));
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
// Already closed elsewhere is the outcome we wanted; let the caller go on.
|
|
24
|
+
if (!isSessionAlreadyClosedError(error))
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
11
27
|
},
|
|
12
28
|
});
|
|
13
29
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGlobalEvent.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useGlobalEvent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useGlobalEvent.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useGlobalEvent.ts"],"names":[],"mappings":"AA6DA,eAAO,MAAM,cAAc,YA0wB1B,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
2
|
import { useLatest } from "ahooks";
|
|
3
3
|
import { DChatSDK } from "../../constants/sdk";
|
|
4
4
|
import { CbEvents, GroupVisibility, MessageType, SessionType, } from "@droppii-org/wasm-client-sdk";
|
|
5
|
-
import { BusinessNotificationType, ConnectStatus, CustomType, SyncStatus, } from "../../types/chat";
|
|
5
|
+
import { BusinessNotificationType, ConnectStatus, CustomType, SessionStatus, SyncStatus, } from "../../types/chat";
|
|
6
6
|
import { pushNewMessage, updateOneMessage } from "../../hooks/message/useMessage";
|
|
7
7
|
import { fetchPinnedMessages } from "../../hooks/message/usePinnedMessages";
|
|
8
8
|
import { applyRevokedMessage } from "../../hooks/message/useRevokeMessage";
|
|
@@ -22,11 +22,17 @@ import { ignoreInternalLatestMessage } from "../../utils/common";
|
|
|
22
22
|
import { invalidateSessionQueries } from "../../utils/queryHelpers";
|
|
23
23
|
import { refreshPublicGroupLists } from "../../hooks/group/useJoinPublicGroup";
|
|
24
24
|
import emitter from "../../utils/events";
|
|
25
|
+
import { withSdkTimeout } from "../../utils/withSdkTimeout";
|
|
26
|
+
import { isCoreLoggedIn, useLoginHealthProbe } from "./useLoginHealth";
|
|
25
27
|
const notPushType = [MessageType.TypingMessage, MessageType.RevokeMessage];
|
|
28
|
+
const LOGIN_TIMEOUT_MS = 30000;
|
|
26
29
|
export const useGlobalEvent = () => {
|
|
27
30
|
const { user } = useChatContext();
|
|
28
31
|
const onNavigate = useChatNavigate();
|
|
29
32
|
const userRef = useLatest(user);
|
|
33
|
+
// One login at a time: boot, token refresh, the health probe and relogin all share it.
|
|
34
|
+
// Keyed by credential: the same user and token share a flight, a new one chains after it.
|
|
35
|
+
const loginInFlight = useRef(null);
|
|
30
36
|
const queryClient = useQueryClient();
|
|
31
37
|
const { updateConnectStatus, updateSyncStatus, getSelfUserInfo } = useChatContext();
|
|
32
38
|
const updateConversationList = useConversationStore((state) => state.updateConversationList);
|
|
@@ -164,7 +170,20 @@ export const useGlobalEvent = () => {
|
|
|
164
170
|
const inputStatusChangedHandler = ({ data, }) => {
|
|
165
171
|
handleInputStatusChanged(data);
|
|
166
172
|
};
|
|
173
|
+
// Token rotation (refresh timer, storage sync) must not lift a kick: re-logging in would
|
|
174
|
+
// kick the other session back. Only a different user, a logout or a reload starts over.
|
|
175
|
+
const isKickedForCurrentCredentials = () => {
|
|
176
|
+
const { isKicked, kickedUserID } = useBootStore.getState();
|
|
177
|
+
if (!isKicked)
|
|
178
|
+
return false;
|
|
179
|
+
if (!kickedUserID)
|
|
180
|
+
return true;
|
|
181
|
+
return kickedUserID === useAuthStore.getState().userID;
|
|
182
|
+
};
|
|
167
183
|
const userTokenHandler = () => {
|
|
184
|
+
// A kicked account's token is dead; refreshing it would re-login and restart the kick loop.
|
|
185
|
+
if (isKickedForCurrentCredentials())
|
|
186
|
+
return;
|
|
168
187
|
refetchChatToken(undefined, {
|
|
169
188
|
onSettled(data) {
|
|
170
189
|
var _a;
|
|
@@ -183,42 +202,129 @@ export const useGlobalEvent = () => {
|
|
|
183
202
|
const initStore = () => {
|
|
184
203
|
loadConversationList();
|
|
185
204
|
};
|
|
186
|
-
|
|
205
|
+
// Resolves true when this run ended with a usable core session.
|
|
206
|
+
const runLoginOnce = (run) => {
|
|
207
|
+
const { userID, chatToken } = useAuthStore.getState();
|
|
208
|
+
const key = `${userID}:${chatToken}`;
|
|
209
|
+
const running = loginInFlight.current;
|
|
210
|
+
if ((running === null || running === void 0 ? void 0 : running.key) === key)
|
|
211
|
+
return running.promise;
|
|
212
|
+
const start = running
|
|
213
|
+
? // A different credential must not be dropped, but the running flight may already
|
|
214
|
+
// have logged core in: logging in again would only hit "repeated login".
|
|
215
|
+
running.promise
|
|
216
|
+
.catch(() => false)
|
|
217
|
+
.then((prevOk) => runChained(run, running.userID, prevOk))
|
|
218
|
+
: run();
|
|
219
|
+
const flight = { key, userID, promise: start };
|
|
220
|
+
flight.promise = start.finally(() => {
|
|
221
|
+
if (loginInFlight.current === flight)
|
|
222
|
+
loginInFlight.current = null;
|
|
223
|
+
});
|
|
224
|
+
loginInFlight.current = flight;
|
|
225
|
+
return flight.promise;
|
|
226
|
+
};
|
|
227
|
+
const runChained = async (run, prevUserID, prevOk) => {
|
|
228
|
+
if (isKickedForCurrentCredentials())
|
|
229
|
+
return false;
|
|
230
|
+
const { userID } = useAuthStore.getState();
|
|
231
|
+
if (!(await isCoreLoggedIn()))
|
|
232
|
+
return run();
|
|
233
|
+
// Same user already logged in by the previous flight: nothing to redo.
|
|
234
|
+
if (prevUserID === userID && prevOk)
|
|
235
|
+
return true;
|
|
236
|
+
// Another user, or the previous login failed with the old token: start over with the new one.
|
|
237
|
+
return reloginUnguarded();
|
|
238
|
+
};
|
|
239
|
+
const getCoreUserID = async () => {
|
|
240
|
+
try {
|
|
241
|
+
return (await withSdkTimeout(DChatSDK.getLoginUserID(), "getLoginUserID")).data;
|
|
242
|
+
}
|
|
243
|
+
catch (_a) {
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
const tryLogin = async (isRetry = false) => {
|
|
248
|
+
var _a;
|
|
249
|
+
if (isKickedForCurrentCredentials())
|
|
250
|
+
return false;
|
|
187
251
|
const { userID, chatToken, platformID, wsAddress } = useAuthStore.getState();
|
|
188
252
|
try {
|
|
189
|
-
await DChatSDK.login({
|
|
253
|
+
await withSdkTimeout(DChatSDK.login({
|
|
190
254
|
userID,
|
|
191
255
|
token: chatToken,
|
|
192
256
|
platformID,
|
|
193
257
|
apiAddr: `${apiAddress}/chat-service`,
|
|
194
258
|
wsAddr: `${wsAddress}/chat-service/ws`,
|
|
195
|
-
});
|
|
259
|
+
}), "login", LOGIN_TIMEOUT_MS);
|
|
196
260
|
initStore();
|
|
261
|
+
return true;
|
|
197
262
|
}
|
|
198
263
|
catch (error) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
264
|
+
const errCode = (_a = error.errCode) !== null && _a !== void 0 ? _a : error.code;
|
|
265
|
+
if (errCode === 10102) {
|
|
266
|
+
// "Already logged in" is only true if core agrees; a stale tab can hold a dead session.
|
|
267
|
+
if (!isRetry && !(await isCoreLoggedIn())) {
|
|
268
|
+
return reloginUnguarded();
|
|
269
|
+
}
|
|
270
|
+
// Core may hold another user's session (shared storage); only trust it for this user.
|
|
271
|
+
if (!isRetry && (await getCoreUserID()) !== userID) {
|
|
272
|
+
return reloginUnguarded();
|
|
273
|
+
}
|
|
274
|
+
// Sync events may have fired while nobody listened (e.g. ChatProvider remounted); trust
|
|
275
|
+
// only what the boot store itself saw: connect, and the first sync finishing.
|
|
276
|
+
const boot = useBootStore.getState();
|
|
277
|
+
if (boot.hasConnected) {
|
|
278
|
+
updateConnectStatus(ConnectStatus.Connected);
|
|
279
|
+
if (boot.isFirstSyncDone)
|
|
280
|
+
updateSyncStatus(SyncStatus.Success);
|
|
281
|
+
}
|
|
206
282
|
getSelfUserInfo();
|
|
207
283
|
initStore();
|
|
208
|
-
return;
|
|
284
|
+
return true;
|
|
209
285
|
}
|
|
210
286
|
useBootStore.getState().markFailed("engine");
|
|
211
287
|
console.error(error);
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
// Drops a dead core session and logs in again with the current chat token; an
|
|
292
|
+
// expired token comes back through OnUserTokenExpired and is refreshed there.
|
|
293
|
+
const reloginUnguarded = async () => {
|
|
294
|
+
if (isKickedForCurrentCredentials())
|
|
295
|
+
return false;
|
|
296
|
+
try {
|
|
297
|
+
await withSdkTimeout(DChatSDK.logout(), "logout");
|
|
212
298
|
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
console.warn("logout before relogin", error);
|
|
301
|
+
}
|
|
302
|
+
return tryLogin(true);
|
|
303
|
+
};
|
|
304
|
+
const relogin = async () => {
|
|
305
|
+
await runLoginOnce(reloginUnguarded);
|
|
306
|
+
};
|
|
307
|
+
// The account signed in elsewhere and the token is now dead. Core logs itself
|
|
308
|
+
// out on a kick, so do not log out here or log back in: that would kick the
|
|
309
|
+
// other session in turn and loop. A reload starts a fresh sign-in.
|
|
310
|
+
const kickedOfflineHandler = () => {
|
|
311
|
+
updateConnectStatus(ConnectStatus.Disconnected);
|
|
312
|
+
useBootStore.getState().markKicked(useAuthStore.getState().userID);
|
|
213
313
|
};
|
|
314
|
+
useLoginHealthProbe(relogin);
|
|
214
315
|
const loginCheck = async () => {
|
|
215
316
|
const chatToken = useAuthStore.getState().chatToken;
|
|
216
317
|
const userID = useAuthStore.getState().userID;
|
|
217
318
|
if (!chatToken || !userID) {
|
|
218
319
|
return;
|
|
219
320
|
}
|
|
321
|
+
// A kicked user stays blocked; only a different user starts a fresh boot.
|
|
322
|
+
if (isKickedForCurrentCredentials())
|
|
323
|
+
return;
|
|
324
|
+
if (useBootStore.getState().isKicked)
|
|
325
|
+
useBootStore.getState().reset();
|
|
220
326
|
useBootStore.getState().startBoot(userID);
|
|
221
|
-
tryLogin();
|
|
327
|
+
void runLoginOnce(() => tryLogin());
|
|
222
328
|
};
|
|
223
329
|
const connectingHandler = () => {
|
|
224
330
|
useBootStore.getState().markConnecting();
|
|
@@ -282,12 +388,36 @@ export const useGlobalEvent = () => {
|
|
|
282
388
|
queryKey: [QUERY_KEYS.GET_BOT_DETAIL, botId],
|
|
283
389
|
});
|
|
284
390
|
};
|
|
391
|
+
// A closed session resets the bot flow server-side, so the open thread's
|
|
392
|
+
// cached step and menu are stale until refetched.
|
|
393
|
+
const handleSessionClosed = (conversationId) => {
|
|
394
|
+
const conversation = useConversationStore.getState().conversationData;
|
|
395
|
+
if (!conversation)
|
|
396
|
+
return;
|
|
397
|
+
const openId = conversation.conversationID;
|
|
398
|
+
if (conversationId && conversationId !== openId)
|
|
399
|
+
return;
|
|
400
|
+
// Keep the in-flight refetch an action started; cancelling it would
|
|
401
|
+
// leave that action waiting on a result that never comes.
|
|
402
|
+
void queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.GET_CONVERSATION_DETAIL, openId] }, { cancelRefetch: false });
|
|
403
|
+
void queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.GET_BOT_MAIN_MENU, openId] }, { cancelRefetch: false });
|
|
404
|
+
};
|
|
285
405
|
const businessNotificationHandler = ({ data, }) => {
|
|
286
406
|
console.log(data.key, "business notification");
|
|
287
407
|
switch (data.key) {
|
|
288
|
-
case BusinessNotificationType.SESSION_STATE_UPDATED:
|
|
289
|
-
|
|
408
|
+
case BusinessNotificationType.SESSION_STATE_UPDATED: {
|
|
409
|
+
const session = JSON.parse(data.data || "{}");
|
|
410
|
+
updateSession(session);
|
|
411
|
+
if (session.status === SessionStatus.COMPLETED) {
|
|
412
|
+
handleSessionClosed(session.conversationId);
|
|
413
|
+
}
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
case BusinessNotificationType.SESSION_CLOSED: {
|
|
417
|
+
const closed = JSON.parse(data.data || "{}");
|
|
418
|
+
handleSessionClosed(closed.conversationId);
|
|
290
419
|
break;
|
|
420
|
+
}
|
|
291
421
|
case BusinessNotificationType.SESSION_CREATED:
|
|
292
422
|
updateSession(JSON.parse(data.data || "{}"));
|
|
293
423
|
queryClient.invalidateQueries({
|
|
@@ -446,6 +576,7 @@ export const useGlobalEvent = () => {
|
|
|
446
576
|
//account
|
|
447
577
|
DChatSDK.on(CbEvents.OnUserTokenExpired, userTokenHandler);
|
|
448
578
|
DChatSDK.on(CbEvents.OnUserTokenInvalid, userTokenHandler);
|
|
579
|
+
DChatSDK.on(CbEvents.OnKickedOffline, kickedOfflineHandler);
|
|
449
580
|
DChatSDK.on(CbEvents.OnConnecting, connectingHandler);
|
|
450
581
|
DChatSDK.on(CbEvents.OnConnectSuccess, connectSuccessHandler);
|
|
451
582
|
DChatSDK.on(CbEvents.OnConnectFailed, connectFailedHandler);
|
|
@@ -481,6 +612,7 @@ export const useGlobalEvent = () => {
|
|
|
481
612
|
//account
|
|
482
613
|
DChatSDK.off(CbEvents.OnUserTokenExpired, userTokenHandler);
|
|
483
614
|
DChatSDK.off(CbEvents.OnUserTokenInvalid, userTokenHandler);
|
|
615
|
+
DChatSDK.off(CbEvents.OnKickedOffline, kickedOfflineHandler);
|
|
484
616
|
DChatSDK.off(CbEvents.OnConnecting, connectingHandler);
|
|
485
617
|
DChatSDK.off(CbEvents.OnConnectSuccess, connectSuccessHandler);
|
|
486
618
|
DChatSDK.off(CbEvents.OnConnectFailed, connectFailedHandler);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLoginHealth.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useLoginHealth.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,cAAc,QAAa,OAAO,CAAC,OAAO,CAQtD,CAAC;AAKF,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,OAAO,CAAC,IAAI,CAAC,SA6B/D,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { useLatest } from "ahooks";
|
|
3
|
+
import { LoginStatus } from "@droppii-org/wasm-client-sdk";
|
|
4
|
+
import { DChatSDK } from "../../constants/sdk";
|
|
5
|
+
import useAuthStore from "../../store/auth";
|
|
6
|
+
import useBootStore from "../../store/boot";
|
|
7
|
+
import { withSdkTimeout } from "../../utils/withSdkTimeout";
|
|
8
|
+
// A failed or hung status read counts as not logged in: either way the tab cannot talk to core.
|
|
9
|
+
export const isCoreLoggedIn = async () => {
|
|
10
|
+
try {
|
|
11
|
+
const { data } = await withSdkTimeout(DChatSDK.getLoginStatus(), "getLoginStatus");
|
|
12
|
+
return data === LoginStatus.Logged || data === LoginStatus.Logging;
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
console.warn("getLoginStatus", error);
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
// A desk tab left open for hours can come back with core logged out while the UI still
|
|
20
|
+
// shows "connected". Checking when the tab is shown again or the network returns repairs
|
|
21
|
+
// it without a reload.
|
|
22
|
+
export const useLoginHealthProbe = (relogin) => {
|
|
23
|
+
const reloginRef = useLatest(relogin);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
let probing = false;
|
|
26
|
+
const probe = async () => {
|
|
27
|
+
var _a;
|
|
28
|
+
if (probing || document.visibilityState === "hidden")
|
|
29
|
+
return;
|
|
30
|
+
const { userID, apiAddress, chatToken } = useAuthStore.getState();
|
|
31
|
+
if (!userID || !apiAddress || !chatToken || !DChatSDK)
|
|
32
|
+
return;
|
|
33
|
+
// Boot owns the first login; after a kick the token is dead and a relogin
|
|
34
|
+
// would only kick the other session again.
|
|
35
|
+
const boot = useBootStore.getState();
|
|
36
|
+
const isBooting = !boot.isFirstSyncDone && !boot.failedStep;
|
|
37
|
+
if (boot.isKicked || isBooting)
|
|
38
|
+
return;
|
|
39
|
+
probing = true;
|
|
40
|
+
try {
|
|
41
|
+
if (!(await isCoreLoggedIn()))
|
|
42
|
+
await ((_a = reloginRef.current) === null || _a === void 0 ? void 0 : _a.call(reloginRef));
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
probing = false;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
document.addEventListener("visibilitychange", probe);
|
|
49
|
+
window.addEventListener("online", probe);
|
|
50
|
+
return () => {
|
|
51
|
+
document.removeEventListener("visibilitychange", probe);
|
|
52
|
+
window.removeEventListener("online", probe);
|
|
53
|
+
};
|
|
54
|
+
}, [reloginRef]);
|
|
55
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCreateGroup.d.ts","sourceRoot":"","sources":["../../../src/hooks/group/useCreateGroup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useCreateGroup.d.ts","sourceRoot":"","sources":["../../../src/hooks/group/useCreateGroup.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEjE,UAAU,iBAAiB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAyGD,eAAO,MAAM,cAAc;6DAS2B,iBAAiB;CAkFtE,CAAC"}
|