@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.
- package/dist/{Card-DvbDgSLn.js → Card-BIb2ouTi.js} +2 -2
- package/dist/{Card-DvbDgSLn.js.map → Card-BIb2ouTi.js.map} +1 -1
- package/dist/{Card-oSacqdqx.js → Card-CafojjYc.js} +3 -3
- package/dist/{Card-oSacqdqx.js.map → Card-CafojjYc.js.map} +1 -1
- package/dist/{Card-CI5xiM4T.js → Card-DmgAq0-y.js} +2 -2
- package/dist/{Card-CI5xiM4T.js.map → Card-DmgAq0-y.js.map} +1 -1
- package/dist/{Card-C6nLpYRM.cjs → Card-Dr3LuTAS.cjs} +2 -2
- package/dist/{Card-C6nLpYRM.cjs.map → Card-Dr3LuTAS.cjs.map} +1 -1
- package/dist/{Card-tWe0vSN0.cjs → Card-DrIyNSwR.cjs} +2 -2
- package/dist/{Card-tWe0vSN0.cjs.map → Card-DrIyNSwR.cjs.map} +1 -1
- package/dist/{Card-BrOaVzOB.cjs → Card-DtoXBrhV.cjs} +2 -2
- package/dist/{Card-BrOaVzOB.cjs.map → Card-DtoXBrhV.cjs.map} +1 -1
- package/dist/{LockedThumbnail-D_HL8AlU.js → LockedThumbnail-B4LMWHDV.js} +2 -2
- package/dist/{LockedThumbnail-D_HL8AlU.js.map → LockedThumbnail-B4LMWHDV.js.map} +1 -1
- package/dist/{LockedThumbnail-P0HCeYW6.cjs → LockedThumbnail-CPAHQ9jA.cjs} +2 -2
- package/dist/{LockedThumbnail-P0HCeYW6.cjs.map → LockedThumbnail-CPAHQ9jA.cjs.map} +1 -1
- package/dist/index-Degc6G3J.cjs +2 -0
- package/dist/index-Degc6G3J.cjs.map +1 -0
- package/dist/{index-DL0Ubrtn.js → index-LiNmL1ax.js} +8 -5
- package/dist/index-LiNmL1ax.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/components/CustomMessage/CustomMessage.translation.test.tsx +191 -0
- package/src/components/CustomMessage/CustomMessageActions.tsx +1 -1
- package/src/utils/getMessageDisplayText.test.ts +32 -0
- package/src/utils/getMessageDisplayText.ts +6 -1
- package/dist/index-DL0Ubrtn.js.map +0 -1
- package/dist/index-DbfBooBe.cjs +0 -2
- 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-
|
|
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-
|
|
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
|
@@ -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
|
|
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
|
-
|
|
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
|
}
|