@linktr.ee/messaging-react 2.6.0 → 2.6.2-rc-1780478292

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 (30) hide show
  1. package/dist/{Card-DvbDgSLn.js → Card-BIb2ouTi.js} +2 -2
  2. package/dist/{Card-DvbDgSLn.js.map → Card-BIb2ouTi.js.map} +1 -1
  3. package/dist/{Card-oSacqdqx.js → Card-CafojjYc.js} +3 -3
  4. package/dist/{Card-oSacqdqx.js.map → Card-CafojjYc.js.map} +1 -1
  5. package/dist/{Card-CI5xiM4T.js → Card-DmgAq0-y.js} +2 -2
  6. package/dist/{Card-CI5xiM4T.js.map → Card-DmgAq0-y.js.map} +1 -1
  7. package/dist/{Card-C6nLpYRM.cjs → Card-Dr3LuTAS.cjs} +2 -2
  8. package/dist/{Card-C6nLpYRM.cjs.map → Card-Dr3LuTAS.cjs.map} +1 -1
  9. package/dist/{Card-tWe0vSN0.cjs → Card-DrIyNSwR.cjs} +2 -2
  10. package/dist/{Card-tWe0vSN0.cjs.map → Card-DrIyNSwR.cjs.map} +1 -1
  11. package/dist/{Card-BrOaVzOB.cjs → Card-DtoXBrhV.cjs} +2 -2
  12. package/dist/{Card-BrOaVzOB.cjs.map → Card-DtoXBrhV.cjs.map} +1 -1
  13. package/dist/{LockedThumbnail-D_HL8AlU.js → LockedThumbnail-B4LMWHDV.js} +2 -2
  14. package/dist/{LockedThumbnail-D_HL8AlU.js.map → LockedThumbnail-B4LMWHDV.js.map} +1 -1
  15. package/dist/{LockedThumbnail-P0HCeYW6.cjs → LockedThumbnail-CPAHQ9jA.cjs} +2 -2
  16. package/dist/{LockedThumbnail-P0HCeYW6.cjs.map → LockedThumbnail-CPAHQ9jA.cjs.map} +1 -1
  17. package/dist/index-Degc6G3J.cjs +2 -0
  18. package/dist/index-Degc6G3J.cjs.map +1 -0
  19. package/dist/{index-DL0Ubrtn.js → index-LiNmL1ax.js} +8 -5
  20. package/dist/index-LiNmL1ax.js.map +1 -0
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.js +1 -1
  23. package/package.json +1 -1
  24. package/src/components/CustomMessage/CustomMessage.translation.test.tsx +191 -0
  25. package/src/components/CustomMessage/CustomMessageActions.tsx +1 -1
  26. package/src/utils/getMessageDisplayText.test.ts +32 -0
  27. package/src/utils/getMessageDisplayText.ts +6 -1
  28. package/dist/index-DL0Ubrtn.js.map +0 -1
  29. package/dist/index-DbfBooBe.cjs +0 -2
  30. package/dist/index-DbfBooBe.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-DbfBooBe.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;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-Degc6G3J.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-DL0Ubrtn.js";
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-LiNmL1ax.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": "2.6.0",
3
+ "version": "2.6.2-rc-1780478292",
4
4
  "description": "React messaging components built on messaging-core for web applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -0,0 +1,191 @@
1
+ import '../../stream-custom-data'
2
+
3
+ import React, { useEffect } from 'react'
4
+ import {
5
+ Channel as ChannelType,
6
+ QueryChannelAPIResponse,
7
+ StreamChat,
8
+ } from 'stream-chat'
9
+ import {
10
+ Channel,
11
+ Chat,
12
+ MessageList,
13
+ MessageUIComponentProps,
14
+ Window,
15
+ } from 'stream-chat-react'
16
+ import { describe, expect, it } from 'vitest'
17
+
18
+ import { storyUsers } from '../../stories/decorators/storyUser'
19
+ import { renderWithProviders, screen, waitFor } from '../../test/utils'
20
+
21
+ import { CustomMessage } from './index'
22
+
23
+ type ReproMessage = {
24
+ id: string
25
+ text: string
26
+ user: (typeof storyUsers)['visitor']
27
+ attachments?: Array<Record<string, unknown>>
28
+ i18n?: Record<string, string>
29
+ }
30
+
31
+ const createMockChannel = async (
32
+ client: StreamChat,
33
+ messages: ReproMessage[]
34
+ ) => {
35
+ const mockMessages = messages.map((msg, index) => ({
36
+ ...msg,
37
+ type: 'regular' as const,
38
+ created_at: new Date(Date.now() - 1000 * 60 * (messages.length - index)),
39
+ updated_at: new Date(Date.now() - 1000 * 60 * (messages.length - index)),
40
+ html: `<p>${msg.text}</p>`,
41
+ attachments: msg.attachments ?? [],
42
+ latest_reactions: [],
43
+ own_reactions: [],
44
+ reaction_counts: {},
45
+ reaction_scores: {},
46
+ reply_count: 0,
47
+ status: 'received',
48
+ cid: 'messaging:mes-789',
49
+ mentioned_users: [],
50
+ }))
51
+
52
+ const channelData = {
53
+ members: [storyUsers.creator.id, storyUsers.visitor.id],
54
+ own_capabilities: ['delete-own-message', 'flag-message'],
55
+ }
56
+
57
+ const channel = client.channel('messaging', 'mes-789', channelData)
58
+
59
+ channel.watch = async () => {
60
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
+ channel.state.messages = mockMessages as unknown as any[]
62
+ channel.state.members = {
63
+ [storyUsers.creator.id]: {
64
+ user: storyUsers.creator,
65
+ user_id: storyUsers.creator.id,
66
+ },
67
+ [storyUsers.visitor.id]: {
68
+ user: storyUsers.visitor,
69
+ user_id: storyUsers.visitor.id,
70
+ },
71
+ }
72
+ return {
73
+ channel: channelData,
74
+ members: [],
75
+ messages: mockMessages,
76
+ watchers: [],
77
+ pinned_messages: [],
78
+ duration: '0ms',
79
+ } as unknown as QueryChannelAPIResponse
80
+ }
81
+
82
+ await channel.watch()
83
+ return channel
84
+ }
85
+
86
+ const Harness: React.FC<{
87
+ message: ReproMessage
88
+ viewerLanguage?: string
89
+ }> = ({ message, viewerLanguage }) => {
90
+ const [client] = React.useState(() => {
91
+ const c = new StreamChat('mock-api-key', { allowServerSideConnect: true })
92
+ c.userID = storyUsers.creator.id
93
+ c.user = storyUsers.creator
94
+ return c
95
+ })
96
+ const [channel, setChannel] = React.useState<ChannelType | null>(null)
97
+
98
+ useEffect(() => {
99
+ createMockChannel(client, [message]).then(setChannel)
100
+ }, [client, message])
101
+
102
+ const MessageComponent = React.useMemo(() => {
103
+ return function CustomMessageComponent(props: MessageUIComponentProps) {
104
+ return <CustomMessage {...props} viewerLanguage={viewerLanguage} />
105
+ }
106
+ }, [viewerLanguage])
107
+
108
+ if (!channel) {
109
+ return <div>loading</div>
110
+ }
111
+
112
+ return (
113
+ <Chat client={client}>
114
+ <Channel channel={channel} Message={MessageComponent}>
115
+ <Window>
116
+ <MessageList />
117
+ </Window>
118
+ </Channel>
119
+ </Chat>
120
+ )
121
+ }
122
+
123
+ const BODY = 'Hey Alex - Im buddies with Hamish @substack'
124
+ const linkAttachment = {
125
+ type: 'og_scrape',
126
+ og_scrape_url: 'https://getsewn.com/',
127
+ image_url: 'https://getsewn.com/og.png',
128
+ title: 'SEWN',
129
+ text: 'SEWN — Turn your audience into drops',
130
+ title_link: 'https://getsewn.com/',
131
+ }
132
+
133
+ const renderBubble = async () =>
134
+ waitFor(() => {
135
+ expect(document.querySelector('.str-chat__message-bubble')).toBeTruthy()
136
+ })
137
+
138
+ /**
139
+ * MES-789: a received message that carries both a body and a link attachment
140
+ * rendered only the attachment card — the text body disappeared — when the
141
+ * viewer's language had an empty translation entry (`en_text: ''`).
142
+ */
143
+ describe('CustomMessage translated text body (MES-789)', () => {
144
+ it('renders the body for a link-attachment message without translations', async () => {
145
+ renderWithProviders(
146
+ <Harness
147
+ message={{
148
+ id: 'm1',
149
+ text: BODY,
150
+ user: storyUsers.visitor,
151
+ attachments: [linkAttachment],
152
+ }}
153
+ />
154
+ )
155
+ await renderBubble()
156
+ expect(screen.getByText(/Im buddies with Hamish/i)).toBeInTheDocument()
157
+ })
158
+
159
+ it('renders the body when the viewer-language translation is an empty string', async () => {
160
+ renderWithProviders(
161
+ <Harness
162
+ viewerLanguage="en-US"
163
+ message={{
164
+ id: 'm2',
165
+ text: BODY,
166
+ user: storyUsers.visitor,
167
+ attachments: [linkAttachment],
168
+ i18n: { language: 'en', en_text: '' },
169
+ }}
170
+ />
171
+ )
172
+ await renderBubble()
173
+ expect(screen.getByText(/Im buddies with Hamish/i)).toBeInTheDocument()
174
+ })
175
+
176
+ it('still shows a real translation when one is present', async () => {
177
+ renderWithProviders(
178
+ <Harness
179
+ viewerLanguage="en-US"
180
+ message={{
181
+ id: 'm3',
182
+ text: 'Bonjour Alex',
183
+ user: storyUsers.visitor,
184
+ i18n: { language: 'fr', en_text: 'Hello Alex' },
185
+ }}
186
+ />
187
+ )
188
+ await renderBubble()
189
+ expect(screen.getByText('Hello Alex')).toBeInTheDocument()
190
+ })
191
+ })
@@ -8,7 +8,7 @@ import {
8
8
 
9
9
  const DeleteAction = () => {
10
10
  const { handleDelete, message } = useMessageContext('CustomMessageActions')
11
- if (message.metadata?.payment_status !== 'paid') return null
11
+ if (message.metadata?.payment_status === 'paid') return null
12
12
  return (
13
13
  <DefaultDropdownActionButton
14
14
  onClick={handleDelete}
@@ -34,6 +34,38 @@ describe('getMessageDisplayText', () => {
34
34
  })
35
35
  ).toBe('Bonjour')
36
36
  })
37
+
38
+ it('falls back to the original message text when the translation is an empty string', () => {
39
+ // Regression test for MES-789: an empty `<lang>_text` entry must not
40
+ // blank the message body. `??` would surface the empty string here.
41
+ expect(
42
+ getMessageDisplayText({
43
+ message: {
44
+ text: 'Hey Alex',
45
+ i18n: {
46
+ language: 'en',
47
+ en_text: '',
48
+ },
49
+ },
50
+ viewerLanguage: 'en-US',
51
+ })
52
+ ).toBe('Hey Alex')
53
+ })
54
+
55
+ it('falls back to the original message text when the translation is whitespace only', () => {
56
+ expect(
57
+ getMessageDisplayText({
58
+ message: {
59
+ text: 'Hey Alex',
60
+ i18n: {
61
+ language: 'en',
62
+ en_text: ' ',
63
+ },
64
+ },
65
+ viewerLanguage: 'en',
66
+ })
67
+ ).toBe('Hey Alex')
68
+ })
37
69
  })
38
70
 
39
71
  describe('normalizeLanguageCode', () => {
@@ -23,5 +23,10 @@ export function getMessageDisplayText({
23
23
  return fallbackText
24
24
  }
25
25
 
26
- return message?.i18n?.[`${normalizedLanguage}_text`] ?? fallbackText
26
+ const translated = message?.i18n?.[`${normalizedLanguage}_text`]
27
+
28
+ // Fall back to the original text when the translation entry is missing or
29
+ // empty/whitespace-only. Using `??` here would surface an empty string and
30
+ // hide the message body in the UI.
31
+ return translated && translated.trim() !== '' ? translated : fallbackText
27
32
  }