@droppii-org/chat-sdk 0.6.33 → 0.6.34
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/hooks/conversation/useResolveConversation.d.ts.map +1 -1
- package/dist/hooks/conversation/useResolveConversation.js +54 -13
- package/dist/store/conversation.d.ts.map +1 -1
- package/dist/store/conversation.js +5 -4
- package/dist/utils/conversation.d.ts +11 -0
- package/dist/utils/conversation.d.ts.map +1 -1
- package/dist/utils/conversation.js +14 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useResolveConversation.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useResolveConversation.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"useResolveConversation.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useResolveConversation.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,gBAAgB,EAIjB,MAAM,8BAA8B,CAAC;AAatC,MAAM,WAAW,4BAA4B;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAsDD,eAAO,MAAM,wBAAwB,GACnC,OAAO,MAAM,KACZ,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAWjC,CAAC;AAmDF,eAAO,MAAM,sBAAsB,GAAI,wDAMpC,4BAA4B;;;CA8J9B,CAAC"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
|
-
import { SessionType } from "@droppii-org/wasm-client-sdk";
|
|
3
|
+
import { GroupStatus, GroupVisibility, SessionType, } from "@droppii-org/wasm-client-sdk";
|
|
4
4
|
import { DChatSDK } from "../../constants/sdk";
|
|
5
5
|
import { apiInstance } from "../../services/api";
|
|
6
6
|
import { ENDPOINTS } from "../../services/routes";
|
|
7
7
|
import { ConnectStatus, SyncStatus } from "../../types/chat";
|
|
8
8
|
import { useChatContext } from "../../context/ChatContext";
|
|
9
9
|
import useConversationStore from "../../store/conversation";
|
|
10
|
-
import { resolveConversationBotId } from "../../utils/conversation";
|
|
10
|
+
import { getGroupIdFromConversationId, resolveConversationBotId, } from "../../utils/conversation";
|
|
11
11
|
const MISSING_BOT_GROUP_ERROR = "Missing groupId from get-or-create bot conversation response";
|
|
12
12
|
const CONVERSATION_NOT_FOUND_ERROR = "Conversation not found";
|
|
13
13
|
const RESOLVE_FAILED_ERROR = "Failed to resolve conversation";
|
|
@@ -20,6 +20,35 @@ const fetchBySource = async (sourceID, sessionType) => {
|
|
|
20
20
|
const { data } = await DChatSDK.getOneConversation({ sourceID, sessionType });
|
|
21
21
|
return data !== null && data !== void 0 ? data : null;
|
|
22
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* A public group is viewable by non-members — the detail screen renders the
|
|
25
|
+
* join button off isViewOnlyMember — so an unresolvable public group must
|
|
26
|
+
* never get the "you don't have access" modal. Only assert no-access when the
|
|
27
|
+
* group info actually says so: a fetch failure returns false, because guessing
|
|
28
|
+
* wrong here claims a permission problem that may not exist.
|
|
29
|
+
*
|
|
30
|
+
* CAVEAT: getSpecifiedGroupsInfo reads the LOCAL db, which only holds groups
|
|
31
|
+
* the user belongs to — an empty response therefore means "not in local db",
|
|
32
|
+
* covering "never joined" as much as "disbanded". Until a BE lookup by groupId
|
|
33
|
+
* exists for non-members, the !group branch below blocks public groups the
|
|
34
|
+
* user has not joined.
|
|
35
|
+
*/
|
|
36
|
+
const shouldBlockGroupAccess = async (groupId) => {
|
|
37
|
+
try {
|
|
38
|
+
const { data } = await DChatSDK.getSpecifiedGroupsInfo([groupId]);
|
|
39
|
+
const group = data === null || data === void 0 ? void 0 : data[0];
|
|
40
|
+
// Empty response = group no longer exists, same as Dismissed.
|
|
41
|
+
if (!group)
|
|
42
|
+
return true;
|
|
43
|
+
if (group.status === GroupStatus.Dismissed)
|
|
44
|
+
return true;
|
|
45
|
+
return group.visibility !== GroupVisibility.Public;
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
console.error("shouldBlockGroupAccess", error);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
23
52
|
export const fetchConversationByBotId = async (botId) => {
|
|
24
53
|
var _a;
|
|
25
54
|
const { data: botConversationResponse } = await apiInstance.get(ENDPOINTS.chatService.getConversationByBotId(botId));
|
|
@@ -30,14 +59,15 @@ export const fetchConversationByBotId = async (botId) => {
|
|
|
30
59
|
return fetchBySource(resolvedGroupId, SessionType.Group);
|
|
31
60
|
};
|
|
32
61
|
const fetchConversationByTarget = ({ conversationId, userId, groupId, botId, }) => {
|
|
33
|
-
|
|
34
|
-
return fetchByConversationId(conversationId);
|
|
62
|
+
const targetGroupId = groupId || getGroupIdFromConversationId(conversationId);
|
|
35
63
|
// groupId before userId: a group-message notification link carries both
|
|
36
64
|
// (the group, plus the sender's userId as secondary info) — resolving
|
|
37
65
|
// userId first would wrongly open a 1:1 thread with the sender instead
|
|
38
66
|
// of the group.
|
|
39
|
-
if (
|
|
40
|
-
return fetchBySource(
|
|
67
|
+
if (targetGroupId)
|
|
68
|
+
return fetchBySource(targetGroupId, SessionType.Group);
|
|
69
|
+
if (conversationId)
|
|
70
|
+
return fetchByConversationId(conversationId);
|
|
41
71
|
if (botId)
|
|
42
72
|
return fetchConversationByBotId(botId);
|
|
43
73
|
if (userId)
|
|
@@ -131,14 +161,25 @@ export const useResolveConversation = ({ conversationId, userId, groupId, botId,
|
|
|
131
161
|
if (cancelled)
|
|
132
162
|
return;
|
|
133
163
|
// A conversationId/groupId deep link that resolves to nothing means
|
|
134
|
-
// the thread never synced to this device
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
// below, so surface the same modal instead of the generic
|
|
138
|
-
// resolve-failed empty state. Left on the error path deliberately:
|
|
139
|
-
// botId (get-or-create failed) and userId (1:1 peer) are genuine
|
|
140
|
-
// failures, not access denials, and the group wording would be wrong.
|
|
164
|
+
// the thread never synced to this device. Left on the error path
|
|
165
|
+
// deliberately: botId (get-or-create failed) and userId (1:1 peer) are
|
|
166
|
+
// genuine failures, not access denials.
|
|
141
167
|
if (!resolved && (conversationId || groupId)) {
|
|
168
|
+
// Check the group before claiming no access: a public group the
|
|
169
|
+
// user simply hasn't joined belongs in the detail screen (join
|
|
170
|
+
// button), not behind this modal. A ?conversationId= target names
|
|
171
|
+
// its group inside the id ("sg_<groupID>"), so that entry point
|
|
172
|
+
// gets the same treatment as an explicit ?groupId=. A 1:1 id yields
|
|
173
|
+
// "" and falls through to the block, which is correct — there is no
|
|
174
|
+
// group whose visibility could excuse it.
|
|
175
|
+
const targetGroupId = groupId || getGroupIdFromConversationId(conversationId);
|
|
176
|
+
if (targetGroupId && !(await shouldBlockGroupAccess(targetGroupId))) {
|
|
177
|
+
if (cancelled)
|
|
178
|
+
return;
|
|
179
|
+
throw new Error(CONVERSATION_NOT_FOUND_ERROR);
|
|
180
|
+
}
|
|
181
|
+
if (cancelled)
|
|
182
|
+
return;
|
|
142
183
|
rejectedTargetRef.current = currentTargetKey;
|
|
143
184
|
pendingTargetRef.current = null;
|
|
144
185
|
clearSelectedConversation();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/store/conversation.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAY3C,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,SAElD,CAAC;AAUF,eAAO,MAAM,cAAc,GAAI,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,SAEhE,CAAC;AAEF,QAAA,MAAM,oBAAoB,
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/store/conversation.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAY3C,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,SAElD,CAAC;AAUF,eAAO,MAAM,cAAc,GAAI,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,SAEhE,CAAC;AAEF,QAAA,MAAM,oBAAoB,gFAmcvB,CAAC;AAEJ,eAAe,oBAAoB,CAAC"}
|
|
@@ -174,7 +174,8 @@ const useConversationStore = create((set, get) => ({
|
|
|
174
174
|
return;
|
|
175
175
|
}
|
|
176
176
|
const notAMember = !get().currentMemberInGroup.has(groupID);
|
|
177
|
-
const isPublic = ((_a = get().currentGroupInfo.get(groupID)) === null || _a === void 0 ? void 0 : _a.visibility) ===
|
|
177
|
+
const isPublic = ((_a = get().currentGroupInfo.get(groupID)) === null || _a === void 0 ? void 0 : _a.visibility) ===
|
|
178
|
+
GroupVisibility.Public;
|
|
178
179
|
if (notAMember && isPublic) {
|
|
179
180
|
set({ isViewOnlyMember: true });
|
|
180
181
|
}
|
|
@@ -197,9 +198,9 @@ const useConversationStore = create((set, get) => ({
|
|
|
197
198
|
try {
|
|
198
199
|
const { data } = await DChatSDK.getSpecifiedGroupsInfo([groupID]);
|
|
199
200
|
// Disbanded group: empty array, not a thrown error — don't crash below.
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
//
|
|
201
|
+
// Recorded so updateCurrentConversation's gate can reject the thread.
|
|
202
|
+
// CAVEAT: this call reads the LOCAL db, so an empty response also covers
|
|
203
|
+
// "never joined" — see shouldBlockGroupAccess in useResolveConversation.
|
|
203
204
|
if (!data[0]) {
|
|
204
205
|
set({ disbandedGroupID: groupID });
|
|
205
206
|
return;
|
|
@@ -10,4 +10,15 @@ export declare const resolveConversationDisplayName: (item: ConversationItem, t:
|
|
|
10
10
|
export declare const resolveConversationAvatarUrl: (item: ConversationItem) => string | null;
|
|
11
11
|
export declare const resolvePeerBadgeLabel: (peerType: PeerType, t: (key: string) => string) => string;
|
|
12
12
|
export declare const resolvePeerBadgeColor: (peerType: PeerType) => "" | "bg-[var(--dc-color-mall-badge)]" | "bg-[var(--dc-color-bot-badge)]";
|
|
13
|
+
/**
|
|
14
|
+
* Group conversationIds are formatted as "sg_<groupID>" — derive the groupID
|
|
15
|
+
* straight from the id when a full ConversationItem isn't available yet (e.g.
|
|
16
|
+
* a non-member previewing a public group via deeplink has no synced
|
|
17
|
+
* conversation object to read groupID off of).
|
|
18
|
+
*
|
|
19
|
+
* Returns "" for anything else, including 1:1 ids ("si_<userA>_<userB>"):
|
|
20
|
+
* those have no group, so callers must treat "" as "not a group", never as
|
|
21
|
+
* "group unknown".
|
|
22
|
+
*/
|
|
23
|
+
export declare const getGroupIdFromConversationId: (conversationId?: string) => string;
|
|
13
24
|
//# sourceMappingURL=conversation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/utils/conversation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/utils/conversation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,KAAK,iBAAiB,EACvB,MAAM,sBAAsB,CAAC;AAW9B,eAAO,MAAM,sBAAsB,GACjC,IAAI,MAAM,GAAG,SAAS,KACrB,iBAcF,CAAC;AAEF,eAAO,MAAM,+BAA+B,GAC1C,MAAM,gBAAgB,KACrB,YAA4D,CAAC;AAEhE,eAAO,MAAM,wBAAwB,GAAI,MAAM,gBAAgB,KAAG,MAOjE,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,MAAM,gBAAgB,YAChB,CAAC;AAEjC,eAAO,MAAM,cAAc,GAAI,MAAM,gBAAgB,YAChB,CAAC;AAEtC,eAAO,MAAM,WAAW,GAAI,MAAM,gBAAgB,YAChB,CAAC;AAEnC,eAAO,MAAM,8BAA8B,GACzC,MAAM,gBAAgB,EACtB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,KACzB,MAKF,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,MAAM,gBAAgB,KACrB,MAAM,GAAG,IAGX,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,UAAU,QAAQ,EAClB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,KACzB,MAIF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,UAAU,QAAQ,8EAIvD,CAAC;AAIF;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,GAAI,iBAAiB,MAAM,WAG5D,CAAC"}
|
|
@@ -61,3 +61,17 @@ export const resolvePeerBadgeColor = (peerType) => {
|
|
|
61
61
|
return "bg-[var(--dc-color-bot-badge)]";
|
|
62
62
|
return "";
|
|
63
63
|
};
|
|
64
|
+
const GROUP_CONVERSATION_ID_PREFIX = "sg_";
|
|
65
|
+
/**
|
|
66
|
+
* Group conversationIds are formatted as "sg_<groupID>" — derive the groupID
|
|
67
|
+
* straight from the id when a full ConversationItem isn't available yet (e.g.
|
|
68
|
+
* a non-member previewing a public group via deeplink has no synced
|
|
69
|
+
* conversation object to read groupID off of).
|
|
70
|
+
*
|
|
71
|
+
* Returns "" for anything else, including 1:1 ids ("si_<userA>_<userB>"):
|
|
72
|
+
* those have no group, so callers must treat "" as "not a group", never as
|
|
73
|
+
* "group unknown".
|
|
74
|
+
*/
|
|
75
|
+
export const getGroupIdFromConversationId = (conversationId) => (conversationId === null || conversationId === void 0 ? void 0 : conversationId.startsWith(GROUP_CONVERSATION_ID_PREFIX))
|
|
76
|
+
? conversationId.slice(GROUP_CONVERSATION_ID_PREFIX.length)
|
|
77
|
+
: "";
|