@linktr.ee/messaging-react 3.6.2-rc-1783305830 → 3.6.2-rc-1783311170
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-DzbA42LS.js → Card-CJh6R76n.js} +2 -2
- package/dist/{Card-DzbA42LS.js.map → Card-CJh6R76n.js.map} +1 -1
- package/dist/{Card-ApKu84eU.cjs → Card-CK1jw1VU.cjs} +2 -2
- package/dist/{Card-ApKu84eU.cjs.map → Card-CK1jw1VU.cjs.map} +1 -1
- package/dist/{Card-BsrjOcey.cjs → Card-CSwQT3wz.cjs} +2 -2
- package/dist/{Card-BsrjOcey.cjs.map → Card-CSwQT3wz.cjs.map} +1 -1
- package/dist/{Card-DRsxhw3X.cjs → Card-DQtg7wWP.cjs} +2 -2
- package/dist/{Card-DRsxhw3X.cjs.map → Card-DQtg7wWP.cjs.map} +1 -1
- package/dist/{Card-ByFl70ay.js → Card-DUoHKAuh.js} +2 -2
- package/dist/{Card-ByFl70ay.js.map → Card-DUoHKAuh.js.map} +1 -1
- package/dist/{Card-BQaLggg8.js → Card-DnEyeR_d.js} +3 -3
- package/dist/{Card-BQaLggg8.js.map → Card-DnEyeR_d.js.map} +1 -1
- package/dist/{LockedThumbnail-C6roO49v.cjs → LockedThumbnail-BZLpmTpY.cjs} +2 -2
- package/dist/{LockedThumbnail-C6roO49v.cjs.map → LockedThumbnail-BZLpmTpY.cjs.map} +1 -1
- package/dist/{LockedThumbnail-B1MyKjeC.js → LockedThumbnail-C_F2Sw-9.js} +2 -2
- package/dist/{LockedThumbnail-B1MyKjeC.js.map → LockedThumbnail-C_F2Sw-9.js.map} +1 -1
- package/dist/assets/index.css +1 -1
- package/dist/{index-Dy-mY8YE.js → index-DcEVHNS3.js} +1062 -1068
- package/dist/index-DcEVHNS3.js.map +1 -0
- package/dist/index-LP0PQJxl.cjs +2 -0
- package/dist/index-LP0PQJxl.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/src/components/CustomMessage/SentMessageDeliveryStatus.test.tsx +64 -53
- package/src/components/CustomMessage/SentMessageDeliveryStatus.tsx +54 -36
- package/src/components/CustomMessage/index.tsx +7 -10
- package/src/styles.css +19 -4
- package/dist/index-Cx5L9rqX.cjs +0 -2
- package/dist/index-Cx5L9rqX.cjs.map +0 -1
- package/dist/index-Dy-mY8YE.js.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-LP0PQJxl.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.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.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 d, m as p, n as v, o as C, p as A, q as k, s as F, t as P, u as b, v as f, w as x, x as y, y as S, z as q, B as z, D as B, E as D, G as E } 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 d, m as p, n as v, o as C, p as A, q as k, s as F, t as P, u as b, v as f, w as x, x as y, y as S, z as q, B as z, D as B, E as D, G as E } from "./index-DcEVHNS3.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.6.2-rc-
|
|
3
|
+
"version": "3.6.2-rc-1783311170",
|
|
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.3.0-rc-
|
|
41
|
+
"@linktr.ee/messaging-core": "2.3.0-rc-1783311170",
|
|
42
42
|
"@phosphor-icons/react": "^2.1.10"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
@@ -1,72 +1,83 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { useMessageContext } from 'stream-chat-react'
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
4
|
|
|
4
|
-
import { renderWithProviders, screen } from '../../test/utils'
|
|
5
|
+
import { renderWithProviders, screen, within } from '../../test/utils'
|
|
5
6
|
|
|
6
7
|
import { SentMessageDeliveryStatus } from './SentMessageDeliveryStatus'
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
// stream-chat-react's `MessageStatus` owns the outer gating (own message, not
|
|
10
|
+
// error, not in a thread) and the state machine — Stream's tested code. What
|
|
11
|
+
// this component owns is the "Delivered" label wired into the terminal slots
|
|
12
|
+
// (sent/delivered/read), its "last own message" self-gate, and suppressing
|
|
13
|
+
// the sending spinner. Mock `MessageStatus` to render the slots it was handed,
|
|
14
|
+
// and `useMessageContext` to feed the label's self-gate.
|
|
15
|
+
vi.mock('stream-chat-react', () => ({
|
|
16
|
+
useMessageContext: vi.fn(),
|
|
17
|
+
MessageStatus: ({
|
|
18
|
+
MessageSentStatus,
|
|
19
|
+
MessageDeliveredStatus,
|
|
20
|
+
MessageReadStatus,
|
|
21
|
+
MessageSendingStatus,
|
|
22
|
+
}: Record<string, React.ComponentType>) => (
|
|
23
|
+
<div>
|
|
24
|
+
<div data-testid="sent-slot">
|
|
25
|
+
<MessageSentStatus />
|
|
26
|
+
</div>
|
|
27
|
+
<div data-testid="delivered-slot">
|
|
28
|
+
<MessageDeliveredStatus />
|
|
29
|
+
</div>
|
|
30
|
+
<div data-testid="read-slot">
|
|
31
|
+
<MessageReadStatus />
|
|
32
|
+
</div>
|
|
33
|
+
<div data-testid="sending-slot">
|
|
34
|
+
<MessageSendingStatus />
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
),
|
|
38
|
+
}))
|
|
13
39
|
|
|
14
|
-
const
|
|
15
|
-
props: Partial<{
|
|
16
|
-
message: LocalMessage
|
|
17
|
-
isMyMessage: boolean
|
|
18
|
-
lastOwnMessageId?: string
|
|
19
|
-
}> = {}
|
|
20
|
-
) =>
|
|
21
|
-
renderWithProviders(
|
|
22
|
-
<SentMessageDeliveryStatus
|
|
23
|
-
message={props.message ?? baseMessage}
|
|
24
|
-
isMyMessage={props.isMyMessage ?? true}
|
|
25
|
-
lastOwnMessageId={props.lastOwnMessageId ?? 'message-1'}
|
|
26
|
-
/>
|
|
27
|
-
)
|
|
40
|
+
const mockedUseMessageContext = vi.mocked(useMessageContext)
|
|
28
41
|
|
|
29
|
-
|
|
30
|
-
it('renders "Delivered" for the last own message once Stream has received it', () => {
|
|
31
|
-
renderStatus()
|
|
42
|
+
const TERMINAL_SLOTS = ['sent-slot', 'delivered-slot', 'read-slot'] as const
|
|
32
43
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
44
|
+
describe('SentMessageDeliveryStatus', () => {
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
mockedUseMessageContext.mockReturnValue({
|
|
47
|
+
lastOwnMessage: { id: 'message-1' },
|
|
48
|
+
message: { id: 'message-1' },
|
|
49
|
+
} as never)
|
|
37
50
|
})
|
|
38
51
|
|
|
39
|
-
it('renders
|
|
40
|
-
|
|
52
|
+
it('renders "Delivered" in every terminal slot for the last own message', () => {
|
|
53
|
+
// delivered/read fire ahead of sent in receipt channels, so all three
|
|
54
|
+
// must show the label — never Stream's default delivered icon / read
|
|
55
|
+
// avatars.
|
|
56
|
+
renderWithProviders(<SentMessageDeliveryStatus />)
|
|
41
57
|
|
|
42
|
-
|
|
58
|
+
for (const slot of TERMINAL_SLOTS) {
|
|
59
|
+
expect(
|
|
60
|
+
within(screen.getByTestId(slot)).getByText('Delivered')
|
|
61
|
+
).toBeInTheDocument()
|
|
62
|
+
}
|
|
43
63
|
})
|
|
44
64
|
|
|
45
|
-
it('renders nothing when
|
|
46
|
-
|
|
65
|
+
it('renders nothing in the terminal slots when it is not the last own message', () => {
|
|
66
|
+
mockedUseMessageContext.mockReturnValue({
|
|
67
|
+
lastOwnMessage: { id: 'message-2' },
|
|
68
|
+
message: { id: 'message-1' },
|
|
69
|
+
} as never)
|
|
47
70
|
|
|
48
|
-
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it.each(['sending', 'failed'] as const)(
|
|
52
|
-
'renders nothing while the message status is %s',
|
|
53
|
-
(status) => {
|
|
54
|
-
const { container } = renderStatus({
|
|
55
|
-
message: { ...baseMessage, status } as unknown as LocalMessage,
|
|
56
|
-
})
|
|
71
|
+
renderWithProviders(<SentMessageDeliveryStatus />)
|
|
57
72
|
|
|
58
|
-
|
|
73
|
+
for (const slot of TERMINAL_SLOTS) {
|
|
74
|
+
expect(screen.getByTestId(slot)).toBeEmptyDOMElement()
|
|
59
75
|
}
|
|
60
|
-
)
|
|
76
|
+
})
|
|
61
77
|
|
|
62
|
-
it('
|
|
63
|
-
|
|
64
|
-
message: {
|
|
65
|
-
...baseMessage,
|
|
66
|
-
type: 'error',
|
|
67
|
-
} as unknown as LocalMessage,
|
|
68
|
-
})
|
|
78
|
+
it('always suppresses the sending spinner', () => {
|
|
79
|
+
renderWithProviders(<SentMessageDeliveryStatus />)
|
|
69
80
|
|
|
70
|
-
expect(
|
|
81
|
+
expect(screen.getByTestId('sending-slot')).toBeEmptyDOMElement()
|
|
71
82
|
})
|
|
72
83
|
})
|
|
@@ -1,49 +1,67 @@
|
|
|
1
1
|
import { ChecksIcon } from '@phosphor-icons/react'
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
interface SentMessageDeliveryStatusProps {
|
|
5
|
-
message: LocalMessage
|
|
6
|
-
/** Result of the message context's `isMyMessage()` for this message. */
|
|
7
|
-
isMyMessage: boolean
|
|
8
|
-
/** `id` of the viewer's most recent own message, from `lastOwnMessage`. */
|
|
9
|
-
lastOwnMessageId?: string
|
|
10
|
-
}
|
|
2
|
+
import { MessageStatus, useMessageContext } from 'stream-chat-react'
|
|
11
3
|
|
|
12
4
|
/**
|
|
13
|
-
* "✓✓ Delivered"
|
|
14
|
-
*
|
|
5
|
+
* "✓✓ Delivered" label, wired into *every* terminal `MessageStatus` state
|
|
6
|
+
* (sent / delivered / read).
|
|
15
7
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* the
|
|
8
|
+
* `MessageStatus` picks its `delivered` and `read` branches ahead of `sent`
|
|
9
|
+
* whenever Stream has `deliveredTo` / `readBy` for the message — and, unlike
|
|
10
|
+
* `sent`, those branches are *not* gated to the last own message. So in a
|
|
11
|
+
* channel that has receipts we must (a) replace Stream's default delivered
|
|
12
|
+
* icon / read avatars with our label — otherwise the required "Delivered"
|
|
13
|
+
* label disappears and an out-of-scope read receipt would show — and (b)
|
|
14
|
+
* re-apply the "last own message only" gate ourselves, since the
|
|
15
|
+
* delivered/read branches would otherwise render on every read own message.
|
|
21
16
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* subtree when `lastOwnMessage` changes so the label hops to a newer message.
|
|
17
|
+
* Rendered inside stream's `.str-chat__message-status` span, which supplies
|
|
18
|
+
* the flex layout; icon and copy are styled via that span in `styles.css`.
|
|
25
19
|
*/
|
|
26
|
-
|
|
27
|
-
message
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}: SentMessageDeliveryStatusProps) => {
|
|
31
|
-
const isLastOwnMessage =
|
|
32
|
-
isMyMessage &&
|
|
33
|
-
message.type !== 'error' &&
|
|
34
|
-
lastOwnMessageId === message.id
|
|
20
|
+
const DeliveredStatus = () => {
|
|
21
|
+
const { lastOwnMessage, message } = useMessageContext(
|
|
22
|
+
'SentMessageDeliveryStatus'
|
|
23
|
+
)
|
|
35
24
|
|
|
36
|
-
if (
|
|
25
|
+
if (lastOwnMessage?.id !== message.id) {
|
|
37
26
|
return null
|
|
38
27
|
}
|
|
39
28
|
|
|
40
29
|
return (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
data-testid="sent-message-delivery-status"
|
|
44
|
-
|
|
45
|
-
<ChecksIcon size={14} weight="bold" aria-hidden="true" />
|
|
46
|
-
<span>Delivered</span>
|
|
47
|
-
</div>
|
|
30
|
+
<>
|
|
31
|
+
<ChecksIcon weight="bold" aria-hidden="true" />
|
|
32
|
+
<span data-testid="sent-message-delivery-status">Delivered</span>
|
|
33
|
+
</>
|
|
48
34
|
)
|
|
49
35
|
}
|
|
36
|
+
|
|
37
|
+
/** Suppresses a MessageStatus slot we don't want to render. */
|
|
38
|
+
const NoStatus = () => null
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Delivery status beneath the viewer's most recent sent message (MES-1036):
|
|
42
|
+
* a right-aligned "✓✓ Delivered" that appears once Stream confirms the
|
|
43
|
+
* message is stored and updates live.
|
|
44
|
+
*
|
|
45
|
+
* Built on stream-chat-react's `MessageStatus`, which owns the gating we want
|
|
46
|
+
* — own message, not an error, and not inside a thread — plus the
|
|
47
|
+
* sending/sent/delivered/read state machine.
|
|
48
|
+
*
|
|
49
|
+
* Scope is "Delivered" only (no Sending / Read). Stream's `sent` state maps
|
|
50
|
+
* to "Delivered" for a backend-confirmed (`status === 'received'`) last own
|
|
51
|
+
* message; its `delivered`/`read` states (only populated in channels with
|
|
52
|
+
* per-recipient receipts) also render the same "Delivered" label via
|
|
53
|
+
* {@link DeliveredStatus} so we never surface a read receipt. The sending
|
|
54
|
+
* spinner is suppressed.
|
|
55
|
+
*
|
|
56
|
+
* `DeliveredStatus` reads `lastOwnMessage` from the message context, so
|
|
57
|
+
* `CustomMessage`'s memo comparator must re-render when it changes (see
|
|
58
|
+
* `index.tsx`) for the label to hop to a newer message.
|
|
59
|
+
*/
|
|
60
|
+
export const SentMessageDeliveryStatus = () => (
|
|
61
|
+
<MessageStatus
|
|
62
|
+
MessageSentStatus={DeliveredStatus}
|
|
63
|
+
MessageDeliveredStatus={DeliveredStatus}
|
|
64
|
+
MessageReadStatus={DeliveredStatus}
|
|
65
|
+
MessageSendingStatus={NoStatus}
|
|
66
|
+
/>
|
|
67
|
+
)
|
|
@@ -75,7 +75,6 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
75
75
|
highlighted,
|
|
76
76
|
isMessageAIGenerated,
|
|
77
77
|
isMyMessage,
|
|
78
|
-
lastOwnMessage,
|
|
79
78
|
message,
|
|
80
79
|
renderText,
|
|
81
80
|
threadList,
|
|
@@ -399,11 +398,7 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
399
398
|
)}
|
|
400
399
|
</div>
|
|
401
400
|
)}
|
|
402
|
-
<SentMessageDeliveryStatus
|
|
403
|
-
message={message}
|
|
404
|
-
isMyMessage={isMine}
|
|
405
|
-
lastOwnMessageId={lastOwnMessage?.id}
|
|
406
|
-
/>
|
|
401
|
+
<SentMessageDeliveryStatus />
|
|
407
402
|
{showReplyCountButton && (
|
|
408
403
|
<MessageRepliesCountButton
|
|
409
404
|
onClick={handleOpenThread}
|
|
@@ -423,10 +418,12 @@ const MemoizedCustomMessage = React.memo(
|
|
|
423
418
|
(prev, next) => {
|
|
424
419
|
if (prev.chatbotVotingEnabled !== next.chatbotVotingEnabled) return false
|
|
425
420
|
if (prev.viewerLanguage !== next.viewerLanguage) return false
|
|
426
|
-
// `lastOwnMessage` drives the "Delivered" indicator
|
|
427
|
-
//
|
|
428
|
-
// it
|
|
429
|
-
//
|
|
421
|
+
// `lastOwnMessage` drives the "Delivered" indicator, which
|
|
422
|
+
// `SentMessageDeliveryStatus` derives from stream's `MessageStatus`
|
|
423
|
+
// (reading it off the message context inside this memoised subtree).
|
|
424
|
+
// `areMessageUIPropsEqual` doesn't compare it, so without this a stale
|
|
425
|
+
// "Delivered" would linger on the previous bubble after a newer message
|
|
426
|
+
// is sent — re-render when it changes.
|
|
430
427
|
if (prev.lastOwnMessage?.id !== next.lastOwnMessage?.id) return false
|
|
431
428
|
return areMessageUIPropsEqual(prev, next)
|
|
432
429
|
}
|
package/src/styles.css
CHANGED
|
@@ -417,11 +417,14 @@
|
|
|
417
417
|
}
|
|
418
418
|
|
|
419
419
|
/* "✓✓ Delivered" delivery status under the last sent message (MES-1036).
|
|
420
|
-
|
|
421
|
-
|
|
420
|
+
`SentMessageDeliveryStatus` renders it through stream's `MessageStatus`,
|
|
421
|
+
whose `.str-chat__message-status` span is the grid item here (right-aligned
|
|
422
|
+
via the outer grid's `justify-items: end`). Stream emits that span for every
|
|
423
|
+
own message and leaves it empty when no state is active (i.e. not the last
|
|
424
|
+
sent message), so `:empty` collapses those away. */
|
|
425
|
+
.str-chat__li .str-chat__message--me .str-chat__message-status {
|
|
422
426
|
grid-area: delivery-status;
|
|
423
|
-
|
|
424
|
-
align-items: center;
|
|
427
|
+
justify-content: flex-end;
|
|
425
428
|
gap: 2px;
|
|
426
429
|
padding-block-start: 2px;
|
|
427
430
|
padding-inline-end: 4px;
|
|
@@ -429,6 +432,18 @@
|
|
|
429
432
|
line-height: 1.4;
|
|
430
433
|
color: var(--str-chat__text-low-emphasis-color, rgba(0, 0, 0, 0.5));
|
|
431
434
|
}
|
|
435
|
+
.str-chat__li .str-chat__message-status:empty {
|
|
436
|
+
display: none;
|
|
437
|
+
}
|
|
438
|
+
/* Match the check icon to the label size/colour (overrides stream's default
|
|
439
|
+
per-state icon sizing and status-colour fill). */
|
|
440
|
+
.str-chat__li .str-chat__message--me .str-chat__message-status svg {
|
|
441
|
+
width: 14px;
|
|
442
|
+
height: 14px;
|
|
443
|
+
}
|
|
444
|
+
.str-chat__li .str-chat__message--me .str-chat__message-status svg path {
|
|
445
|
+
fill: currentColor;
|
|
446
|
+
}
|
|
432
447
|
|
|
433
448
|
/* Channel list load-more button overrides */
|
|
434
449
|
.str-chat__channel-list .str-chat__load-more-button__button {
|