@linktr.ee/messaging-react 3.13.4 → 3.14.0-rc-1784604876
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-C0aP28ZH.cjs → Card-5OElkb1I.cjs} +2 -2
- package/dist/{Card-C0aP28ZH.cjs.map → Card-5OElkb1I.cjs.map} +1 -1
- package/dist/{Card-fPhu90tj.js → Card-BcvG9C1y.js} +2 -2
- package/dist/{Card-fPhu90tj.js.map → Card-BcvG9C1y.js.map} +1 -1
- package/dist/{index-Cw1ij-Py.js → index-0uNeCMZl.js} +303 -300
- package/dist/{index-Cw1ij-Py.js.map → index-0uNeCMZl.js.map} +1 -1
- package/dist/{index-34_jzKSz.cjs → index-CBSdgfCs.cjs} +2 -2
- package/dist/{index-34_jzKSz.cjs.map → index-CBSdgfCs.cjs.map} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.js +1 -1
- package/dist/testing.d.ts +9 -0
- package/package.json +2 -2
- package/src/components/CustomMessageInput/CustomMessageInput.test.tsx +98 -1
- package/src/components/CustomMessageInput/index.tsx +28 -0
- package/src/stream-custom-data.ts +9 -0
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-CBSdgfCs.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.MessageBubble=e.MessageBubble;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.optimizeMessagingAttachmentUrl=e.optimizeMessagingAttachmentUrl;exports.resolveConversationParticipant=e.resolveConversationParticipant;exports.resolveLinkAttachment=e.resolveLinkAttachment;exports.resolveMediaFromMessage=e.resolveMediaFromMessage;exports.resolveParticipantDisplayName=e.resolveParticipantDisplayName;exports.useComposerLocked=e.useComposerLocked;exports.useCustomMessage=e.useCustomMessage;exports.useMessageVote=e.useMessageVote;exports.useMessaging=e.useMessaging;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1312,6 +1312,15 @@ export declare interface MessageMetadata {
|
|
|
1312
1312
|
attachment_mime_type?: string;
|
|
1313
1313
|
attachment_thumbnail?: string;
|
|
1314
1314
|
attachment_detail?: string;
|
|
1315
|
+
/**
|
|
1316
|
+
* The client surface a linker send was composed from, when it needs to be
|
|
1317
|
+
* distinguished server-side. A plain reply carries no `sent_from`; a single
|
|
1318
|
+
* reply composed inside stack-detail channel mode sets `'stack_detail'` so the
|
|
1319
|
+
* backend can leave stack removal confirm-gated (client-owned) instead of
|
|
1320
|
+
* silently removing the conversation from its stack. Composes with
|
|
1321
|
+
* `custom_type` — it identifies the send context, not the message kind. (MES-1266)
|
|
1322
|
+
*/
|
|
1323
|
+
sent_from?: 'stack_detail';
|
|
1315
1324
|
}
|
|
1316
1325
|
|
|
1317
1326
|
export declare const MessageVoteButtons: default_2.FC<MessageVoteButtonsProps>;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as e, b as t, C as i, c as o, d as n, e as m, F as r, f as g, L as l, h as M, M as c, i as u, j as L, k as h, l as p, m as d, n as v, p as C, q as A, s as k, t as F, u as b, v as P, w as f, x, o as y, y as z, z as B, B as S, D as q, E as D, G as E, H as V, I as w } from "./index-
|
|
1
|
+
import { a as e, b as t, C as i, c as o, d as n, e as m, F as r, f as g, L as l, h as M, M as c, i as u, j as L, k as h, l as p, m as d, n as v, p as C, q as A, s as k, t as F, u as b, v as P, w as f, x, o as y, y as z, z as B, B as S, D as q, E as D, G as E, H as V, I as w } from "./index-0uNeCMZl.js";
|
|
2
2
|
export {
|
|
3
3
|
e as ActionButton,
|
|
4
4
|
t as Avatar,
|
package/dist/testing.d.ts
CHANGED
|
@@ -53,6 +53,15 @@ export declare interface MessageMetadata {
|
|
|
53
53
|
attachment_mime_type?: string;
|
|
54
54
|
attachment_thumbnail?: string;
|
|
55
55
|
attachment_detail?: string;
|
|
56
|
+
/**
|
|
57
|
+
* The client surface a linker send was composed from, when it needs to be
|
|
58
|
+
* distinguished server-side. A plain reply carries no `sent_from`; a single
|
|
59
|
+
* reply composed inside stack-detail channel mode sets `'stack_detail'` so the
|
|
60
|
+
* backend can leave stack removal confirm-gated (client-owned) instead of
|
|
61
|
+
* silently removing the conversation from its stack. Composes with
|
|
62
|
+
* `custom_type` — it identifies the send context, not the message kind. (MES-1266)
|
|
63
|
+
*/
|
|
64
|
+
sent_from?: 'stack_detail';
|
|
56
65
|
}
|
|
57
66
|
|
|
58
67
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linktr.ee/messaging-react",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.14.0-rc-1784604876",
|
|
4
4
|
"description": "React messaging components built on messaging-core for web applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@linktr.ee/component-library": "11.8.6",
|
|
53
|
-
"@linktr.ee/messaging-core": "
|
|
53
|
+
"@linktr.ee/messaging-core": "2.3.0-rc-1784604876",
|
|
54
54
|
"@phosphor-icons/react": "^2.1.10"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
3
|
|
|
4
4
|
import { renderWithProviders, screen } from '../../test/utils'
|
|
5
5
|
|
|
@@ -276,4 +276,101 @@ describe('CustomMessageInput', () => {
|
|
|
276
276
|
expect(screen.queryByText(/should not show/i)).not.toBeInTheDocument()
|
|
277
277
|
expect(screen.getByTestId('stream-message-input')).toBeInTheDocument()
|
|
278
278
|
})
|
|
279
|
+
|
|
280
|
+
// MES-1303: the floating composer overlays the message list, so growing it
|
|
281
|
+
// reserves space via padding-bottom but never moves the viewport. The resize
|
|
282
|
+
// observer re-pins the thread to the latest message when the composer grows —
|
|
283
|
+
// but only if the user was already at the bottom.
|
|
284
|
+
describe('re-pins the thread when the floating composer grows (MES-1303)', () => {
|
|
285
|
+
// Render the floating composer inside the real layout structure so its
|
|
286
|
+
// `parentElement` is `.messaging-channel-layout` and it can find the Stream
|
|
287
|
+
// scroll container the way it does in ChannelView.
|
|
288
|
+
const renderInLayout = () =>
|
|
289
|
+
renderWithProviders(
|
|
290
|
+
<div className="messaging-channel-layout">
|
|
291
|
+
<div className="messaging-channel-message-list--floating">
|
|
292
|
+
<div
|
|
293
|
+
className="str-chat__message-list-scroll"
|
|
294
|
+
data-testid="scroller"
|
|
295
|
+
/>
|
|
296
|
+
</div>
|
|
297
|
+
<CustomMessageInput
|
|
298
|
+
composerInput={() => <div data-testid="custom-composer" />}
|
|
299
|
+
/>
|
|
300
|
+
</div>
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
// Force jsdom's zero-valued scroll geometry to a chosen shape. jsdom has no
|
|
304
|
+
// layout engine, so setProperty can't grow scrollHeight the way a browser
|
|
305
|
+
// reflow would; we set the post-growth geometry directly.
|
|
306
|
+
const setScrollGeometry = (
|
|
307
|
+
el: HTMLElement,
|
|
308
|
+
{ scrollHeight, clientHeight, scrollTop }: Record<string, number>
|
|
309
|
+
) => {
|
|
310
|
+
Object.defineProperty(el, 'scrollHeight', {
|
|
311
|
+
value: scrollHeight,
|
|
312
|
+
configurable: true,
|
|
313
|
+
})
|
|
314
|
+
Object.defineProperty(el, 'clientHeight', {
|
|
315
|
+
value: clientHeight,
|
|
316
|
+
configurable: true,
|
|
317
|
+
})
|
|
318
|
+
el.scrollTop = scrollTop
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
let resizeCallback: ResizeObserverCallback | undefined
|
|
322
|
+
|
|
323
|
+
beforeEach(() => {
|
|
324
|
+
mockChannelData = {}
|
|
325
|
+
resizeCallback = undefined
|
|
326
|
+
vi.stubGlobal(
|
|
327
|
+
'ResizeObserver',
|
|
328
|
+
class {
|
|
329
|
+
constructor(cb: ResizeObserverCallback) {
|
|
330
|
+
resizeCallback = cb
|
|
331
|
+
}
|
|
332
|
+
observe() {}
|
|
333
|
+
unobserve() {}
|
|
334
|
+
disconnect() {}
|
|
335
|
+
}
|
|
336
|
+
)
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
afterEach(() => {
|
|
340
|
+
vi.unstubAllGlobals()
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
it('scrolls to the bottom when the user was already at the bottom', () => {
|
|
344
|
+
renderInLayout()
|
|
345
|
+
const scroller = screen.getByTestId('scroller')
|
|
346
|
+
|
|
347
|
+
// At the bottom: scrollHeight - scrollTop - clientHeight = 0.
|
|
348
|
+
setScrollGeometry(scroller, {
|
|
349
|
+
scrollHeight: 560,
|
|
350
|
+
clientHeight: 300,
|
|
351
|
+
scrollTop: 260,
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
resizeCallback?.([], {} as ResizeObserver)
|
|
355
|
+
|
|
356
|
+
expect(scroller.scrollTop).toBe(560)
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
it('leaves the scroll position alone when the user has scrolled up', () => {
|
|
360
|
+
renderInLayout()
|
|
361
|
+
const scroller = screen.getByTestId('scroller')
|
|
362
|
+
|
|
363
|
+
// Scrolled up reading history: distance from bottom (600px) is well over
|
|
364
|
+
// the threshold.
|
|
365
|
+
setScrollGeometry(scroller, {
|
|
366
|
+
scrollHeight: 1000,
|
|
367
|
+
clientHeight: 300,
|
|
368
|
+
scrollTop: 100,
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
resizeCallback?.([], {} as ResizeObserver)
|
|
372
|
+
|
|
373
|
+
expect(scroller.scrollTop).toBe(100)
|
|
374
|
+
})
|
|
375
|
+
})
|
|
279
376
|
})
|
|
@@ -23,6 +23,14 @@ import { CustomLinkPreviewList } from '../CustomLinkPreviewList'
|
|
|
23
23
|
*/
|
|
24
24
|
const ComposerLockedContext = React.createContext(false)
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Distance from the bottom (px) still treated as "pinned to the latest
|
|
28
|
+
* message". Small enough not to fire mid-scrollback, larger than sub-pixel
|
|
29
|
+
* rounding drift. Used to decide whether a growing composer should re-pin the
|
|
30
|
+
* thread to the bottom (MES-1303).
|
|
31
|
+
*/
|
|
32
|
+
const COMPOSER_REPIN_THRESHOLD_PX = 40
|
|
33
|
+
|
|
26
34
|
/**
|
|
27
35
|
* Read the composer's locked (frozen-channel) state from inside a custom
|
|
28
36
|
* composer supplied via `composerInput`. Returns true when the channel is
|
|
@@ -170,10 +178,30 @@ export const CustomMessageInput: React.FC<CustomMessageInputProps> = ({
|
|
|
170
178
|
}
|
|
171
179
|
|
|
172
180
|
const updateComposerHeight = () => {
|
|
181
|
+
// The floating composer overlays the message list; growing it only
|
|
182
|
+
// reserves space via padding-bottom, which never moves the viewport, so
|
|
183
|
+
// a taller composer creeps up over the last message (MES-1303). Capture
|
|
184
|
+
// whether the thread is pinned to the latest message *before* the padding
|
|
185
|
+
// grows, then re-pin afterwards. Guarded on `wasAtBottom` so we never
|
|
186
|
+
// yank a user who has scrolled up to read history.
|
|
187
|
+
const scroller = layout.querySelector<HTMLElement>(
|
|
188
|
+
'.str-chat__message-list-scroll'
|
|
189
|
+
)
|
|
190
|
+
const wasAtBottom = scroller
|
|
191
|
+
? scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight <
|
|
192
|
+
COMPOSER_REPIN_THRESHOLD_PX
|
|
193
|
+
: false
|
|
194
|
+
|
|
173
195
|
layout.style.setProperty(
|
|
174
196
|
'--messaging-composer-height',
|
|
175
197
|
`${composer.offsetHeight + 16}px`
|
|
176
198
|
)
|
|
199
|
+
|
|
200
|
+
// Reading scrollHeight forces a synchronous reflow, so it reflects the
|
|
201
|
+
// padding-bottom we just set; scrolling to it pins the true bottom.
|
|
202
|
+
if (wasAtBottom && scroller) {
|
|
203
|
+
scroller.scrollTop = scroller.scrollHeight
|
|
204
|
+
}
|
|
177
205
|
}
|
|
178
206
|
|
|
179
207
|
updateComposerHeight()
|
|
@@ -48,6 +48,15 @@ export interface MessageMetadata {
|
|
|
48
48
|
attachment_mime_type?: string
|
|
49
49
|
attachment_thumbnail?: string
|
|
50
50
|
attachment_detail?: string
|
|
51
|
+
/**
|
|
52
|
+
* The client surface a linker send was composed from, when it needs to be
|
|
53
|
+
* distinguished server-side. A plain reply carries no `sent_from`; a single
|
|
54
|
+
* reply composed inside stack-detail channel mode sets `'stack_detail'` so the
|
|
55
|
+
* backend can leave stack removal confirm-gated (client-owned) instead of
|
|
56
|
+
* silently removing the conversation from its stack. Composes with
|
|
57
|
+
* `custom_type` — it identifies the send context, not the message kind. (MES-1266)
|
|
58
|
+
*/
|
|
59
|
+
sent_from?: 'stack_detail'
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
/**
|