@linktr.ee/messaging-react 4.1.5-rc-1786947787 → 4.1.5
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-Ce9211Ms.cjs → Card-BD16XAaT.cjs} +2 -2
- package/dist/{Card-Ce9211Ms.cjs.map → Card-BD16XAaT.cjs.map} +1 -1
- package/dist/{Card-CNOZoAGn.js → Card-DDv7af2K.js} +2 -2
- package/dist/{Card-CNOZoAGn.js.map → Card-DDv7af2K.js.map} +1 -1
- package/dist/index-9elRVMIF.cjs +3 -0
- package/dist/index-9elRVMIF.cjs.map +1 -0
- package/dist/{index-CBGh9Qyj.js → index-qKUd4Fz2.js} +1189 -1188
- package/dist/index-qKUd4Fz2.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/src/components/CustomMessage/CustomMessage.test.tsx +2 -0
- package/src/components/CustomMessage/MessageTag.tsx +10 -1
- package/src/components/CustomMessage/SentMessageDeliveryStatus.test.tsx +141 -0
- package/src/components/CustomMessage/SentMessageDeliveryStatus.tsx +12 -3
- package/src/components/MessagingShell/MessagingShell.test.tsx +0 -20
- package/src/components/MessagingShell/index.tsx +0 -2
- package/src/types.ts +0 -1
- package/dist/index-CBGh9Qyj.js.map +0 -1
- package/dist/index-D0h8u2Iq.cjs +0 -3
- package/dist/index-D0h8u2Iq.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-9elRVMIF.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.MessagingProvider=e.MessagingProvider;exports.MessagingShell=e.MessagingShell;exports.RichCard=e.RichCard;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.useMessaging=e.useMessaging;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.d.ts
CHANGED
|
@@ -208,7 +208,7 @@ export declare const ChannelView: default_2.NamedExoticComponent<ChannelViewProp
|
|
|
208
208
|
* Props that MessagingShell passes through to ChannelView.
|
|
209
209
|
* ChannelViewProps is the source of truth for these props.
|
|
210
210
|
*/
|
|
211
|
-
declare type ChannelViewPassthroughProps = Pick<ChannelViewProps, 'renderMessageInputActions' | 'renderConversationFooter' | 'CustomChannelEmptyState' | 'onBlockParticipantClick' | 'onReportParticipantClick' | 'dmAgentEnabled' | 'onMessageSent' | 'viewerLanguage' | 'renderChannelBanner' | 'customChannelActions' | 'renderChannelActions' | 'onParticipantNameClick' | 'renderMessage' | 'onMessageLinkClick' | 'onOutboundClick' | 'resolveOutbound' | 'showChannelInfo' | 'composerInput'
|
|
211
|
+
declare type ChannelViewPassthroughProps = Pick<ChannelViewProps, 'renderMessageInputActions' | 'renderConversationFooter' | 'CustomChannelEmptyState' | 'onBlockParticipantClick' | 'onReportParticipantClick' | 'dmAgentEnabled' | 'onMessageSent' | 'viewerLanguage' | 'renderChannelBanner' | 'customChannelActions' | 'renderChannelActions' | 'onParticipantNameClick' | 'renderMessage' | 'onMessageLinkClick' | 'onOutboundClick' | 'resolveOutbound' | 'showChannelInfo' | 'composerInput'>;
|
|
212
212
|
|
|
213
213
|
/**
|
|
214
214
|
* ChannelView component props
|
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 r, f as l, L as g, h as c, M, i as h, j as L, k as u, l as d, R as p, m as C, n as v, p as A, q as k, s as F, t as b, u as P, v as f, w as x, o as y, x as z, y as S, z as q, B, D, E, G as R } from "./index-
|
|
1
|
+
import { a as e, b as t, C as i, c as n, d as o, e as m, F as r, f as l, L as g, h as c, M, i as h, j as L, k as u, l as d, R as p, m as C, n as v, p as A, q as k, s as F, t as b, u as P, v as f, w as x, o as y, x as z, y as S, z as q, B, D, E, G as R } from "./index-qKUd4Fz2.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": "4.1.5
|
|
3
|
+
"version": "4.1.5",
|
|
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
|
"chromatic:local": "yarn storybook:build && chromatic"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@linktr.ee/messaging-core": "2.4.0
|
|
53
|
+
"@linktr.ee/messaging-core": "^2.4.0",
|
|
54
54
|
"@linktr.ee/messaging-taxonomy": "^0.10.0",
|
|
55
55
|
"@phosphor-icons/react": "^2.1.10"
|
|
56
56
|
},
|
|
@@ -60,6 +60,8 @@ vi.mock('./MessageTag', () => ({
|
|
|
60
60
|
isMediaAttachmentMessage: () => false,
|
|
61
61
|
isTextAttachmentMessage: () => false,
|
|
62
62
|
isTipOnlyMessage: () => false,
|
|
63
|
+
isTipMessage: () => false,
|
|
64
|
+
hasPaidDeliveryTag: () => false,
|
|
63
65
|
}))
|
|
64
66
|
|
|
65
67
|
vi.mock('./LockedAttachment', () => ({
|
|
@@ -29,7 +29,7 @@ const SparkleIcon = ({ size = 15 }: { size?: number }) => (
|
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
/** Check if a message is a tip based on metadata */
|
|
32
|
-
const isTipMessage = (message: LocalMessage): boolean => {
|
|
32
|
+
export const isTipMessage = (message: LocalMessage): boolean => {
|
|
33
33
|
return message.metadata?.custom_type === MESSAGE_CUSTOM_TYPE.TIP
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -67,6 +67,15 @@ const isTipOrPaidMessage = (message: LocalMessage): boolean => {
|
|
|
67
67
|
return isTipMessage(message) || isPaidMessage(message)
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/** Check if the footer tag on this message already reads "Delivered with …" */
|
|
71
|
+
export const hasPaidDeliveryTag = (message: LocalMessage): boolean => {
|
|
72
|
+
return (
|
|
73
|
+
isTipOrPaidMessage(message) &&
|
|
74
|
+
!isTipOnlyMessage(message) &&
|
|
75
|
+
!!message.metadata?.amount_text
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
70
79
|
/** Check if a message is a tip/paid-only message (no text) */
|
|
71
80
|
export const isTipOnlyMessage = (message: LocalMessage): boolean => {
|
|
72
81
|
return isTipOrPaidMessage(message) && !message.text?.trim()
|
|
@@ -391,6 +391,147 @@ describe('SentMessageDeliveryStatus', () => {
|
|
|
391
391
|
expect(screen.queryByTestId('sending-slot')).not.toBeInTheDocument()
|
|
392
392
|
})
|
|
393
393
|
|
|
394
|
+
it.each([
|
|
395
|
+
['paid', 'MESSAGE_PAID'],
|
|
396
|
+
['tip', 'MESSAGE_TIP'],
|
|
397
|
+
])('does not render the delivery stamp on a %s message', (_label, type) => {
|
|
398
|
+
mockedUseMessageContext.mockReturnValue({
|
|
399
|
+
message: {
|
|
400
|
+
id: 'own-2',
|
|
401
|
+
text: 'thanks!',
|
|
402
|
+
metadata: { custom_type: type, amount_text: '$10.00' },
|
|
403
|
+
},
|
|
404
|
+
isMyMessage: () => true,
|
|
405
|
+
threadList: false,
|
|
406
|
+
} as never)
|
|
407
|
+
|
|
408
|
+
renderWithProviders(<SentMessageDeliveryStatus />)
|
|
409
|
+
|
|
410
|
+
for (const slot of TERMINAL_SLOTS) {
|
|
411
|
+
expect(screen.queryByTestId(slot)).not.toBeInTheDocument()
|
|
412
|
+
}
|
|
413
|
+
expect(screen.queryByTestId('sending-slot')).not.toBeInTheDocument()
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
it('keeps the delivery stamp on a textless paid message, which has no footer tag', () => {
|
|
417
|
+
mockedUseMessageContext.mockReturnValue({
|
|
418
|
+
message: {
|
|
419
|
+
id: 'own-2',
|
|
420
|
+
metadata: { custom_type: 'MESSAGE_PAID', amount_text: '$10.00' },
|
|
421
|
+
},
|
|
422
|
+
isMyMessage: () => true,
|
|
423
|
+
threadList: false,
|
|
424
|
+
} as never)
|
|
425
|
+
|
|
426
|
+
renderWithProviders(<SentMessageDeliveryStatus />)
|
|
427
|
+
|
|
428
|
+
for (const slot of TERMINAL_SLOTS) {
|
|
429
|
+
expect(screen.getByTestId(slot)).toHaveTextContent(/delivered/i)
|
|
430
|
+
}
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
it('does not render the sending stamp on a paid message', () => {
|
|
434
|
+
mockedUseMessageContext.mockReturnValue({
|
|
435
|
+
message: {
|
|
436
|
+
id: 'own-2',
|
|
437
|
+
status: 'sending',
|
|
438
|
+
text: 'thanks!',
|
|
439
|
+
metadata: { custom_type: 'MESSAGE_PAID', amount_text: '$10.00' },
|
|
440
|
+
},
|
|
441
|
+
isMyMessage: () => true,
|
|
442
|
+
threadList: false,
|
|
443
|
+
} as never)
|
|
444
|
+
|
|
445
|
+
renderWithProviders(<SentMessageDeliveryStatus />)
|
|
446
|
+
|
|
447
|
+
expect(screen.queryByTestId('sending-slot')).not.toBeInTheDocument()
|
|
448
|
+
expect(
|
|
449
|
+
screen.queryByTestId('sent-message-sending-status')
|
|
450
|
+
).not.toBeInTheDocument()
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
it('renders "Delivered" instead of "Sending" on a pending textless tip', () => {
|
|
454
|
+
mockedUseMessageContext.mockReturnValue({
|
|
455
|
+
message: {
|
|
456
|
+
id: 'own-2',
|
|
457
|
+
status: 'sending',
|
|
458
|
+
metadata: { custom_type: 'MESSAGE_TIP', amount_text: '$5.00' },
|
|
459
|
+
},
|
|
460
|
+
isMyMessage: () => true,
|
|
461
|
+
threadList: false,
|
|
462
|
+
} as never)
|
|
463
|
+
|
|
464
|
+
renderWithProviders(<SentMessageDeliveryStatus />)
|
|
465
|
+
|
|
466
|
+
expect(screen.getByTestId('sending-slot')).toHaveTextContent(/delivered/i)
|
|
467
|
+
expect(
|
|
468
|
+
screen.queryByTestId('sent-message-sending-status')
|
|
469
|
+
).not.toBeInTheDocument()
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
it('keeps "Failed to send" on a tip that fails', () => {
|
|
473
|
+
mockedUseMessageContext.mockReturnValue({
|
|
474
|
+
message: {
|
|
475
|
+
id: 'own-2',
|
|
476
|
+
status: 'failed',
|
|
477
|
+
type: 'regular',
|
|
478
|
+
metadata: { custom_type: 'MESSAGE_TIP', amount_text: '$5.00' },
|
|
479
|
+
},
|
|
480
|
+
isMyMessage: () => true,
|
|
481
|
+
threadList: false,
|
|
482
|
+
} as never)
|
|
483
|
+
|
|
484
|
+
renderWithProviders(<SentMessageDeliveryStatus />)
|
|
485
|
+
|
|
486
|
+
expect(screen.getByTestId('sent-message-failed-status')).toHaveTextContent(
|
|
487
|
+
/failed to send/i
|
|
488
|
+
)
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
it('keeps the failed and outbound stamps on a paid message', () => {
|
|
492
|
+
mockedUseMessageContext.mockReturnValue({
|
|
493
|
+
message: {
|
|
494
|
+
id: 'own-2',
|
|
495
|
+
status: 'failed',
|
|
496
|
+
type: 'regular',
|
|
497
|
+
text: 'thanks!',
|
|
498
|
+
metadata: { custom_type: 'MESSAGE_PAID', amount_text: '$10.00' },
|
|
499
|
+
},
|
|
500
|
+
isMyMessage: () => true,
|
|
501
|
+
threadList: false,
|
|
502
|
+
} as never)
|
|
503
|
+
|
|
504
|
+
const { rerender } = renderWithProviders(<SentMessageDeliveryStatus />)
|
|
505
|
+
|
|
506
|
+
expect(screen.getByTestId('sent-message-failed-status')).toHaveTextContent(
|
|
507
|
+
/failed to send/i
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
mockedUseMessageContext.mockReturnValue({
|
|
511
|
+
message: {
|
|
512
|
+
id: 'own-2',
|
|
513
|
+
text: 'thanks!',
|
|
514
|
+
metadata: {
|
|
515
|
+
custom_type: 'MESSAGE_PAID',
|
|
516
|
+
amount_text: '$10.00',
|
|
517
|
+
outbound_id: 'outbound-1',
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
isMyMessage: () => true,
|
|
521
|
+
threadList: false,
|
|
522
|
+
} as never)
|
|
523
|
+
rerender(
|
|
524
|
+
<SentMessageDeliveryStatus
|
|
525
|
+
resolvedOutbound={{ id: 'automation-1', name: 'July 9th Class' }}
|
|
526
|
+
/>
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
expect(
|
|
530
|
+
screen.getByTestId('sent-message-outbound-status')
|
|
531
|
+
).toHaveTextContent('July 9th Class')
|
|
532
|
+
expect(screen.queryByText('•')).not.toBeInTheDocument()
|
|
533
|
+
})
|
|
534
|
+
|
|
394
535
|
it('does not render failure status for a received message or thread message', () => {
|
|
395
536
|
mockedUseMessageContext.mockReturnValue({
|
|
396
537
|
message: { id: 'own-1', status: 'failed', type: 'regular' },
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
|
|
14
14
|
import type { OutboundClickHandler, ResolvedOutbound } from '../../types'
|
|
15
15
|
|
|
16
|
+
import { hasPaidDeliveryTag, isTipMessage } from './MessageTag'
|
|
17
|
+
|
|
16
18
|
/**
|
|
17
19
|
* Message status stamps for sending and terminal `MessageStatus` states
|
|
18
20
|
* (sent / delivered / read), plus failed messages.
|
|
@@ -159,6 +161,11 @@ const FailedStatus = () => (
|
|
|
159
161
|
* latest-message-in-thread gate; failed messages bypass `MessageStatus`
|
|
160
162
|
* because it has no failed slot and are shown on every own, non-thread
|
|
161
163
|
* failed message.
|
|
164
|
+
*
|
|
165
|
+
* Messages whose footer tag already reads "Delivered with $10 message" skip
|
|
166
|
+
* this label so delivery is only claimed once (MES-1480); failed and broadcast
|
|
167
|
+
* stamps still apply.
|
|
168
|
+
* Tips read as delivered while still pending; a failed tip still shows Failed.
|
|
162
169
|
*/
|
|
163
170
|
export const SentMessageDeliveryStatus = ({
|
|
164
171
|
onOutboundClick,
|
|
@@ -199,12 +206,14 @@ export const SentMessageDeliveryStatus = ({
|
|
|
199
206
|
/>
|
|
200
207
|
) : null
|
|
201
208
|
|
|
202
|
-
const messageStatus = (
|
|
209
|
+
const messageStatus = hasPaidDeliveryTag(message) ? null : (
|
|
203
210
|
<MessageStatus
|
|
204
211
|
MessageSentStatus={DeliveredStatus}
|
|
205
212
|
MessageDeliveredStatus={DeliveredStatus}
|
|
206
213
|
MessageReadStatus={DeliveredStatus}
|
|
207
|
-
MessageSendingStatus={
|
|
214
|
+
MessageSendingStatus={
|
|
215
|
+
isTipMessage(message) ? DeliveredStatus : SendingStatus
|
|
216
|
+
}
|
|
208
217
|
/>
|
|
209
218
|
)
|
|
210
219
|
|
|
@@ -215,7 +224,7 @@ export const SentMessageDeliveryStatus = ({
|
|
|
215
224
|
return (
|
|
216
225
|
<span className="sent-message-status-row">
|
|
217
226
|
{messageStatus}
|
|
218
|
-
{isLatestRealMessage && (
|
|
227
|
+
{messageStatus && isLatestRealMessage && (
|
|
219
228
|
<span className="sent-message-outbound-separator" aria-hidden="true">
|
|
220
229
|
•
|
|
221
230
|
</span>
|
|
@@ -125,26 +125,6 @@ describe('MessagingShell', () => {
|
|
|
125
125
|
})
|
|
126
126
|
})
|
|
127
127
|
|
|
128
|
-
it('forwards attachmentPreviewList to the selected channel view', async () => {
|
|
129
|
-
queryChannelsMock.mockResolvedValue([makeChannel('existing-1')])
|
|
130
|
-
const AttachmentPreviewList = () => <div>staged attachment</div>
|
|
131
|
-
|
|
132
|
-
renderWithProviders(
|
|
133
|
-
<MessagingShell
|
|
134
|
-
initialParticipantFilter="other-1"
|
|
135
|
-
attachmentPreviewList={AttachmentPreviewList}
|
|
136
|
-
/>
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
await waitFor(() => {
|
|
140
|
-
expect(screen.getByTestId('channel-view')).toBeInTheDocument()
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
expect(capturedChannelViewProps.attachmentPreviewList).toBe(
|
|
144
|
-
AttachmentPreviewList
|
|
145
|
-
)
|
|
146
|
-
})
|
|
147
|
-
|
|
148
128
|
it('calls onExitConversation and renders "Conversation ended" when the user leaves the conversation', async () => {
|
|
149
129
|
queryChannelsMock.mockResolvedValue([makeChannel('existing-1')])
|
|
150
130
|
const onExit = vi.fn()
|
|
@@ -43,7 +43,6 @@ export const MessagingShell: React.FC<MessagingShellProps> = ({
|
|
|
43
43
|
onMessageLinkClick,
|
|
44
44
|
showChannelInfo,
|
|
45
45
|
composerInput,
|
|
46
|
-
attachmentPreviewList,
|
|
47
46
|
}) => {
|
|
48
47
|
const {
|
|
49
48
|
client,
|
|
@@ -270,7 +269,6 @@ export const MessagingShell: React.FC<MessagingShellProps> = ({
|
|
|
270
269
|
onMessageLinkClick={onMessageLinkClick}
|
|
271
270
|
showChannelInfo={showChannelInfo}
|
|
272
271
|
composerInput={composerInput}
|
|
273
|
-
attachmentPreviewList={attachmentPreviewList}
|
|
274
272
|
/>
|
|
275
273
|
</div>
|
|
276
274
|
</div>
|