@droppii-org/chat-sdk 0.5.1 → 0.5.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.
@@ -1 +1 @@
1
- {"version":3,"file":"useResolveConversation.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useResolveConversation.ts"],"names":[],"mappings":"AAaA,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;CAChB;AAqED,eAAO,MAAM,sBAAsB,GAAI,6CAKpC,4BAA4B;;;CA2E9B,CAAC"}
1
+ {"version":3,"file":"useResolveConversation.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useResolveConversation.ts"],"names":[],"mappings":"AAaA,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;CAChB;AA6ED,eAAO,MAAM,sBAAsB,GAAI,6CAKpC,4BAA4B;;;CA2H9B,CAAC"}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { useEffect, useState } from "react";
2
+ import { useEffect, useRef, useState } from "react";
3
3
  import { SessionType } from "@droppii-org/wasm-client-sdk";
4
4
  import { DChatSDK } from "../../constants/sdk";
5
5
  import { apiInstance } from "../../services/api";
@@ -48,18 +48,54 @@ const matchesTarget = (conversation, { conversationId, userId, groupId, botId })
48
48
  (!!groupId && conversation.groupID === groupId) ||
49
49
  (!!botId && resolveConversationBotId(conversation) === botId));
50
50
  };
51
+ const targetKey = ({ conversationId, userId, groupId, botId, }) => `${conversationId !== null && conversationId !== void 0 ? conversationId : ""}|${userId !== null && userId !== void 0 ? userId : ""}|${groupId !== null && groupId !== void 0 ? groupId : ""}|${botId !== null && botId !== void 0 ? botId : ""}`;
51
52
  export const useResolveConversation = ({ conversationId, userId, groupId, botId, }) => {
52
53
  const [isFetching, setIsFetching] = useState(false);
53
54
  const [error, setError] = useState(null);
55
+ // A target the store rejected (no access) clears conversationData back to
56
+ // null, which would otherwise re-satisfy shouldResolve and refetch forever.
57
+ const rejectedTargetRef = useRef(null);
58
+ // The conversationID + target key currently in flight through
59
+ // setConversationData — setConversationData sets conversationData to it
60
+ // optimistically before updateCurrentConversation's async group-membership
61
+ // check has a chance to reject it, so this can't be cleared just because
62
+ // conversationData looks truthy; only a rename to something else (a newer
63
+ // resolve superseding this one) or an explicit reject below clears it.
64
+ const pendingTargetRef = useRef(null);
54
65
  const { connectStatus, syncStatus } = useChatContext();
55
66
  const isSdkReady = connectStatus === ConnectStatus.Connected &&
56
67
  syncStatus !== SyncStatus.Loading;
57
68
  const conversationData = useConversationStore((state) => state.conversationData);
69
+ const removedFromGroupModalOpen = useConversationStore((state) => state.removedFromGroupModalOpen);
58
70
  const setConversationData = useConversationStore((state) => state.setConversationData);
59
71
  const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
60
72
  const target = { conversationId, userId, groupId, botId };
61
73
  const hasTarget = !!(conversationId || userId || groupId || botId);
62
- const shouldResolve = hasTarget && !matchesTarget(conversationData, target);
74
+ const currentTargetKey = targetKey(target);
75
+ const wasRejected = rejectedTargetRef.current === currentTargetKey;
76
+ const shouldResolve = hasTarget && !matchesTarget(conversationData, target) && !wasRejected;
77
+ // Catches updateCurrentConversation's async, silent rejection (blocked/no
78
+ // group access) of the target this hook just resolved and handed to
79
+ // setConversationData — that path clears conversationData back to null
80
+ // via clearSelectedConversation instead of throwing, which would otherwise
81
+ // re-satisfy shouldResolve above and refetch this target forever.
82
+ // showRemovedFromGroupModal is only ever called from that one rejection
83
+ // branch, immediately after clearSelectedConversation — react to it
84
+ // opening at all. pendingTargetRef is deliberately NOT cleared on a
85
+ // matching conversationData: setConversationData sets that optimistically,
86
+ // synchronously, before updateCurrentConversation's async membership check
87
+ // has a chance to reject it — clearing early on that transient match would
88
+ // just recreate the loop. It's overwritten by the next resolve() call
89
+ // instead, once a newer/different target supersedes this one.
90
+ useEffect(() => {
91
+ if (!removedFromGroupModalOpen)
92
+ return;
93
+ const pending = pendingTargetRef.current;
94
+ if (!pending)
95
+ return;
96
+ rejectedTargetRef.current = pending.key;
97
+ pendingTargetRef.current = null;
98
+ }, [removedFromGroupModalOpen]);
63
99
  useEffect(() => {
64
100
  if (!shouldResolve || !isSdkReady)
65
101
  return;
@@ -79,6 +115,14 @@ export const useResolveConversation = ({ conversationId, userId, groupId, botId,
79
115
  if (!resolved)
80
116
  throw new Error(CONVERSATION_NOT_FOUND_ERROR);
81
117
  // Entry point for notifications/deep links — force a fresh check.
118
+ // The store's own group-membership rejection (blocked/no access)
119
+ // lands asynchronously after this call returns, via
120
+ // clearSelectedConversation + showRemovedFromGroupModal — a separate
121
+ // effect below watches removedFromGroupModalOpen to catch it.
122
+ pendingTargetRef.current = {
123
+ key: currentTargetKey,
124
+ conversationID: resolved.conversationID,
125
+ };
82
126
  setConversationData(resolved, undefined, { forceRevalidate: true });
83
127
  setSelectedConversationId(resolved.conversationID);
84
128
  }
@@ -106,6 +150,7 @@ export const useResolveConversation = ({ conversationId, userId, groupId, botId,
106
150
  isSdkReady,
107
151
  setConversationData,
108
152
  setSelectedConversationId,
153
+ currentTargetKey,
109
154
  ]);
110
155
  return {
111
156
  isResolving: shouldResolve && (isFetching || !isSdkReady),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@droppii-org/chat-sdk",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Droppii React Chat SDK",
5
5
  "repository": {
6
6
  "type": "git",