@linktr.ee/messaging-react 3.6.2-rc-1783307602 → 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.
Files changed (28) hide show
  1. package/dist/{Card-BqePrWk0.js → Card-CJh6R76n.js} +2 -2
  2. package/dist/{Card-BqePrWk0.js.map → Card-CJh6R76n.js.map} +1 -1
  3. package/dist/{Card-DQGYnsjO.cjs → Card-CK1jw1VU.cjs} +2 -2
  4. package/dist/{Card-DQGYnsjO.cjs.map → Card-CK1jw1VU.cjs.map} +1 -1
  5. package/dist/{Card-bm6JeM03.cjs → Card-CSwQT3wz.cjs} +2 -2
  6. package/dist/{Card-bm6JeM03.cjs.map → Card-CSwQT3wz.cjs.map} +1 -1
  7. package/dist/{Card-Dlb_74PT.cjs → Card-DQtg7wWP.cjs} +2 -2
  8. package/dist/{Card-Dlb_74PT.cjs.map → Card-DQtg7wWP.cjs.map} +1 -1
  9. package/dist/{Card-CS_6M9I_.js → Card-DUoHKAuh.js} +2 -2
  10. package/dist/{Card-CS_6M9I_.js.map → Card-DUoHKAuh.js.map} +1 -1
  11. package/dist/{Card-CYD26x1-.js → Card-DnEyeR_d.js} +3 -3
  12. package/dist/{Card-CYD26x1-.js.map → Card-DnEyeR_d.js.map} +1 -1
  13. package/dist/{LockedThumbnail-C3kgiGFK.cjs → LockedThumbnail-BZLpmTpY.cjs} +2 -2
  14. package/dist/{LockedThumbnail-C3kgiGFK.cjs.map → LockedThumbnail-BZLpmTpY.cjs.map} +1 -1
  15. package/dist/{LockedThumbnail-DC782hvV.js → LockedThumbnail-C_F2Sw-9.js} +2 -2
  16. package/dist/{LockedThumbnail-DC782hvV.js.map → LockedThumbnail-C_F2Sw-9.js.map} +1 -1
  17. package/dist/{index-DaF1G1q2.js → index-DcEVHNS3.js} +490 -483
  18. package/dist/index-DcEVHNS3.js.map +1 -0
  19. package/dist/index-LP0PQJxl.cjs +2 -0
  20. package/dist/index-LP0PQJxl.cjs.map +1 -0
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.js +1 -1
  23. package/package.json +2 -2
  24. package/src/components/CustomMessage/SentMessageDeliveryStatus.test.tsx +50 -21
  25. package/src/components/CustomMessage/SentMessageDeliveryStatus.tsx +42 -24
  26. package/dist/index-DaF1G1q2.js.map +0 -1
  27. package/dist/index-eVxxkfK6.cjs +0 -2
  28. package/dist/index-eVxxkfK6.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-eVxxkfK6.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;
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-DaF1G1q2.js";
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-1783307602",
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-1783307602",
41
+ "@linktr.ee/messaging-core": "2.3.0-rc-1783311170",
42
42
  "@phosphor-icons/react": "^2.1.10"
43
43
  },
44
44
  "devDependencies": {
@@ -1,27 +1,35 @@
1
1
  import React from 'react'
2
- import { describe, expect, it, vi } from 'vitest'
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
- // stream-chat-react's `MessageStatus` owns the gating (own message, not error,
9
- // last own message, not in a thread) and the sending/sent/delivered/read state
10
- // machine that's Stream's tested code. What this component owns is the label
11
- // wired into the `sent` slot and suppressing the sending spinner, so mock
12
- // `MessageStatus` to render the slots it was handed and assert on those.
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.
13
15
  vi.mock('stream-chat-react', () => ({
16
+ useMessageContext: vi.fn(),
14
17
  MessageStatus: ({
15
18
  MessageSentStatus,
19
+ MessageDeliveredStatus,
20
+ MessageReadStatus,
16
21
  MessageSendingStatus,
17
- }: {
18
- MessageSentStatus: React.ComponentType
19
- MessageSendingStatus: React.ComponentType
20
- }) => (
22
+ }: Record<string, React.ComponentType>) => (
21
23
  <div>
22
24
  <div data-testid="sent-slot">
23
25
  <MessageSentStatus />
24
26
  </div>
27
+ <div data-testid="delivered-slot">
28
+ <MessageDeliveredStatus />
29
+ </div>
30
+ <div data-testid="read-slot">
31
+ <MessageReadStatus />
32
+ </div>
25
33
  <div data-testid="sending-slot">
26
34
  <MessageSendingStatus />
27
35
  </div>
@@ -29,24 +37,45 @@ vi.mock('stream-chat-react', () => ({
29
37
  ),
30
38
  }))
31
39
 
40
+ const mockedUseMessageContext = vi.mocked(useMessageContext)
41
+
42
+ const TERMINAL_SLOTS = ['sent-slot', 'delivered-slot', 'read-slot'] as const
43
+
32
44
  describe('SentMessageDeliveryStatus', () => {
33
- it('renders "Delivered" for the sent state', () => {
45
+ beforeEach(() => {
46
+ mockedUseMessageContext.mockReturnValue({
47
+ lastOwnMessage: { id: 'message-1' },
48
+ message: { id: 'message-1' },
49
+ } as never)
50
+ })
51
+
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.
34
56
  renderWithProviders(<SentMessageDeliveryStatus />)
35
57
 
36
- expect(
37
- screen.getByTestId('sent-message-delivery-status')
38
- ).toHaveTextContent('Delivered')
58
+ for (const slot of TERMINAL_SLOTS) {
59
+ expect(
60
+ within(screen.getByTestId(slot)).getByText('Delivered')
61
+ ).toBeInTheDocument()
62
+ }
39
63
  })
40
64
 
41
- it('renders an icon alongside the label', () => {
42
- const { container } = renderWithProviders(<SentMessageDeliveryStatus />)
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)
70
+
71
+ renderWithProviders(<SentMessageDeliveryStatus />)
43
72
 
44
- expect(
45
- container.querySelector('[data-testid="sent-slot"] svg')
46
- ).toBeInTheDocument()
73
+ for (const slot of TERMINAL_SLOTS) {
74
+ expect(screen.getByTestId(slot)).toBeEmptyDOMElement()
75
+ }
47
76
  })
48
77
 
49
- it('suppresses the sending spinner', () => {
78
+ it('always suppresses the sending spinner', () => {
50
79
  renderWithProviders(<SentMessageDeliveryStatus />)
51
80
 
52
81
  expect(screen.getByTestId('sending-slot')).toBeEmptyDOMElement()
@@ -1,20 +1,38 @@
1
1
  import { ChecksIcon } from '@phosphor-icons/react'
2
- import { MessageStatus } from 'stream-chat-react'
2
+ import { MessageStatus, useMessageContext } from 'stream-chat-react'
3
3
 
4
4
  /**
5
- * "✓✓ Delivered" label wired into `MessageStatus`'s `sent` state.
5
+ * "✓✓ Delivered" label, wired into *every* terminal `MessageStatus` state
6
+ * (sent / delivered / read).
6
7
  *
7
- * See {@link SentMessageDeliveryStatus} for why `sent` not `delivered` — is
8
- * the state that fires in our setup. Rendered inside stream's
9
- * `.str-chat__message-status` span, which supplies the flex layout; icon and
10
- * copy are styled via that span in `styles.css`.
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.
16
+ *
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`.
11
19
  */
12
- const DeliveredStatus = () => (
13
- <>
14
- <ChecksIcon weight="bold" aria-hidden="true" />
15
- <span data-testid="sent-message-delivery-status">Delivered</span>
16
- </>
17
- )
20
+ const DeliveredStatus = () => {
21
+ const { lastOwnMessage, message } = useMessageContext(
22
+ 'SentMessageDeliveryStatus'
23
+ )
24
+
25
+ if (lastOwnMessage?.id !== message.id) {
26
+ return null
27
+ }
28
+
29
+ return (
30
+ <>
31
+ <ChecksIcon weight="bold" aria-hidden="true" />
32
+ <span data-testid="sent-message-delivery-status">Delivered</span>
33
+ </>
34
+ )
35
+ }
18
36
 
19
37
  /** Suppresses a MessageStatus slot we don't want to render. */
20
38
  const NoStatus = () => null
@@ -24,26 +42,26 @@ const NoStatus = () => null
24
42
  * a right-aligned "✓✓ Delivered" that appears once Stream confirms the
25
43
  * message is stored and updates live.
26
44
  *
27
- * Built on stream-chat-react's `MessageStatus`, which already owns the exact
28
- * gating we want — own message, not an error, the *last* own message, and not
29
- * inside a thread — plus the sending/sent/delivered/read state machine.
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.
30
48
  *
31
- * The catch: Stream's `delivered` and `read` states require per-recipient
32
- * delivery/read receipts, which Linktree's Stream setup does not populate. So
33
- * those branches never fire; the branch that fires for a backend-confirmed
34
- * (`status === 'received'`) last own message is `sent`. We therefore map the
35
- * `sent` state to the ticket's "Delivered" label and suppress the default
36
- * sending spinner (scope is "Delivered" only — no Sending/Read). If receipts
37
- * are ever enabled, give `MessageDeliveredStatus` / `MessageReadStatus` their
38
- * own labels here.
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.
39
55
  *
40
- * `MessageStatus` reads `lastOwnMessage` from the message context, so
56
+ * `DeliveredStatus` reads `lastOwnMessage` from the message context, so
41
57
  * `CustomMessage`'s memo comparator must re-render when it changes (see
42
58
  * `index.tsx`) for the label to hop to a newer message.
43
59
  */
44
60
  export const SentMessageDeliveryStatus = () => (
45
61
  <MessageStatus
46
62
  MessageSentStatus={DeliveredStatus}
63
+ MessageDeliveredStatus={DeliveredStatus}
64
+ MessageReadStatus={DeliveredStatus}
47
65
  MessageSendingStatus={NoStatus}
48
66
  />
49
67
  )