@droppii-org/chat-sdk 0.7.3 → 0.7.4
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/boot/BootProgressBar.d.ts +13 -0
- package/dist/components/boot/BootProgressBar.d.ts.map +1 -0
- package/dist/components/boot/BootProgressBar.js +18 -0
- package/dist/components/canned-response/CannedResponsePanel.d.ts.map +1 -1
- package/dist/components/canned-response/CannedResponsePanel.js +2 -2
- package/dist/components/common/BottomSheet.d.ts +18 -0
- package/dist/components/common/BottomSheet.d.ts.map +1 -0
- package/dist/components/common/BottomSheet.js +49 -0
- package/dist/components/common/DeskPanelDrawer.d.ts.map +1 -1
- package/dist/components/common/DeskPanelDrawer.js +29 -15
- package/dist/components/common/DeskSidePanel.d.ts.map +1 -1
- package/dist/components/common/DeskSidePanel.js +1 -1
- package/dist/components/compose/ComposeNewMessageModal.d.ts.map +1 -1
- package/dist/components/compose/ComposeNewMessageModal.js +3 -3
- package/dist/components/conversation/ConversationByGroupItem.d.ts.map +1 -1
- package/dist/components/conversation/ConversationByGroupItem.js +3 -8
- package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
- package/dist/components/conversation/ConversationBySessionItem.js +3 -8
- package/dist/components/conversation/DeskConversationList.d.ts +4 -1
- package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
- package/dist/components/conversation/DeskConversationList.js +2 -2
- package/dist/components/conversation/DeskListHeader.d.ts +2 -0
- package/dist/components/conversation/DeskListHeader.d.ts.map +1 -1
- package/dist/components/conversation/DeskListHeader.js +3 -3
- package/dist/components/conversation-detail/ConversationMessageList.d.ts.map +1 -1
- package/dist/components/conversation-detail/ConversationMessageList.js +1 -1
- package/dist/components/conversation-inbox/ConversationInboxList.d.ts.map +1 -1
- package/dist/components/conversation-inbox/ConversationInboxList.js +12 -3
- package/dist/components/conversation-inbox/InboxBootStatus.d.ts +10 -0
- package/dist/components/conversation-inbox/InboxBootStatus.d.ts.map +1 -0
- package/dist/components/conversation-inbox/InboxBootStatus.js +34 -0
- package/dist/components/conversation-inbox/InboxListSkeleton.d.ts +6 -0
- package/dist/components/conversation-inbox/InboxListSkeleton.d.ts.map +1 -0
- package/dist/components/conversation-inbox/InboxListSkeleton.js +8 -0
- package/dist/components/group/GroupPanelHeader.js +1 -1
- package/dist/components/icon/strokeIconNodes.d.ts +6 -0
- package/dist/components/icon/strokeIconNodes.d.ts.map +1 -1
- package/dist/components/icon/strokeIconNodes.js +5 -0
- package/dist/components/message/MessageHeader.d.ts +3 -1
- package/dist/components/message/MessageHeader.d.ts.map +1 -1
- package/dist/components/message/MessageHeader.js +10 -6
- package/dist/components/message/MessageList.d.ts +2 -0
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +15 -4
- package/dist/components/message/SelectSession.js +1 -1
- package/dist/components/message/footer/ActionBar.d.ts.map +1 -1
- package/dist/components/message/footer/ActionBar.js +2 -2
- package/dist/components/message/footer/ComposerTabs.js +1 -1
- package/dist/components/message/footer/CrmComposerCard.js +1 -1
- package/dist/components/message/footer/EmojiPicker.d.ts +3 -1
- package/dist/components/message/footer/EmojiPicker.d.ts.map +1 -1
- package/dist/components/message/footer/EmojiPicker.js +3 -2
- package/dist/components/message/footer/EnterHandler.d.ts.map +1 -1
- package/dist/components/message/footer/EnterHandler.js +16 -2
- package/dist/components/message/footer/MediaActions.d.ts +2 -1
- package/dist/components/message/footer/MediaActions.d.ts.map +1 -1
- package/dist/components/message/footer/MediaActions.js +4 -4
- package/dist/components/message/footer/ToolbarPlugin.js +1 -1
- package/dist/components/message/footer/mention/MentionMenu.js +1 -1
- package/dist/components/message/headerIconButton.js +1 -1
- package/dist/components/message/item/MessageMoreButton.d.ts.map +1 -1
- package/dist/components/message/item/MessageMoreButton.js +18 -5
- package/dist/components/message/item/QuoteMessage.d.ts.map +1 -1
- package/dist/components/message/item/QuoteMessage.js +3 -50
- package/dist/components/message/reply/ReplyPreviewBar.d.ts.map +1 -1
- package/dist/components/message/reply/ReplyPreviewBar.js +4 -13
- package/dist/components/rich-text-editor/RichTextEditor.d.ts +3 -1
- package/dist/components/rich-text-editor/RichTextEditor.d.ts.map +1 -1
- package/dist/components/rich-text-editor/RichTextEditor.js +4 -2
- package/dist/components/search-conversation/item/SearchItemAsGroup.d.ts.map +1 -1
- package/dist/components/search-conversation/item/SearchItemAsGroup.js +3 -3
- package/dist/components/search-conversation/item/SearchItemAsMessage.d.ts.map +1 -1
- package/dist/components/search-conversation/item/SearchItemAsMessage.js +10 -0
- package/dist/components/search-conversation/item/SearchItemAsUser.d.ts.map +1 -1
- package/dist/components/search-conversation/item/SearchItemAsUser.js +6 -0
- package/dist/components/session/DeskAssignedSession.js +2 -2
- package/dist/components/session/FolderRow.js +1 -1
- package/dist/components/thread/ManualAssignPopover.d.ts.map +1 -1
- package/dist/components/thread/ManualAssignPopover.js +13 -6
- package/dist/components/thread/SessionDetailCard.js +1 -1
- package/dist/components/thread/SessionTagPicker.js +1 -1
- package/dist/components/thread/SupportSessionPanel.d.ts +1 -1
- package/dist/components/thread/SupportSessionPanel.d.ts.map +1 -1
- package/dist/components/thread/SupportSessionPanel.js +1 -1
- package/dist/components/thread/ThreadInfo.d.ts.map +1 -1
- package/dist/components/thread/ThreadInfo.js +3 -1
- package/dist/components/thread/ThreadInfoHeader.d.ts +1 -1
- package/dist/components/thread/ThreadInfoHeader.d.ts.map +1 -1
- package/dist/components/thread/ThreadInfoHeader.js +1 -1
- package/dist/components/thread/UserSection.js +2 -2
- package/dist/constants/layout.d.ts +10 -1
- package/dist/constants/layout.d.ts.map +1 -1
- package/dist/constants/layout.js +10 -1
- package/dist/hooks/common/useDeskShortcuts.d.ts.map +1 -1
- package/dist/hooks/common/useDeskShortcuts.js +3 -1
- package/dist/hooks/common/useIsDeskWorkspaceMounted.d.ts +3 -0
- package/dist/hooks/common/useIsDeskWorkspaceMounted.d.ts.map +1 -0
- package/dist/hooks/common/useIsDeskWorkspaceMounted.js +4 -0
- package/dist/hooks/common/useViewportTier.d.ts +4 -0
- package/dist/hooks/common/useViewportTier.d.ts.map +1 -0
- package/dist/hooks/common/useViewportTier.js +9 -0
- package/dist/hooks/common/useVisualViewportHeight.d.ts +10 -0
- package/dist/hooks/common/useVisualViewportHeight.d.ts.map +1 -0
- package/dist/hooks/common/useVisualViewportHeight.js +30 -0
- package/dist/hooks/conversation/useConversationList.d.ts.map +1 -1
- package/dist/hooks/conversation/useConversationList.js +31 -0
- package/dist/hooks/conversation/useDeskOfflineLock.d.ts +9 -0
- package/dist/hooks/conversation/useDeskOfflineLock.d.ts.map +1 -0
- package/dist/hooks/conversation/useDeskOfflineLock.js +46 -0
- package/dist/hooks/conversation/useDeskThreadNavigation.d.ts +19 -0
- package/dist/hooks/conversation/useDeskThreadNavigation.d.ts.map +1 -0
- package/dist/hooks/conversation/useDeskThreadNavigation.js +68 -0
- package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
- package/dist/hooks/global/useGlobalEvent.js +22 -2
- package/dist/hooks/group/useCreateGroup.d.ts.map +1 -1
- package/dist/hooks/group/useCreateGroup.js +4 -4
- package/dist/hooks/init/useChatBootPhase.d.ts +30 -0
- package/dist/hooks/init/useChatBootPhase.d.ts.map +1 -0
- package/dist/hooks/init/useChatBootPhase.js +72 -0
- package/dist/hooks/message/usePinMessage.d.ts +0 -2
- package/dist/hooks/message/usePinMessage.d.ts.map +1 -1
- package/dist/hooks/message/usePinMessage.js +7 -11
- package/dist/hooks/message/useQuotedSenderName.d.ts +14 -0
- package/dist/hooks/message/useQuotedSenderName.d.ts.map +1 -0
- package/dist/hooks/message/useQuotedSenderName.js +41 -0
- package/dist/hooks/user/useAuth.d.ts.map +1 -1
- package/dist/hooks/user/useAuth.js +2 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/locales/i18n.d.ts.map +1 -1
- package/dist/locales/i18n.js +3 -2
- package/dist/locales/i18n.ts +3 -2
- package/dist/locales/vi/boot.json +28 -0
- package/dist/locales/vi/common.json +1 -1
- package/dist/screens/conversation-detail/index.d.ts.map +1 -1
- package/dist/screens/conversation-detail/index.js +8 -3
- package/dist/screens/desk-message/DeskBootCard.d.ts +7 -0
- package/dist/screens/desk-message/DeskBootCard.d.ts.map +1 -0
- package/dist/screens/desk-message/DeskBootCard.js +69 -0
- package/dist/screens/desk-message/DeskOfflineOverlay.d.ts +10 -0
- package/dist/screens/desk-message/DeskOfflineOverlay.d.ts.map +1 -0
- package/dist/screens/desk-message/DeskOfflineOverlay.js +11 -0
- package/dist/screens/desk-message/DeskWorkspace.d.ts +7 -1
- package/dist/screens/desk-message/DeskWorkspace.d.ts.map +1 -1
- package/dist/screens/desk-message/DeskWorkspace.js +82 -13
- package/dist/screens/desk-message/DeskWorkspaceSkeleton.d.ts +10 -0
- package/dist/screens/desk-message/DeskWorkspaceSkeleton.d.ts.map +1 -0
- package/dist/screens/desk-message/DeskWorkspaceSkeleton.js +18 -0
- package/dist/screens/desk-message/PhoneThreadFallbackHeader.d.ts +8 -0
- package/dist/screens/desk-message/PhoneThreadFallbackHeader.d.ts.map +1 -0
- package/dist/screens/desk-message/PhoneThreadFallbackHeader.js +11 -0
- package/dist/screens/desk-message/index.d.ts +2 -0
- package/dist/screens/desk-message/index.d.ts.map +1 -1
- package/dist/screens/desk-message/index.js +47 -3
- package/dist/store/boot.d.ts +29 -0
- package/dist/store/boot.d.ts.map +1 -0
- package/dist/store/boot.js +21 -0
- package/dist/store/conversation.d.ts +2 -0
- package/dist/store/conversation.d.ts.map +1 -1
- package/dist/store/conversation.js +13 -12
- package/dist/store/deskPanelHost.d.ts +2 -0
- package/dist/store/deskPanelHost.d.ts.map +1 -1
- package/dist/store/deskPanelHost.js +1 -0
- package/dist/store/type.d.ts +4 -1
- package/dist/store/type.d.ts.map +1 -1
- package/dist/store/workspaceLayout.d.ts +3 -0
- package/dist/store/workspaceLayout.d.ts.map +1 -1
- package/dist/store/workspaceLayout.js +2 -0
- package/dist/styles/global.css +1 -1
- package/dist/utils/conversation.d.ts +1 -0
- package/dist/utils/conversation.d.ts.map +1 -1
- package/dist/utils/conversation.js +1 -1
- package/package.json +1 -1
- package/dist/screens/desk-message/ViewportNotice.d.ts +0 -4
- package/dist/screens/desk-message/ViewportNotice.d.ts.map +0 -1
- package/dist/screens/desk-message/ViewportNotice.js +0 -9
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
/** Visible (keyboard-aware) viewport height, set on the desk root. */
|
|
4
|
+
export const DESK_HEIGHT_VAR = "--dc-desk-height";
|
|
5
|
+
/** Same height on <html>, for portaled bottom sheets. */
|
|
6
|
+
export const VISUAL_HEIGHT_VAR = "--dc-visual-height";
|
|
7
|
+
/**
|
|
8
|
+
* Mirrors `visualViewport.height` into the height vars while `enabled` (resize only).
|
|
9
|
+
* WebKit ignores `interactive-widget`; zoom is cancelled out with `scale`.
|
|
10
|
+
*/
|
|
11
|
+
export const useVisualViewportHeight = (el, enabled) => {
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const vv = typeof window === "undefined" ? undefined : window.visualViewport;
|
|
14
|
+
if (!el || !enabled || !vv)
|
|
15
|
+
return;
|
|
16
|
+
const root = document.documentElement;
|
|
17
|
+
const sync = () => {
|
|
18
|
+
const value = `${Math.round(vv.height * (vv.scale || 1))}px`;
|
|
19
|
+
el.style.setProperty(DESK_HEIGHT_VAR, value);
|
|
20
|
+
root.style.setProperty(VISUAL_HEIGHT_VAR, value);
|
|
21
|
+
};
|
|
22
|
+
sync();
|
|
23
|
+
vv.addEventListener("resize", sync);
|
|
24
|
+
return () => {
|
|
25
|
+
vv.removeEventListener("resize", sync);
|
|
26
|
+
el.style.removeProperty(DESK_HEIGHT_VAR);
|
|
27
|
+
root.style.removeProperty(VISUAL_HEIGHT_VAR);
|
|
28
|
+
};
|
|
29
|
+
}, [el, enabled]);
|
|
30
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useConversationList.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useConversationList.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"useConversationList.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useConversationList.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAK/E,UAAU,yBAAyB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,UAAU,yBAAyB;IACjC,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AA4BD,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,yBAAyB,KAChC,yBA4GF,CAAC"}
|
|
@@ -6,7 +6,25 @@ import { useChatContext } from "../../context/ChatContext";
|
|
|
6
6
|
import { ConnectStatus, DChatApplicationType } from "../../types/chat";
|
|
7
7
|
import useAuthStore from "../../store/auth";
|
|
8
8
|
import useConversationStore from "../../store/conversation";
|
|
9
|
+
import useBootStore from "../../store/boot";
|
|
9
10
|
const toConversationListItem = (conversation) => (Object.assign(Object.assign({}, conversation), { peerType: conversation.peerType }));
|
|
11
|
+
/** After an initial load: marks an empty list as really empty, or asks for one more load. */
|
|
12
|
+
const settleAfterLoad = (startedAfterSync, bootUserID, reload) => {
|
|
13
|
+
const boot = useBootStore.getState();
|
|
14
|
+
if (boot.userID !== bootUserID)
|
|
15
|
+
return;
|
|
16
|
+
if (!startedAfterSync) {
|
|
17
|
+
// Started before the first sync finished, so it may be empty on a fresh device: load again.
|
|
18
|
+
if (boot.isFirstSyncDone)
|
|
19
|
+
reload();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// A sync we only stopped waiting on can still be filling an empty local DB; its finish reloads.
|
|
23
|
+
const isEmpty = useConversationStore.getState().conversationList.length === 0;
|
|
24
|
+
if (boot.isSyncStalled && isEmpty)
|
|
25
|
+
return;
|
|
26
|
+
boot.markListSettled();
|
|
27
|
+
};
|
|
10
28
|
export const useConversationList = (params) => {
|
|
11
29
|
const { enabled, chatCategory } = params;
|
|
12
30
|
const { connectStatus } = useChatContext();
|
|
@@ -30,6 +48,7 @@ export const useConversationList = (params) => {
|
|
|
30
48
|
if (isLoadingInitialRef.current)
|
|
31
49
|
return;
|
|
32
50
|
isLoadingInitialRef.current = true;
|
|
51
|
+
const { isFirstSyncDone: startedAfterSync, userID: bootUserID } = useBootStore.getState();
|
|
33
52
|
setIsLoading(true);
|
|
34
53
|
try {
|
|
35
54
|
const hasMore = await getConversationListByReq(false, appParams);
|
|
@@ -38,6 +57,9 @@ export const useConversationList = (params) => {
|
|
|
38
57
|
finally {
|
|
39
58
|
isLoadingInitialRef.current = false;
|
|
40
59
|
setIsLoading(false);
|
|
60
|
+
settleAfterLoad(startedAfterSync, bootUserID, () => {
|
|
61
|
+
void loadInitialRef.current();
|
|
62
|
+
});
|
|
41
63
|
}
|
|
42
64
|
}, [getConversationListByReq, appParams]);
|
|
43
65
|
// Always points at the latest loadInitial (fresh appParams) without being
|
|
@@ -70,6 +92,15 @@ export const useConversationList = (params) => {
|
|
|
70
92
|
DChatSDK.off(CbEvents.OnSyncServerFinish, onSyncFinish);
|
|
71
93
|
};
|
|
72
94
|
}, [queryEnabled]);
|
|
95
|
+
// Also covers a sync the boot screen stopped waiting on, which never fires OnSyncServerFinish.
|
|
96
|
+
const isFirstSyncDone = useBootStore((state) => state.isFirstSyncDone);
|
|
97
|
+
const wasFirstSyncDoneRef = useRef(isFirstSyncDone);
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
const justSynced = isFirstSyncDone && !wasFirstSyncDoneRef.current;
|
|
100
|
+
wasFirstSyncDoneRef.current = isFirstSyncDone;
|
|
101
|
+
if (justSynced && queryEnabled)
|
|
102
|
+
void loadInitialRef.current();
|
|
103
|
+
}, [isFirstSyncDone, queryEnabled]);
|
|
73
104
|
const items = useMemo(() => conversationList.map(toConversationListItem), [conversationList]);
|
|
74
105
|
return {
|
|
75
106
|
items,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Marks the offline overlay, which stays interactive while the rest of the desk is locked. */
|
|
2
|
+
export declare const DESK_OFFLINE_OVERLAY_ATTR = "data-desk-offline-overlay";
|
|
3
|
+
/**
|
|
4
|
+
* While offline, makes every direct child of the workspace except the overlay inert (including
|
|
5
|
+
* ones added later) and turns shortcuts off. On reconnect it restores the previous focus,
|
|
6
|
+
* else `getFallbackFocus()`, so typing never lands on <body> where single-key shortcuts fire.
|
|
7
|
+
*/
|
|
8
|
+
export declare const useDeskOfflineLock: (workspaceEl: HTMLElement | null, isOffline: boolean, getFallbackFocus: () => HTMLElement | null) => void;
|
|
9
|
+
//# sourceMappingURL=useDeskOfflineLock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDeskOfflineLock.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useDeskOfflineLock.ts"],"names":[],"mappings":"AAGA,+FAA+F;AAC/F,eAAO,MAAM,yBAAyB,8BAA8B,CAAC;AAErE;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAC7B,aAAa,WAAW,GAAG,IAAI,EAC/B,WAAW,OAAO,EAClB,kBAAkB,MAAM,WAAW,GAAG,IAAI,SAoC3C,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
/** Marks the offline overlay, which stays interactive while the rest of the desk is locked. */
|
|
4
|
+
export const DESK_OFFLINE_OVERLAY_ATTR = "data-desk-offline-overlay";
|
|
5
|
+
/**
|
|
6
|
+
* While offline, makes every direct child of the workspace except the overlay inert (including
|
|
7
|
+
* ones added later) and turns shortcuts off. On reconnect it restores the previous focus,
|
|
8
|
+
* else `getFallbackFocus()`, so typing never lands on <body> where single-key shortcuts fire.
|
|
9
|
+
*/
|
|
10
|
+
export const useDeskOfflineLock = (workspaceEl, isOffline, getFallbackFocus) => {
|
|
11
|
+
const fallbackRef = useRef(getFallbackFocus);
|
|
12
|
+
fallbackRef.current = getFallbackFocus;
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!workspaceEl || !isOffline)
|
|
15
|
+
return;
|
|
16
|
+
const active = document.activeElement;
|
|
17
|
+
const previousFocus = active instanceof HTMLElement && workspaceEl.contains(active) ? active : null;
|
|
18
|
+
const locked = new Set();
|
|
19
|
+
const lockChildren = () => {
|
|
20
|
+
Array.from(workspaceEl.children).forEach((child) => {
|
|
21
|
+
if (child.hasAttribute(DESK_OFFLINE_OVERLAY_ATTR) || locked.has(child))
|
|
22
|
+
return;
|
|
23
|
+
child.setAttribute("inert", "");
|
|
24
|
+
locked.add(child);
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
lockChildren();
|
|
28
|
+
const observer = new MutationObserver(lockChildren);
|
|
29
|
+
observer.observe(workspaceEl, { childList: true });
|
|
30
|
+
workspaceEl.setAttribute("data-desk-shortcuts-off", "");
|
|
31
|
+
return () => {
|
|
32
|
+
observer.disconnect();
|
|
33
|
+
locked.forEach((child) => child.removeAttribute("inert"));
|
|
34
|
+
workspaceEl.removeAttribute("data-desk-shortcuts-off");
|
|
35
|
+
if (document.activeElement && document.activeElement !== document.body)
|
|
36
|
+
return;
|
|
37
|
+
for (const target of [previousFocus, fallbackRef.current()]) {
|
|
38
|
+
if (!(target === null || target === void 0 ? void 0 : target.isConnected))
|
|
39
|
+
continue;
|
|
40
|
+
target.focus();
|
|
41
|
+
if (document.activeElement === target)
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}, [workspaceEl, isOffline]);
|
|
46
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** history.state key on entries pushed by opening a thread from the list or a search hit. */
|
|
2
|
+
export declare const DESK_FROM_LIST_KEY = "dcDeskFromList";
|
|
3
|
+
/** Marker value: unique per page load, so an entry left by an earlier document is never trusted. */
|
|
4
|
+
export declare const DESK_DOC_ID: string;
|
|
5
|
+
interface ThreadTarget {
|
|
6
|
+
threadId: string;
|
|
7
|
+
messageId?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Opening and leaving a thread. On phone a thread is its own history entry (marked) so the
|
|
11
|
+
* browser back button returns to the list; otherwise leaving replaces the URL, never leaves the app.
|
|
12
|
+
*/
|
|
13
|
+
export declare const useDeskThreadNavigation: () => {
|
|
14
|
+
openFromList: (target: ThreadTarget) => void;
|
|
15
|
+
openThread: (target: ThreadTarget, hostUrl: string) => void;
|
|
16
|
+
backToList: () => void;
|
|
17
|
+
};
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=useDeskThreadNavigation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDeskThreadNavigation.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useDeskThreadNavigation.ts"],"names":[],"mappings":"AAOA,6FAA6F;AAC7F,eAAO,MAAM,kBAAkB,mBAAmB,CAAC;AACnD,oGAAoG;AACpG,eAAO,MAAM,WAAW,QAAqE,CAAC;AAE9F,UAAU,YAAY;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAeD;;;GAGG;AACH,eAAO,MAAM,uBAAuB;2BAIvB,YAAY;yBAiBZ,YAAY,WAAW,MAAM;;CAwBzC,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import useConversationStore, { THREAD_URL_PARAMS } from "../../store/conversation";
|
|
4
|
+
import useDeskPanelHostStore from "../../store/deskPanelHost";
|
|
5
|
+
import useWorkspaceLayoutStore from "../../store/workspaceLayout";
|
|
6
|
+
import { useChatNavigate } from "./useChatNavigate";
|
|
7
|
+
/** history.state key on entries pushed by opening a thread from the list or a search hit. */
|
|
8
|
+
export const DESK_FROM_LIST_KEY = "dcDeskFromList";
|
|
9
|
+
/** Marker value: unique per page load, so an entry left by an earlier document is never trusted. */
|
|
10
|
+
export const DESK_DOC_ID = `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
|
|
11
|
+
const isMarkedEntry = () => { var _a; return ((_a = window.history.state) === null || _a === void 0 ? void 0 : _a[DESK_FROM_LIST_KEY]) === DESK_DOC_ID; };
|
|
12
|
+
const buildUrl = (target) => {
|
|
13
|
+
const params = new URLSearchParams(window.location.search);
|
|
14
|
+
THREAD_URL_PARAMS.forEach((key) => params.delete(key));
|
|
15
|
+
if (target) {
|
|
16
|
+
params.set("threadId", target.threadId);
|
|
17
|
+
if (target.messageId)
|
|
18
|
+
params.set("messageId", target.messageId);
|
|
19
|
+
}
|
|
20
|
+
const query = params.toString();
|
|
21
|
+
return `${window.location.pathname}${query ? `?${query}` : ""}`;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Opening and leaving a thread. On phone a thread is its own history entry (marked) so the
|
|
25
|
+
* browser back button returns to the list; otherwise leaving replaces the URL, never leaves the app.
|
|
26
|
+
*/
|
|
27
|
+
export const useDeskThreadNavigation = () => {
|
|
28
|
+
const navigate = useChatNavigate();
|
|
29
|
+
const openFromList = useCallback((target) => {
|
|
30
|
+
const url = buildUrl(target);
|
|
31
|
+
if (useDeskPanelHostStore.getState().isFullScreen) {
|
|
32
|
+
useWorkspaceLayoutStore.getState().setPhonePendingPane("thread");
|
|
33
|
+
const state = { [DESK_FROM_LIST_KEY]: DESK_DOC_ID };
|
|
34
|
+
// Thread to thread (j/k) swaps the entry so back still lands on the list.
|
|
35
|
+
if (isMarkedEntry())
|
|
36
|
+
window.history.replaceState(state, "", url);
|
|
37
|
+
else
|
|
38
|
+
window.history.pushState(state, "", url);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
navigate(url);
|
|
42
|
+
}, [navigate]);
|
|
43
|
+
/** Shared openers: the phone desk gets a marked entry, every other host keeps its own URL. */
|
|
44
|
+
const openThread = useCallback((target, hostUrl) => {
|
|
45
|
+
if (useDeskPanelHostStore.getState().isFullScreen)
|
|
46
|
+
openFromList(target);
|
|
47
|
+
else
|
|
48
|
+
navigate(hostUrl);
|
|
49
|
+
}, [navigate, openFromList]);
|
|
50
|
+
const backToList = useCallback(() => {
|
|
51
|
+
const layout = useWorkspaceLayoutStore.getState();
|
|
52
|
+
if (layout.phonePendingPane === "list")
|
|
53
|
+
return;
|
|
54
|
+
layout.setPhonePendingPane("list");
|
|
55
|
+
if (isMarkedEntry()) {
|
|
56
|
+
useConversationStore.getState().clearSelectedConversation({ skipUrl: true });
|
|
57
|
+
window.history.back();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const hadThread = new URLSearchParams(window.location.search).has("threadId");
|
|
61
|
+
// Replace first so clearing finds a clean URL and doesn't push an entry.
|
|
62
|
+
window.history.replaceState({}, "", buildUrl());
|
|
63
|
+
useConversationStore.getState().clearSelectedConversation();
|
|
64
|
+
if (!hadThread)
|
|
65
|
+
layout.setPhonePendingPane(null);
|
|
66
|
+
}, []);
|
|
67
|
+
return { openFromList, openThread, backToList };
|
|
68
|
+
};
|
|
@@ -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":"AAiDA,eAAO,MAAM,cAAc,YAmnB1B,CAAC"}
|
|
@@ -12,6 +12,7 @@ import { useChatNavigate } from "../../hooks/conversation/useChatNavigate";
|
|
|
12
12
|
import useConversationStore from "../../store/conversation";
|
|
13
13
|
import { resolveConversationBotId } from "../../utils/conversation";
|
|
14
14
|
import useAuthStore from "../../store/auth";
|
|
15
|
+
import useBootStore from "../../store/boot";
|
|
15
16
|
import { useRefetchChatToken } from "../../hooks/init/useChatToken";
|
|
16
17
|
import { updateSession } from "../../hooks/session/useUpdateSession";
|
|
17
18
|
import { QUERY_KEYS } from "../../services/query";
|
|
@@ -168,7 +169,9 @@ export const useGlobalEvent = () => {
|
|
|
168
169
|
var _a;
|
|
169
170
|
if (data) {
|
|
170
171
|
useAuthStore.getState().setChatToken((_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.token);
|
|
172
|
+
return;
|
|
171
173
|
}
|
|
174
|
+
useBootStore.getState().markFailed("engine");
|
|
172
175
|
},
|
|
173
176
|
});
|
|
174
177
|
};
|
|
@@ -195,10 +198,15 @@ export const useGlobalEvent = () => {
|
|
|
195
198
|
if (error.errCode === 10102) {
|
|
196
199
|
updateConnectStatus(ConnectStatus.Connected);
|
|
197
200
|
updateSyncStatus(SyncStatus.Success);
|
|
201
|
+
// Already logged in (e.g. ChatProvider remounted): sync events may have fired while
|
|
202
|
+
// nobody listened, so stop waiting for them rather than risk a boot screen that never ends.
|
|
203
|
+
useBootStore.getState().markConnected();
|
|
204
|
+
useBootStore.getState().markFirstSyncDone();
|
|
198
205
|
getSelfUserInfo();
|
|
199
206
|
initStore();
|
|
200
207
|
return;
|
|
201
208
|
}
|
|
209
|
+
useBootStore.getState().markFailed("engine");
|
|
202
210
|
console.error(error);
|
|
203
211
|
}
|
|
204
212
|
};
|
|
@@ -208,27 +216,39 @@ export const useGlobalEvent = () => {
|
|
|
208
216
|
if (!chatToken || !userID) {
|
|
209
217
|
return;
|
|
210
218
|
}
|
|
219
|
+
useBootStore.getState().startBoot(userID);
|
|
211
220
|
tryLogin();
|
|
212
221
|
};
|
|
213
222
|
const connectingHandler = () => {
|
|
223
|
+
useBootStore.getState().markConnecting();
|
|
214
224
|
updateConnectStatus(ConnectStatus.Connecting);
|
|
215
225
|
};
|
|
216
226
|
const connectSuccessHandler = () => {
|
|
227
|
+
useBootStore.getState().markConnected();
|
|
217
228
|
updateConnectStatus(ConnectStatus.Connected);
|
|
218
229
|
};
|
|
219
230
|
const connectFailedHandler = () => {
|
|
231
|
+
// After boot, core keeps retrying on its own; the boot screen only shows "connect" then.
|
|
232
|
+
if (!useBootStore.getState().isFirstSyncDone) {
|
|
233
|
+
useBootStore.getState().markFailed("connect");
|
|
234
|
+
}
|
|
220
235
|
updateConnectStatus(ConnectStatus.Disconnected);
|
|
221
236
|
};
|
|
222
237
|
const syncStartHandler = ({ data }) => {
|
|
223
238
|
updateSyncStatus(SyncStatus.Loading);
|
|
224
239
|
};
|
|
240
|
+
const syncProgressHandler = ({ data }) => {
|
|
241
|
+
useBootStore.getState().setSyncProgress(data);
|
|
242
|
+
};
|
|
225
243
|
const syncFinishHandler = () => {
|
|
244
|
+
useBootStore.getState().markFirstSyncDone();
|
|
226
245
|
updateSyncStatus(SyncStatus.Success);
|
|
227
246
|
getSelfUserInfo();
|
|
228
247
|
loadConversationList();
|
|
229
248
|
void refreshCurrentGroupState();
|
|
230
249
|
};
|
|
231
250
|
const syncFailedHandler = () => {
|
|
251
|
+
useBootStore.getState().markFirstSyncDone();
|
|
232
252
|
updateSyncStatus(SyncStatus.Failed);
|
|
233
253
|
};
|
|
234
254
|
// The bot behind the open thread changed (flow swapped, type switched,
|
|
@@ -419,7 +439,7 @@ export const useGlobalEvent = () => {
|
|
|
419
439
|
DChatSDK.on(CbEvents.OnConnectFailed, connectFailedHandler);
|
|
420
440
|
// sync
|
|
421
441
|
DChatSDK.on(CbEvents.OnSyncServerStart, syncStartHandler);
|
|
422
|
-
|
|
442
|
+
DChatSDK.on(CbEvents.OnSyncServerProgress, syncProgressHandler);
|
|
423
443
|
DChatSDK.on(CbEvents.OnSyncServerFinish, syncFinishHandler);
|
|
424
444
|
DChatSDK.on(CbEvents.OnSyncServerFailed, syncFailedHandler);
|
|
425
445
|
// message
|
|
@@ -454,7 +474,7 @@ export const useGlobalEvent = () => {
|
|
|
454
474
|
DChatSDK.off(CbEvents.OnConnectFailed, connectFailedHandler);
|
|
455
475
|
// sync
|
|
456
476
|
DChatSDK.off(CbEvents.OnSyncServerStart, syncStartHandler);
|
|
457
|
-
|
|
477
|
+
DChatSDK.off(CbEvents.OnSyncServerProgress, syncProgressHandler);
|
|
458
478
|
DChatSDK.off(CbEvents.OnSyncServerFinish, syncFinishHandler);
|
|
459
479
|
DChatSDK.off(CbEvents.OnSyncServerFailed, syncFailedHandler);
|
|
460
480
|
// message
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCreateGroup.d.ts","sourceRoot":"","sources":["../../../src/hooks/group/useCreateGroup.ts"],"names":[],"mappings":"AAYA,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;AAqCD,eAAO,MAAM,cAAc;6DAQ2B,iBAAiB;
|
|
1
|
+
{"version":3,"file":"useCreateGroup.d.ts","sourceRoot":"","sources":["../../../src/hooks/group/useCreateGroup.ts"],"names":[],"mappings":"AAYA,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;AAqCD,eAAO,MAAM,cAAc;6DAQ2B,iBAAiB;CA4DtE,CAAC"}
|
|
@@ -6,7 +6,7 @@ import { useTranslation } from "react-i18next";
|
|
|
6
6
|
import { GroupType, GroupVerificationType, GroupVisibility, SessionType } from "@droppii-org/wasm-client-sdk";
|
|
7
7
|
import { DChatSDK } from "../../constants/sdk";
|
|
8
8
|
import useAuthStore from "../../store/auth";
|
|
9
|
-
import {
|
|
9
|
+
import { useDeskThreadNavigation } from "../../hooks/conversation/useDeskThreadNavigation";
|
|
10
10
|
import { generateDefaultGroupName } from "../../utils/groupName";
|
|
11
11
|
import { QUERY_KEYS } from "../../services/query";
|
|
12
12
|
const GET_CONVERSATION_RETRY_COUNT = 4;
|
|
@@ -43,7 +43,7 @@ const getOneConversationWithRetry = async (groupID) => {
|
|
|
43
43
|
export const useCreateGroup = () => {
|
|
44
44
|
const { t } = useTranslation("conversation-inbox");
|
|
45
45
|
const { t: tCommon } = useTranslation();
|
|
46
|
-
const
|
|
46
|
+
const { openThread } = useDeskThreadNavigation();
|
|
47
47
|
const queryClient = useQueryClient();
|
|
48
48
|
const userID = useAuthStore((state) => state.userID);
|
|
49
49
|
const createGroup = useCallback(async ({ groupName, isPublic, members, faceURL }) => {
|
|
@@ -80,7 +80,7 @@ export const useCreateGroup = () => {
|
|
|
80
80
|
});
|
|
81
81
|
const searchParams = new URLSearchParams(window.location.search);
|
|
82
82
|
searchParams.set("threadId", conversation.conversationID);
|
|
83
|
-
|
|
83
|
+
openThread({ threadId: conversation.conversationID }, `${window.location.pathname}?${searchParams.toString()}`);
|
|
84
84
|
return conversation;
|
|
85
85
|
}
|
|
86
86
|
catch (error) {
|
|
@@ -88,6 +88,6 @@ export const useCreateGroup = () => {
|
|
|
88
88
|
message.error(errorMessage);
|
|
89
89
|
throw error;
|
|
90
90
|
}
|
|
91
|
-
}, [
|
|
91
|
+
}, [openThread, t, tCommon, userID, queryClient]);
|
|
92
92
|
return { createGroup };
|
|
93
93
|
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ChatBootFailedStep } from "../../store/boot";
|
|
2
|
+
import { ConnectStatus } from "../../types/chat";
|
|
3
|
+
export type ChatBootPhase = "engine" | "connect" | "sync" | "ready" | "error";
|
|
4
|
+
export interface ChatBootState {
|
|
5
|
+
phase: ChatBootPhase;
|
|
6
|
+
/** Step that failed when `phase` is "error". */
|
|
7
|
+
failedStep: ChatBootFailedStep | null;
|
|
8
|
+
/** 0-100, meaningful in the "sync" phase. */
|
|
9
|
+
syncProgress: number;
|
|
10
|
+
/** The current step has run longer than the caller's `slowAfterMs`. */
|
|
11
|
+
isSlow: boolean;
|
|
12
|
+
/** False for the first moments, so a fast cached boot never flashes status text. */
|
|
13
|
+
isStatusVisible: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare const BOOT_STATUS_DELAY_MS = 600;
|
|
16
|
+
/** A connect or sync step that sees no change for this long is treated as stuck. */
|
|
17
|
+
export declare const BOOT_STALL_MS = 30000;
|
|
18
|
+
interface ResolveBootPhaseInput {
|
|
19
|
+
connectStatus: ConnectStatus;
|
|
20
|
+
hasConnected: boolean;
|
|
21
|
+
isFirstSyncDone: boolean;
|
|
22
|
+
failedStep: ChatBootFailedStep | null;
|
|
23
|
+
}
|
|
24
|
+
export declare const resolveChatBootPhase: ({ connectStatus, hasConnected, isFirstSyncDone, failedStep, }: ResolveBootPhaseInput) => ChatBootPhase;
|
|
25
|
+
/** Where the chat engine is in its boot: WASM + login, connect, first sync, ready, or failed. */
|
|
26
|
+
export declare const useChatBootPhase: ({ slowAfterMs, }: {
|
|
27
|
+
slowAfterMs: number;
|
|
28
|
+
}) => ChatBootState;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=useChatBootPhase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useChatBootPhase.d.ts","sourceRoot":"","sources":["../../../src/hooks/init/useChatBootPhase.ts"],"names":[],"mappings":"AAIA,OAAqB,EAAE,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAE9E,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,aAAa,CAAC;IACrB,gDAAgD;IAChD,UAAU,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACtC,6CAA6C;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,MAAM,EAAE,OAAO,CAAC;IAChB,oFAAoF;IACpF,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,eAAO,MAAM,oBAAoB,MAAM,CAAC;AACxC,oFAAoF;AACpF,eAAO,MAAM,aAAa,QAAS,CAAC;AAEpC,UAAU,qBAAqB;IAC7B,aAAa,EAAE,aAAa,CAAC;IAC7B,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACvC;AAED,eAAO,MAAM,oBAAoB,GAAI,+DAKlC,qBAAqB,KAAG,aAO1B,CAAC;AAEF,iGAAiG;AACjG,eAAO,MAAM,gBAAgB,GAAI,kBAE9B;IACD,WAAW,EAAE,MAAM,CAAC;CACrB,KAAG,aAwDH,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { useChatContext } from "../../context/ChatContext";
|
|
4
|
+
import useBootStore from "../../store/boot";
|
|
5
|
+
import { ConnectStatus } from "../../types/chat";
|
|
6
|
+
export const BOOT_STATUS_DELAY_MS = 600;
|
|
7
|
+
/** A connect or sync step that sees no change for this long is treated as stuck. */
|
|
8
|
+
export const BOOT_STALL_MS = 30000;
|
|
9
|
+
export const resolveChatBootPhase = ({ connectStatus, hasConnected, isFirstSyncDone, failedStep, }) => {
|
|
10
|
+
if (connectStatus === ConnectStatus.Connected) {
|
|
11
|
+
return isFirstSyncDone ? "ready" : "sync";
|
|
12
|
+
}
|
|
13
|
+
if (failedStep)
|
|
14
|
+
return "error";
|
|
15
|
+
if (connectStatus === ConnectStatus.Connecting || hasConnected)
|
|
16
|
+
return "connect";
|
|
17
|
+
return "engine";
|
|
18
|
+
};
|
|
19
|
+
/** Where the chat engine is in its boot: WASM + login, connect, first sync, ready, or failed. */
|
|
20
|
+
export const useChatBootPhase = ({ slowAfterMs, }) => {
|
|
21
|
+
const { connectStatus } = useChatContext();
|
|
22
|
+
const hasConnected = useBootStore((state) => state.hasConnected);
|
|
23
|
+
const isFirstSyncDone = useBootStore((state) => state.isFirstSyncDone);
|
|
24
|
+
const failedStep = useBootStore((state) => state.failedStep);
|
|
25
|
+
const syncProgress = useBootStore((state) => state.syncProgress);
|
|
26
|
+
const basePhase = resolveChatBootPhase({
|
|
27
|
+
connectStatus,
|
|
28
|
+
hasConnected,
|
|
29
|
+
isFirstSyncDone,
|
|
30
|
+
failedStep,
|
|
31
|
+
});
|
|
32
|
+
// Sync progress restarts the slow/stall timers: a moving sync is not stuck.
|
|
33
|
+
const stepKey = basePhase === "sync" ? `sync:${syncProgress}` : basePhase;
|
|
34
|
+
const [isDelayOver, setIsDelayOver] = useState(false);
|
|
35
|
+
const [slowStepKey, setSlowStepKey] = useState(null);
|
|
36
|
+
const [stalledStepKey, setStalledStepKey] = useState(null);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const timer = setTimeout(() => setIsDelayOver(true), BOOT_STATUS_DELAY_MS);
|
|
39
|
+
return () => clearTimeout(timer);
|
|
40
|
+
}, []);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (basePhase === "ready" || basePhase === "error")
|
|
43
|
+
return;
|
|
44
|
+
const slowTimer = setTimeout(() => setSlowStepKey(stepKey), slowAfterMs);
|
|
45
|
+
// No stall timer for "engine": a first WASM download on a slow network can take minutes.
|
|
46
|
+
// A stuck sync falls through to the UI it already has; a stuck connect becomes an error.
|
|
47
|
+
const stallTimer = basePhase === "engine"
|
|
48
|
+
? undefined
|
|
49
|
+
: setTimeout(() => {
|
|
50
|
+
if (basePhase === "sync")
|
|
51
|
+
useBootStore.getState().markSyncStalled();
|
|
52
|
+
else
|
|
53
|
+
setStalledStepKey(stepKey);
|
|
54
|
+
}, BOOT_STALL_MS);
|
|
55
|
+
return () => {
|
|
56
|
+
clearTimeout(slowTimer);
|
|
57
|
+
clearTimeout(stallTimer);
|
|
58
|
+
// Re-entering the same step later (e.g. "connect" after a drop) starts from scratch.
|
|
59
|
+
setSlowStepKey(null);
|
|
60
|
+
setStalledStepKey(null);
|
|
61
|
+
};
|
|
62
|
+
}, [basePhase, stepKey, slowAfterMs]);
|
|
63
|
+
const isStalled = basePhase === "connect" && stalledStepKey === stepKey;
|
|
64
|
+
const phase = isStalled ? "error" : basePhase;
|
|
65
|
+
return {
|
|
66
|
+
phase,
|
|
67
|
+
failedStep: isStalled ? "connect" : phase === "error" ? failedStep : null,
|
|
68
|
+
syncProgress,
|
|
69
|
+
isSlow: slowStepKey === stepKey,
|
|
70
|
+
isStatusVisible: isDelayOver || phase === "error",
|
|
71
|
+
};
|
|
72
|
+
};
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
export declare const usePinMessage: () => {
|
|
2
2
|
pinMessage: (conversationID: string, clientMsgID: string) => Promise<void>;
|
|
3
3
|
unpinMessage: (conversationID: string, clientMsgID: string) => Promise<void>;
|
|
4
|
-
pinLoading: boolean;
|
|
5
|
-
unpinLoading: boolean;
|
|
6
4
|
};
|
|
7
5
|
//# sourceMappingURL=usePinMessage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePinMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/usePinMessage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"usePinMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/usePinMessage.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,aAAa;iCAIC,MAAM,eAAe,MAAM;mCAiB3B,MAAM,eAAe,MAAM;CAkBrD,CAAC"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCallback } from "react";
|
|
2
2
|
import { DChatSDK } from "../../constants/sdk";
|
|
3
3
|
import useAuthStore from "../../store/auth";
|
|
4
4
|
import { updateOneMessage } from "../../hooks/message/useMessage";
|
|
5
5
|
import { fetchPinnedMessages } from "./usePinnedMessages";
|
|
6
|
+
// Plain callbacks, not ahooks useRequest: unpinning the last pin unmounts the
|
|
7
|
+
// pinned panel, and ahooks >=3.10 rejects a request whose owner unmounts.
|
|
6
8
|
export const usePinMessage = () => {
|
|
7
9
|
const userID = useAuthStore((state) => state.userID);
|
|
8
|
-
const
|
|
10
|
+
const pinMessage = useCallback(async (conversationID, clientMsgID) => {
|
|
9
11
|
await DChatSDK.pinMsg({
|
|
10
12
|
conversationID,
|
|
11
13
|
clientMsgID,
|
|
@@ -17,10 +19,8 @@ export const usePinMessage = () => {
|
|
|
17
19
|
pinnedTime: Date.now(),
|
|
18
20
|
});
|
|
19
21
|
await fetchPinnedMessages(conversationID);
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
});
|
|
23
|
-
const { runAsync: unpinMessage, loading: unpinLoading } = useRequest(async (conversationID, clientMsgID) => {
|
|
22
|
+
}, [userID]);
|
|
23
|
+
const unpinMessage = useCallback(async (conversationID, clientMsgID) => {
|
|
24
24
|
await DChatSDK.unpinMsg({
|
|
25
25
|
conversationID,
|
|
26
26
|
clientMsgID,
|
|
@@ -30,13 +30,9 @@ export const usePinMessage = () => {
|
|
|
30
30
|
isPinned: false,
|
|
31
31
|
});
|
|
32
32
|
await fetchPinnedMessages(conversationID);
|
|
33
|
-
},
|
|
34
|
-
manual: true,
|
|
35
|
-
});
|
|
33
|
+
}, []);
|
|
36
34
|
return {
|
|
37
35
|
pinMessage,
|
|
38
36
|
unpinMessage,
|
|
39
|
-
pinLoading,
|
|
40
|
-
unpinLoading,
|
|
41
37
|
};
|
|
42
38
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type QuotedSender = {
|
|
2
|
+
sendID?: string;
|
|
3
|
+
senderNickname?: string;
|
|
4
|
+
} | null;
|
|
5
|
+
/**
|
|
6
|
+
* Who a quote is attributed to; the sent quote and the reply bar share it.
|
|
7
|
+
* `name` is empty when the viewer quoted their own message (`isMe`).
|
|
8
|
+
*/
|
|
9
|
+
export declare const useQuotedSenderName: (quoted?: QuotedSender) => {
|
|
10
|
+
isMe: boolean;
|
|
11
|
+
name: string;
|
|
12
|
+
};
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=useQuotedSenderName.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useQuotedSenderName.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useQuotedSenderName.ts"],"names":[],"mappings":"AASA,KAAK,YAAY,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC;AAExE;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,YAAY;;;CAqCxD,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import useAuthStore from "../../store/auth";
|
|
3
|
+
import useConversationStore from "../../store/conversation";
|
|
4
|
+
import { useUserUsername } from "../../hooks/user/useUserUsername";
|
|
5
|
+
import { BOT_SESSION_CONVERSATION_PREFIX, isSupportSessionAgentView, } from "../../utils/conversation";
|
|
6
|
+
/**
|
|
7
|
+
* Who a quote is attributed to; the sent quote and the reply bar share it.
|
|
8
|
+
* `name` is empty when the viewer quoted their own message (`isMe`).
|
|
9
|
+
*/
|
|
10
|
+
export const useQuotedSenderName = (quoted) => {
|
|
11
|
+
const userID = useAuthStore((state) => state.userID);
|
|
12
|
+
const isCrm = useAuthStore((state) => state.isCrm);
|
|
13
|
+
const conversationData = useConversationStore((state) => state.conversationData);
|
|
14
|
+
const isMe = !!quoted && quoted.sendID === userID;
|
|
15
|
+
// Outside CRM an agent tells senders apart by username.
|
|
16
|
+
const isAgentView = !isCrm && isSupportSessionAgentView(conversationData, userID);
|
|
17
|
+
const quotedUsername = useUserUsername(isAgentView && !isMe ? quoted === null || quoted === void 0 ? void 0 : quoted.sendID : undefined);
|
|
18
|
+
const isSupportThread = useMemo(() => {
|
|
19
|
+
var _a, _b, _c, _d;
|
|
20
|
+
// Core sometimes leaves ex empty; the conversation id still marks a bot thread.
|
|
21
|
+
const conversationID = (_a = conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID) !== null && _a !== void 0 ? _a : "";
|
|
22
|
+
if (conversationID.startsWith(BOT_SESSION_CONVERSATION_PREFIX))
|
|
23
|
+
return true;
|
|
24
|
+
try {
|
|
25
|
+
return Boolean((_d = (_c = (_b = JSON.parse((conversationData === null || conversationData === void 0 ? void 0 : conversationData.ex) || "{}")) === null || _b === void 0 ? void 0 : _b.sessionInfo) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.botId);
|
|
26
|
+
}
|
|
27
|
+
catch (_e) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}, [conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID, conversationData === null || conversationData === void 0 ? void 0 : conversationData.ex]);
|
|
31
|
+
if (!quoted || isMe)
|
|
32
|
+
return { isMe, name: "" };
|
|
33
|
+
if (isAgentView) {
|
|
34
|
+
return { isMe, name: quotedUsername || quoted.senderNickname || "" };
|
|
35
|
+
}
|
|
36
|
+
// The session owner only ever sees the thread's own name for the other side.
|
|
37
|
+
if (!isCrm && isSupportThread) {
|
|
38
|
+
return { isMe, name: (conversationData === null || conversationData === void 0 ? void 0 : conversationData.showName) || "" };
|
|
39
|
+
}
|
|
40
|
+
return { isMe, name: quoted.senderNickname || "" };
|
|
41
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../../../src/hooks/user/useAuth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../../../src/hooks/user/useAuth.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,YAAY;;CA8BxB,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Platform } from "@droppii-org/wasm-client-sdk";
|
|
2
2
|
import { DChatSDK } from "../../constants/sdk";
|
|
3
3
|
import useAuthStore from "../../store/auth";
|
|
4
|
+
import useBootStore from "../../store/boot";
|
|
4
5
|
import useConversationStore from "../../store/conversation";
|
|
5
6
|
import useMessageDraftStore from "../../store/messageDraft";
|
|
6
7
|
import useTypingStore from "../../store/typing";
|
|
@@ -12,6 +13,7 @@ export const useDChatAuth = () => {
|
|
|
12
13
|
const res = await DChatSDK.logout();
|
|
13
14
|
const applicationType = useAuthStore.getState().applicationType;
|
|
14
15
|
resetConversationStore();
|
|
16
|
+
useBootStore.getState().reset();
|
|
15
17
|
clearAllDrafts();
|
|
16
18
|
useTypingStore.getState().clearTypers();
|
|
17
19
|
initAuthStore({
|