@linktr.ee/messaging-react 3.3.11-rc-1781859612 → 3.3.11
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/{Card-CizcvpTa.cjs → Card-BxHcN_uK.cjs} +2 -2
- package/dist/{Card-CizcvpTa.cjs.map → Card-BxHcN_uK.cjs.map} +1 -1
- package/dist/{Card-PGPY8ZRL.cjs → Card-Dx9vU_aU.cjs} +2 -2
- package/dist/{Card-PGPY8ZRL.cjs.map → Card-Dx9vU_aU.cjs.map} +1 -1
- package/dist/{Card-D-sSG334.js → Card-Likc_mEq.js} +3 -3
- package/dist/{Card-D-sSG334.js.map → Card-Likc_mEq.js.map} +1 -1
- package/dist/{Card-omzCiCc9.cjs → Card-Som2Laov.cjs} +2 -2
- package/dist/{Card-omzCiCc9.cjs.map → Card-Som2Laov.cjs.map} +1 -1
- package/dist/{Card-BmU_ztmU.js → Card-VgnjOBSF.js} +2 -2
- package/dist/{Card-BmU_ztmU.js.map → Card-VgnjOBSF.js.map} +1 -1
- package/dist/{Card-CHDQEG9i.js → Card-datOr9-K.js} +2 -2
- package/dist/{Card-CHDQEG9i.js.map → Card-datOr9-K.js.map} +1 -1
- package/dist/{LockedThumbnail-BynAff_z.js → LockedThumbnail-MW14CvSv.js} +2 -2
- package/dist/{LockedThumbnail-BynAff_z.js.map → LockedThumbnail-MW14CvSv.js.map} +1 -1
- package/dist/{LockedThumbnail-UEkCam1N.cjs → LockedThumbnail-QWGt7mcF.cjs} +2 -2
- package/dist/{LockedThumbnail-UEkCam1N.cjs.map → LockedThumbnail-QWGt7mcF.cjs.map} +1 -1
- package/dist/index-CRVJo6HP.cjs +2 -0
- package/dist/index-CRVJo6HP.cjs.map +1 -0
- package/dist/{index-CAAtAfn1.js → index-cIErdakH.js} +531 -537
- package/dist/index-cIErdakH.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/src/providers/MessagingProvider.test.tsx +46 -2
- package/src/providers/MessagingProvider.tsx +51 -44
- package/dist/index-CAAtAfn1.js.map +0 -1
- package/dist/index-SMjrfgpc.cjs +0 -2
- package/dist/index-SMjrfgpc.cjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-CRVJo6HP.cjs");exports.ActionButton=e.ActionButton;exports.Avatar=e.Avatar;exports.ChannelEmptyState=e.ChannelEmptyState;exports.ChannelList=e.ChannelList;exports.ChannelView=e.ChannelView;exports.CustomMessageProvider=e.CustomMessageProvider;exports.FaqList=e.FaqList;exports.FaqListItem=e.FaqListItem;exports.LinkAttachment=e.LinkAttachment;exports.LockedAttachment=e.LockedAttachment;exports.MediaMessage=e.MediaMessage;exports.MessageAttachment=e.MessageAttachment;exports.MessageVoteButtons=e.MessageVoteButtons;exports.MessagingProvider=e.MessagingProvider;exports.MessagingShell=e.MessagingShell;exports.buildCompactMetaLabel=e.buildCompactMetaLabel;exports.formatFileSize=e.formatFileSize;exports.formatRelativeTime=e.formatRelativeTime;exports.getFileExtensionLabel=e.getFileExtensionLabel;exports.getMessageDisplayText=e.getMessageDisplayText;exports.isLinkAttachment=e.isLinkAttachment;exports.isUuidLike=e.isUuidLike;exports.messageAttachmentGroupPositionFromStream=e.bubbleGroupPositionFromStream;exports.normalizeLanguageCode=e.normalizeLanguageCode;exports.resolveLinkAttachment=e.resolveLinkAttachment;exports.resolveMediaFromMessage=e.resolveMediaFromMessage;exports.resolveParticipantDisplayName=e.resolveParticipantDisplayName;exports.useCustomMessage=e.useCustomMessage;exports.useMessageVote=e.useMessageVote;exports.useMessaging=e.useMessaging;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as e, b as t, C as i, c as n, d as o, e as m, F as g, f as l, L as r, h as M, M as u, i as L, j as c, k as h, l as d, m as p, n as v, o as A, p as C, q as F, s as k, t as b, u as f, v as x, w as y, x as P, y as S, z as q, B as z, D as B } from "./index-
|
|
1
|
+
import { a as e, b as t, C as i, c as n, d as o, e as m, F as g, f as l, L as r, h as M, M as u, i as L, j as c, k as h, l as d, m as p, n as v, o as A, p as C, q as F, s as k, t as b, u as f, v as x, w as y, x as P, y as S, z as q, B as z, D as B } from "./index-cIErdakH.js";
|
|
2
2
|
export {
|
|
3
3
|
e as ActionButton,
|
|
4
4
|
t as Avatar,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linktr.ee/messaging-react",
|
|
3
|
-
"version": "3.3.11
|
|
3
|
+
"version": "3.3.11",
|
|
4
4
|
"description": "React messaging components built on messaging-core for web applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@linktr.ee/component-library": "11.8.6",
|
|
41
|
-
"@linktr.ee/messaging-core": "2.2.0
|
|
41
|
+
"@linktr.ee/messaging-core": "^2.2.0",
|
|
42
42
|
"@phosphor-icons/react": "^2.1.10"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
@@ -147,8 +147,8 @@ describe('MessagingProvider', () => {
|
|
|
147
147
|
await waitFor(() => expect(connectUser).toHaveBeenCalledTimes(1))
|
|
148
148
|
|
|
149
149
|
// Re-render with a NEW inline serviceConfig object (same apiKey). The
|
|
150
|
-
// service is
|
|
151
|
-
// service
|
|
150
|
+
// service is created once per client lifetime, so a serviceConfig change
|
|
151
|
+
// neither recreates the service nor tears down the live connection.
|
|
152
152
|
await act(async () => {
|
|
153
153
|
rerender(
|
|
154
154
|
<MessagingProvider
|
|
@@ -167,6 +167,50 @@ describe('MessagingProvider', () => {
|
|
|
167
167
|
expect(disconnectUser).not.toHaveBeenCalled()
|
|
168
168
|
})
|
|
169
169
|
|
|
170
|
+
it('does not disconnect the reused client when only the debug prop toggles', async () => {
|
|
171
|
+
const { connectUser, disconnectUser } = setupServiceMock()
|
|
172
|
+
const user = {
|
|
173
|
+
type: 'guest' as const,
|
|
174
|
+
id: 'guest-1',
|
|
175
|
+
name: 'Guest',
|
|
176
|
+
} as MessagingUser
|
|
177
|
+
const serviceConfig = {
|
|
178
|
+
fetchToken: async () => 'token',
|
|
179
|
+
createChannel: async () => ({ channelId: 'ch-1' }),
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const { rerender } = render(
|
|
183
|
+
<MessagingProvider
|
|
184
|
+
apiKey="mock-api-key"
|
|
185
|
+
user={user}
|
|
186
|
+
serviceConfig={serviceConfig}
|
|
187
|
+
debug={false}
|
|
188
|
+
>
|
|
189
|
+
<div />
|
|
190
|
+
</MessagingProvider>
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
await waitFor(() => expect(connectUser).toHaveBeenCalledTimes(1))
|
|
194
|
+
|
|
195
|
+
// Toggling `debug` changes the internal debugLog identity. The client
|
|
196
|
+
// cleanup must stay keyed on the client's lifetime, not on debugLog, so the
|
|
197
|
+
// live connection is not torn down for a logging change.
|
|
198
|
+
await act(async () => {
|
|
199
|
+
rerender(
|
|
200
|
+
<MessagingProvider
|
|
201
|
+
apiKey="mock-api-key"
|
|
202
|
+
user={user}
|
|
203
|
+
serviceConfig={serviceConfig}
|
|
204
|
+
debug={true}
|
|
205
|
+
>
|
|
206
|
+
<div />
|
|
207
|
+
</MessagingProvider>
|
|
208
|
+
)
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
expect(disconnectUser).not.toHaveBeenCalled()
|
|
212
|
+
})
|
|
213
|
+
|
|
170
214
|
it('disconnects and reconnects when remounted with a new key', async () => {
|
|
171
215
|
const { connectUser, disconnectUser } = setupServiceMock()
|
|
172
216
|
|
|
@@ -79,10 +79,18 @@ export const MessagingProvider: React.FC<MessagingProviderProps> = ({
|
|
|
79
79
|
// Prevent multiple concurrent connection attempts
|
|
80
80
|
const connectingRef = useRef(false)
|
|
81
81
|
|
|
82
|
-
//
|
|
83
|
-
// lifetime rather than the service's (see the client-cleanup effect below).
|
|
82
|
+
// The single service instance, for teardown in the client-cleanup effect.
|
|
84
83
|
const serviceRef = useRef<StreamChatService | null>(null)
|
|
85
84
|
|
|
85
|
+
// serviceConfig / debug are read at service-creation time via refs so that
|
|
86
|
+
// changing them does not recreate the service (and re-adopt the live client).
|
|
87
|
+
// The service is created once per client lifetime (apiKey); the connection is
|
|
88
|
+
// driven by `user`. Real consumers pass a stable, memoized serviceConfig.
|
|
89
|
+
const serviceConfigRef = useRef(serviceConfig)
|
|
90
|
+
serviceConfigRef.current = serviceConfig
|
|
91
|
+
const debugRef = useRef(debug)
|
|
92
|
+
debugRef.current = debug
|
|
93
|
+
|
|
86
94
|
// Track renders and prop changes
|
|
87
95
|
const prevPropsRef = useRef({
|
|
88
96
|
userId: user?.id,
|
|
@@ -118,50 +126,50 @@ export const MessagingProvider: React.FC<MessagingProviderProps> = ({
|
|
|
118
126
|
// connectUser resolved (~0.8s later) because the children's parent element
|
|
119
127
|
// changed — re-running every mount effect (refetches, Stream listeners,
|
|
120
128
|
// page-view events) and resetting transient state. This client is injected
|
|
121
|
-
// into the service below, so connectUser connects this exact instance.
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
//
|
|
129
|
+
// into the service below, so connectUser connects this exact instance.
|
|
130
|
+
//
|
|
131
|
+
// Stored in a ref (not useMemo) so its identity is *guaranteed* stable for the
|
|
132
|
+
// apiKey's lifetime. useMemo is unsafe here: React may discard a memo and
|
|
133
|
+
// recompute it, which would mint a new client, re-run the service-init effect,
|
|
134
|
+
// and tear down + rebuild the live connection — the very remount this fix
|
|
135
|
+
// exists to prevent. Both effects below key on `chatClient`, so it must not
|
|
136
|
+
// depend on memo retention. One client per provider mount (keyed on apiKey) —
|
|
137
|
+
// not the process-wide StreamChat.getInstance, which is shared across keys and
|
|
138
|
+
// provider instances.
|
|
139
|
+
const clientRef = useRef<{ apiKey: string; client: StreamChat } | null>(null)
|
|
140
|
+
if (apiKey && clientRef.current?.apiKey !== apiKey) {
|
|
141
|
+
clientRef.current = { apiKey, client: new StreamChat(apiKey) }
|
|
142
|
+
}
|
|
143
|
+
const chatClient = apiKey ? (clientRef.current?.client ?? null) : null
|
|
144
|
+
|
|
145
|
+
// Create the service once per client lifetime (apiKey). Keyed on `chatClient`
|
|
146
|
+
// — NOT on serviceConfig/debug — so those changing does not spin up a second
|
|
147
|
+
// service that would re-adopt the live client and race the in-flight connect.
|
|
148
|
+
// serviceConfig/debug are read from refs at creation. There is therefore a
|
|
149
|
+
// single service per client, which owns the connection lifecycle and tracks
|
|
150
|
+
// the active user; `user` changes reconnect through it.
|
|
130
151
|
useEffect(() => {
|
|
131
152
|
const currentRender = renderCountRef.current
|
|
132
|
-
|
|
133
|
-
renderCount: currentRender,
|
|
134
|
-
apiKey: !!apiKey,
|
|
135
|
-
serviceConfig: !!serviceConfig,
|
|
136
|
-
dependencies: {
|
|
137
|
-
apiKey: apiKey?.substring(0, 8) + '...',
|
|
138
|
-
serviceConfigRef: serviceConfig,
|
|
139
|
-
serviceConfigStable:
|
|
140
|
-
prevPropsRef.current.serviceConfig === serviceConfig,
|
|
141
|
-
apiKeyStable: prevPropsRef.current.apiKey === apiKey,
|
|
142
|
-
},
|
|
143
|
-
})
|
|
153
|
+
const currentConfig = serviceConfigRef.current
|
|
144
154
|
|
|
145
|
-
if (!apiKey || !
|
|
155
|
+
if (!apiKey || !chatClient || !currentConfig) {
|
|
146
156
|
debugLog('⚠️ SERVICE INIT SKIPPED', {
|
|
147
157
|
renderCount: currentRender,
|
|
148
|
-
reason: 'Missing apiKey or serviceConfig',
|
|
158
|
+
reason: 'Missing apiKey, client, or serviceConfig',
|
|
149
159
|
})
|
|
150
160
|
return
|
|
151
161
|
}
|
|
152
162
|
|
|
153
|
-
debugLog('🚀 CREATING
|
|
163
|
+
debugLog('🚀 CREATING SERVICE', {
|
|
154
164
|
renderCount: currentRender,
|
|
155
165
|
apiKey: apiKey?.substring(0, 8) + '...',
|
|
156
|
-
serviceConfigChanged:
|
|
157
|
-
prevPropsRef.current.serviceConfig !== serviceConfig,
|
|
158
166
|
})
|
|
159
167
|
|
|
160
168
|
const newService = new StreamChatService({
|
|
161
|
-
...
|
|
169
|
+
...currentConfig,
|
|
162
170
|
apiKey,
|
|
163
|
-
debug,
|
|
164
|
-
|
|
171
|
+
debug: debugRef.current,
|
|
172
|
+
client: chatClient,
|
|
165
173
|
})
|
|
166
174
|
|
|
167
175
|
serviceRef.current = newService
|
|
@@ -171,12 +179,10 @@ export const MessagingProvider: React.FC<MessagingProviderProps> = ({
|
|
|
171
179
|
serviceInstance: !!newService,
|
|
172
180
|
})
|
|
173
181
|
|
|
174
|
-
// No disconnect on cleanup here
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
|
|
178
|
-
// effect below, keyed on `chatClient` (apiKey) and unmount.
|
|
179
|
-
}, [apiKey, serviceConfig, debug, debugLog, chatClient]) // Use serviceConfig object directly, not individual properties
|
|
182
|
+
// No disconnect on cleanup here — disconnection is owned by the
|
|
183
|
+
// client-cleanup effect below, keyed on `chatClient` (apiKey) and unmount.
|
|
184
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- serviceConfig/debug/debugLog are read via refs by design (see above); recreating on them would re-adopt the live client
|
|
185
|
+
}, [apiKey, chatClient])
|
|
180
186
|
|
|
181
187
|
// Track if we've already connected this user with this service to prevent duplicate connections
|
|
182
188
|
const connectedUserRef = useRef<{
|
|
@@ -252,22 +258,23 @@ export const MessagingProvider: React.FC<MessagingProviderProps> = ({
|
|
|
252
258
|
}
|
|
253
259
|
|
|
254
260
|
connectUser()
|
|
255
|
-
}, [service, user, debugLog, isConnected])
|
|
261
|
+
}, [service, user, debugLog, isConnected])
|
|
256
262
|
|
|
257
263
|
// Disconnect tied to the client's lifetime: only when `chatClient` changes
|
|
258
264
|
// (i.e. `apiKey` changes) or the provider unmounts — never on a mere
|
|
259
265
|
// serviceConfig/debug change, which reuses the same client. Uses `serviceRef`
|
|
260
|
-
// so the disconnect goes through the latest service instance.
|
|
266
|
+
// so the disconnect goes through the latest service instance. Deliberately
|
|
267
|
+
// keyed on `chatClient` only: depending on `debugLog` (which changes identity
|
|
268
|
+
// when the `debug` prop toggles) would tear down the live client just for a
|
|
269
|
+
// logging change.
|
|
261
270
|
useEffect(() => {
|
|
262
|
-
|
|
263
|
-
hasClient: !!chatClient,
|
|
264
|
-
})
|
|
271
|
+
const client = chatClient
|
|
265
272
|
return () => {
|
|
266
|
-
|
|
273
|
+
if (!client) return
|
|
267
274
|
connectedUserRef.current = null // Reset connection tracking
|
|
268
275
|
serviceRef.current?.disconnectUser().catch(console.error)
|
|
269
276
|
}
|
|
270
|
-
}, [chatClient
|
|
277
|
+
}, [chatClient])
|
|
271
278
|
|
|
272
279
|
const refreshConnection = useCallback(async () => {
|
|
273
280
|
debugLog('🔄 REFRESH CONNECTION CALLED', {
|