@linktr.ee/messaging-react 4.2.0-rc-1787325521 → 4.2.0-rc-1787599724
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-BYJyA83X.js → Card-D7UPq5-g.js} +2 -2
- package/dist/{Card-BYJyA83X.js.map → Card-D7UPq5-g.js.map} +1 -1
- package/dist/{Card-BplJtZao.cjs → Card-DcBZqMBt.cjs} +2 -2
- package/dist/{Card-BplJtZao.cjs.map → Card-DcBZqMBt.cjs.map} +1 -1
- package/dist/assets/index.css +1 -1
- package/dist/{index-BUC9vqhJ.js → index-CSkr_CO5.js} +2089 -1952
- package/dist/index-CSkr_CO5.js.map +1 -0
- package/dist/index-CwkY7QwT.cjs +3 -0
- package/dist/index-CwkY7QwT.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +59 -78
- package/dist/index.js +26 -30
- package/dist/testing.cjs +1 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js +2 -2
- package/dist/testing.js.map +1 -1
- package/package.json +13 -11
- package/src/components/AttachmentCard/utils/icons.ts +7 -6
- package/src/components/Avatar/Avatar.test.tsx +0 -126
- package/src/components/Avatar/getAvatarEmoji.test.ts +0 -39
- package/src/components/ChannelList/CustomChannelPreview.test.tsx +3 -410
- package/src/components/ChannelList/index.test.tsx +1 -132
- package/src/components/ChannelView.stories.tsx +0 -4
- package/src/components/ChannelView.test.tsx +3 -9
- package/src/components/CustomMessage/CustomMessage.stories.tsx +314 -6
- package/src/components/CustomMessage/CustomMessage.test.tsx +2 -56
- package/src/components/CustomMessage/MessageTag.stories.tsx +53 -17
- package/src/components/CustomMessage/MessageTag.test.tsx +40 -7
- package/src/components/CustomMessage/MessageTag.tsx +39 -7
- package/src/components/CustomMessage/SentMessageDeliveryStatus.test.tsx +7 -8
- package/src/components/CustomMessage/SentMessageDeliveryStatus.tsx +1 -2
- package/src/components/CustomMessage/StreamAttachmentMessage.test.tsx +33 -0
- package/src/components/CustomMessage/StreamAttachmentMessage.tsx +12 -5
- package/src/components/CustomMessage/index.tsx +19 -22
- package/src/components/CustomMessageInput/index.tsx +1 -1
- package/src/components/CustomSystemMessage/index.tsx +2 -3
- package/src/components/LinkAttachment/LinkAttachment.test.tsx +0 -327
- package/src/components/MessageAttachment/Audio/AudioAttachment.stories.tsx +165 -58
- package/src/components/MessageAttachment/Audio/WaveformAudioRow.test.tsx +153 -0
- package/src/components/MessageAttachment/Audio/WaveformAudioRow.tsx +450 -0
- package/src/components/MessageAttachment/Audio/audioRowRequester.test.ts +24 -0
- package/src/components/MessageAttachment/Audio/audioRowRequester.ts +16 -0
- package/src/components/MessageAttachment/Audio/index.tsx +83 -77
- package/src/components/MessageAttachment/MessageAttachment.test.tsx +176 -74
- package/src/components/MessageAttachment/_shared/fileMeta.test.ts +0 -44
- package/src/components/MessageAttachment/_shared/useIsClient.ts +23 -0
- package/src/components/MessageAttachment/index.tsx +25 -9
- package/src/components/MessageAttachment/types.ts +19 -9
- package/src/components/MessagingShell/MessagingShell.test.tsx +0 -20
- package/src/components/MessagingShell/index.tsx +0 -2
- package/src/components/RichCard/RichCard.test.tsx +1 -3
- package/src/index.ts +0 -4
- package/src/providers/MessagingProvider.test.tsx +2 -130
- package/src/providers/MessagingProvider.tsx +15 -26
- package/src/styles.css +210 -57
- package/src/testing/createMockMessagingClient.ts +2 -2
- package/src/types.ts +0 -9
- package/dist/index-BUC9vqhJ.js.map +0 -1
- package/dist/index-BkqyKPJP.cjs +0 -3
- package/dist/index-BkqyKPJP.cjs.map +0 -1
- package/src/components/AttachmentCard/MediaPlayer.test.tsx +0 -346
- package/src/components/AttachmentCard/Thumbnail.test.tsx +0 -215
- package/src/components/AttachmentCard/index.test.tsx +0 -160
- package/src/components/ChannelList/ChannelListContext.test.tsx +0 -87
- package/src/components/CustomMessage/Locked.stories.tsx +0 -108
- package/src/components/CustomMessage/PaidMessage.stories.tsx +0 -42
- package/src/components/CustomMessage/Tip.stories.tsx +0 -48
- package/src/components/CustomMessage/TipMessage/TipMessage.stories.tsx +0 -51
- package/src/components/CustomMessage/TipMessage/TipMessage.test.tsx +0 -65
- package/src/components/CustomMessage/TipMessage/index.tsx +0 -27
- package/src/components/FaqList/FaqList.test.tsx +0 -204
- package/src/components/LinkAttachment/components/_shared/BubbleTail.test.tsx +0 -76
- package/src/components/LinkAttachment/components/_shared/CardBody.test.tsx +0 -248
- package/src/components/LinkAttachment/components/_shared/CardCta.test.tsx +0 -123
- package/src/components/LinkAttachment/components/_shared/CardShell.test.tsx +0 -193
- package/src/components/LinkAttachment/components/_shared/CardThumbnail.test.tsx +0 -242
- package/src/components/LinkAttachment/components/_shared/hex.test.ts +0 -37
- package/src/components/LinkAttachment/components/_shared/normalizeExternalHref.test.ts +0 -72
- package/src/components/LinkAttachment/components/_shared/useChinPalette.test.ts +0 -162
- package/src/components/MessageAttachment/MessageAttachment.behavior.test.tsx +0 -182
- package/src/components/MessageAttachment/_shared/Bubble.test.tsx +0 -111
- package/src/components/MessageAttachment/_shared/CarouselNav.test.tsx +0 -36
- package/src/components/MessageAttachment/_shared/CompactDocumentRow.test.tsx +0 -114
- package/src/components/MessageAttachment/_shared/DismissButton.test.tsx +0 -42
- package/src/components/MessageAttachment/_shared/DownloadAction.test.tsx +0 -168
- package/src/components/MessageAttachment/_shared/ImageViewer.test.tsx +0 -52
- package/src/components/MessageAttachment/_shared/MediaStackGrid.test.tsx +0 -69
- package/src/components/MessageAttachment/_shared/PdfViewer.test.tsx +0 -45
- package/src/components/MessageAttachment/_shared/VideoViewer.test.tsx +0 -55
- package/src/components/MessageAttachment/_shared/ViewerShell.test.tsx +0 -246
- package/src/components/MessageAttachment/_shared/triggerDownload.test.ts +0 -113
- package/src/components/MessageAttachment/_shared/useCarousel.test.tsx +0 -68
- package/src/components/MessageAttachment/_shared/viewerChromeClasses.test.ts +0 -16
- package/src/logging/index.test.tsx +0 -136
- package/src/logging/index.tsx +0 -48
- package/src/stories/customMessageStoryTemplate.tsx +0 -202
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { describe, expect, it } from 'vitest'
|
|
3
|
-
|
|
4
|
-
import { renderWithProviders, screen } from '../../test/utils'
|
|
5
|
-
|
|
6
|
-
import AttachmentCard from './index'
|
|
7
|
-
|
|
8
|
-
const thumbnail = <div data-testid="thumbnail">thumbnail</div>
|
|
9
|
-
|
|
10
|
-
describe('AttachmentCard', () => {
|
|
11
|
-
it('applies the dark and light root surfaces and type icon colors', () => {
|
|
12
|
-
const dark = renderWithProviders(
|
|
13
|
-
<AttachmentCard
|
|
14
|
-
variant="dark"
|
|
15
|
-
thumbnail={thumbnail}
|
|
16
|
-
mimeType="application/pdf"
|
|
17
|
-
title="Dark file"
|
|
18
|
-
/>
|
|
19
|
-
)
|
|
20
|
-
expect(screen.getByText('Dark file')).toBeInTheDocument()
|
|
21
|
-
expect(dark.container.firstElementChild).toHaveClass('bg-[#1e2330]')
|
|
22
|
-
expect(dark.container.querySelector('svg')).toHaveClass('text-white/55')
|
|
23
|
-
dark.unmount()
|
|
24
|
-
|
|
25
|
-
const light = renderWithProviders(
|
|
26
|
-
<AttachmentCard
|
|
27
|
-
variant="light"
|
|
28
|
-
thumbnail={thumbnail}
|
|
29
|
-
mimeType="application/pdf"
|
|
30
|
-
title="Light file"
|
|
31
|
-
/>
|
|
32
|
-
)
|
|
33
|
-
expect(light.container.firstElementChild).toHaveClass('bg-white')
|
|
34
|
-
expect(light.container.querySelector('svg')).toHaveClass('text-black/55')
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it('uses the dark placeholder title and dims it when title is absent', () => {
|
|
38
|
-
renderWithProviders(
|
|
39
|
-
<AttachmentCard
|
|
40
|
-
variant="dark"
|
|
41
|
-
thumbnail={thumbnail}
|
|
42
|
-
mimeType="application/pdf"
|
|
43
|
-
placeholderTitle="Waiting for title"
|
|
44
|
-
/>
|
|
45
|
-
)
|
|
46
|
-
expect(screen.getByText('Waiting for title')).toHaveClass('text-white/30')
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('does not render a light title for missing or whitespace-only values', () => {
|
|
50
|
-
const { rerender } = renderWithProviders(
|
|
51
|
-
<AttachmentCard
|
|
52
|
-
variant="light"
|
|
53
|
-
thumbnail={thumbnail}
|
|
54
|
-
mimeType="application/pdf"
|
|
55
|
-
/>
|
|
56
|
-
)
|
|
57
|
-
expect(screen.queryByText('Attachment title')).toBeNull()
|
|
58
|
-
|
|
59
|
-
rerender(
|
|
60
|
-
<AttachmentCard
|
|
61
|
-
variant="light"
|
|
62
|
-
thumbnail={thumbnail}
|
|
63
|
-
mimeType="application/pdf"
|
|
64
|
-
title=" "
|
|
65
|
-
/>
|
|
66
|
-
)
|
|
67
|
-
expect(screen.queryByText(' ')).toBeNull()
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('uses an undimmed title class when a dark title is provided', () => {
|
|
71
|
-
renderWithProviders(
|
|
72
|
-
<AttachmentCard
|
|
73
|
-
variant="dark"
|
|
74
|
-
thumbnail={thumbnail}
|
|
75
|
-
mimeType="application/pdf"
|
|
76
|
-
title="Named file"
|
|
77
|
-
/>
|
|
78
|
-
)
|
|
79
|
-
expect(screen.getByText('Named file')).toHaveClass('text-white')
|
|
80
|
-
expect(screen.getByText('Named file')).not.toHaveClass('text-white/30')
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
it('renders detail only for non-empty values', () => {
|
|
84
|
-
const { rerender } = renderWithProviders(
|
|
85
|
-
<AttachmentCard
|
|
86
|
-
variant="light"
|
|
87
|
-
thumbnail={thumbnail}
|
|
88
|
-
mimeType="application/pdf"
|
|
89
|
-
title="File"
|
|
90
|
-
detail="2 MB"
|
|
91
|
-
/>
|
|
92
|
-
)
|
|
93
|
-
expect(screen.getByText('2 MB')).toBeInTheDocument()
|
|
94
|
-
|
|
95
|
-
rerender(
|
|
96
|
-
<AttachmentCard
|
|
97
|
-
variant="light"
|
|
98
|
-
thumbnail={thumbnail}
|
|
99
|
-
mimeType="application/pdf"
|
|
100
|
-
title="File"
|
|
101
|
-
detail=""
|
|
102
|
-
/>
|
|
103
|
-
)
|
|
104
|
-
expect(screen.queryByText('2 MB')).toBeNull()
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
it('renders optional slots only when supplied', () => {
|
|
108
|
-
const { container } = renderWithProviders(
|
|
109
|
-
<AttachmentCard
|
|
110
|
-
variant="light"
|
|
111
|
-
thumbnail={thumbnail}
|
|
112
|
-
mimeType="application/pdf"
|
|
113
|
-
statusBadge={<span>Ready</span>}
|
|
114
|
-
action={<button type="button">Open</button>}
|
|
115
|
-
topLeft={<span>Left</span>}
|
|
116
|
-
topRight={<span>Right</span>}
|
|
117
|
-
/>
|
|
118
|
-
)
|
|
119
|
-
expect(screen.getByText('Ready')).toBeInTheDocument()
|
|
120
|
-
expect(screen.getByRole('button', { name: 'Open' })).toBeInTheDocument()
|
|
121
|
-
expect(screen.getByText('Left').parentElement).toHaveClass(
|
|
122
|
-
'pointer-events-auto',
|
|
123
|
-
'absolute',
|
|
124
|
-
'left-3',
|
|
125
|
-
'top-3'
|
|
126
|
-
)
|
|
127
|
-
expect(screen.getByText('Right').parentElement).toHaveClass(
|
|
128
|
-
'pointer-events-auto',
|
|
129
|
-
'absolute',
|
|
130
|
-
'right-3',
|
|
131
|
-
'top-3'
|
|
132
|
-
)
|
|
133
|
-
expect(container.querySelectorAll('.pointer-events-auto')).toHaveLength(2)
|
|
134
|
-
|
|
135
|
-
const withoutSlots = renderWithProviders(
|
|
136
|
-
<AttachmentCard
|
|
137
|
-
variant="light"
|
|
138
|
-
thumbnail={thumbnail}
|
|
139
|
-
mimeType="application/pdf"
|
|
140
|
-
/>
|
|
141
|
-
)
|
|
142
|
-
expect(
|
|
143
|
-
withoutSlots.container.querySelectorAll('.pointer-events-auto')
|
|
144
|
-
).toHaveLength(0)
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
it('forwards rootRef and data-testid to the root', () => {
|
|
148
|
-
const rootRef = React.createRef<HTMLDivElement>()
|
|
149
|
-
renderWithProviders(
|
|
150
|
-
<AttachmentCard
|
|
151
|
-
variant="light"
|
|
152
|
-
thumbnail={thumbnail}
|
|
153
|
-
mimeType="application/pdf"
|
|
154
|
-
rootRef={rootRef}
|
|
155
|
-
data-testid="attachment-card"
|
|
156
|
-
/>
|
|
157
|
-
)
|
|
158
|
-
expect(screen.getByTestId('attachment-card')).toBe(rootRef.current)
|
|
159
|
-
})
|
|
160
|
-
})
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import type { Channel } from 'stream-chat'
|
|
2
|
-
import { describe, expect, it, vi } from 'vitest'
|
|
3
|
-
|
|
4
|
-
import { fireEvent, renderWithProviders, screen } from '../../test/utils'
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
ChannelListProvider,
|
|
8
|
-
useChannelListContext,
|
|
9
|
-
} from './ChannelListContext'
|
|
10
|
-
|
|
11
|
-
const channel = { id: 'channel-1' } as Channel
|
|
12
|
-
|
|
13
|
-
function ContextConsumer() {
|
|
14
|
-
const value = useChannelListContext()
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<div>
|
|
18
|
-
<span data-testid="selected-channel">
|
|
19
|
-
{value.selectedChannel?.id ?? 'undefined'}
|
|
20
|
-
</span>
|
|
21
|
-
<span data-testid="debug">{String(value.debug)}</span>
|
|
22
|
-
<span data-testid="render-message-preview">
|
|
23
|
-
{value.renderMessagePreview ? 'provided' : 'undefined'}
|
|
24
|
-
</span>
|
|
25
|
-
<span data-testid="channel-preview">
|
|
26
|
-
{value.channelPreview ? 'provided' : 'undefined'}
|
|
27
|
-
</span>
|
|
28
|
-
<span data-testid="viewer-language">
|
|
29
|
-
{value.viewerLanguage ?? 'undefined'}
|
|
30
|
-
</span>
|
|
31
|
-
<button type="button" onClick={() => value.onChannelSelect(channel)}>
|
|
32
|
-
Select channel
|
|
33
|
-
</button>
|
|
34
|
-
</div>
|
|
35
|
-
)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
describe('ChannelListContext', () => {
|
|
39
|
-
it('provides safe documented defaults without a provider', () => {
|
|
40
|
-
renderWithProviders(<ContextConsumer />)
|
|
41
|
-
|
|
42
|
-
expect(screen.getByTestId('selected-channel')).toHaveTextContent(
|
|
43
|
-
'undefined'
|
|
44
|
-
)
|
|
45
|
-
expect(screen.getByTestId('debug')).toHaveTextContent('false')
|
|
46
|
-
expect(screen.getByTestId('render-message-preview')).toHaveTextContent(
|
|
47
|
-
'undefined'
|
|
48
|
-
)
|
|
49
|
-
expect(screen.getByTestId('channel-preview')).toHaveTextContent('undefined')
|
|
50
|
-
expect(screen.getByTestId('viewer-language')).toHaveTextContent('undefined')
|
|
51
|
-
expect(() => fireEvent.click(screen.getByRole('button'))).not.toThrow()
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('exposes the value supplied by a provider', () => {
|
|
55
|
-
const onChannelSelect = vi.fn()
|
|
56
|
-
const ChannelPreview = () => null
|
|
57
|
-
const renderMessagePreview = () => 'preview'
|
|
58
|
-
|
|
59
|
-
renderWithProviders(
|
|
60
|
-
<ChannelListProvider
|
|
61
|
-
value={{
|
|
62
|
-
selectedChannel: channel,
|
|
63
|
-
onChannelSelect,
|
|
64
|
-
debug: true,
|
|
65
|
-
renderMessagePreview,
|
|
66
|
-
channelPreview: ChannelPreview,
|
|
67
|
-
viewerLanguage: 'es',
|
|
68
|
-
}}
|
|
69
|
-
>
|
|
70
|
-
<ContextConsumer />
|
|
71
|
-
</ChannelListProvider>
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
expect(screen.getByTestId('selected-channel')).toHaveTextContent(
|
|
75
|
-
'channel-1'
|
|
76
|
-
)
|
|
77
|
-
expect(screen.getByTestId('debug')).toHaveTextContent('true')
|
|
78
|
-
expect(screen.getByTestId('render-message-preview')).toHaveTextContent(
|
|
79
|
-
'provided'
|
|
80
|
-
)
|
|
81
|
-
expect(screen.getByTestId('channel-preview')).toHaveTextContent('provided')
|
|
82
|
-
expect(screen.getByTestId('viewer-language')).toHaveTextContent('es')
|
|
83
|
-
|
|
84
|
-
fireEvent.click(screen.getByRole('button'))
|
|
85
|
-
expect(onChannelSelect).toHaveBeenCalledWith(channel)
|
|
86
|
-
})
|
|
87
|
-
})
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import type { Meta } from '@storybook/react'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
Template,
|
|
5
|
-
type TemplateProps,
|
|
6
|
-
} from '../../stories/customMessageStoryTemplate'
|
|
7
|
-
import {
|
|
8
|
-
currentUserArgType,
|
|
9
|
-
storyUsers,
|
|
10
|
-
} from '../../stories/decorators/storyUser'
|
|
11
|
-
|
|
12
|
-
const meta: Meta = {
|
|
13
|
-
title: 'CustomMessage/Locked Attachment',
|
|
14
|
-
argTypes: currentUserArgType,
|
|
15
|
-
args: { currentUser: storyUsers.creator },
|
|
16
|
-
parameters: {
|
|
17
|
-
layout: 'fullscreen',
|
|
18
|
-
},
|
|
19
|
-
}
|
|
20
|
-
export default meta
|
|
21
|
-
|
|
22
|
-
export const LockedAttachment = Template.bind({})
|
|
23
|
-
LockedAttachment.args = {
|
|
24
|
-
messages: [
|
|
25
|
-
{
|
|
26
|
-
id: 'msg-1',
|
|
27
|
-
text: 'Can I have your workout plan?',
|
|
28
|
-
user: storyUsers.visitor,
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
id: 'msg-2',
|
|
32
|
-
text: 'Yes, of course!',
|
|
33
|
-
user: storyUsers.creator,
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
id: 'msg-3',
|
|
37
|
-
text: 'let me know if you have any questions!',
|
|
38
|
-
user: storyUsers.creator,
|
|
39
|
-
metadata: {
|
|
40
|
-
custom_type: 'MESSAGE_ATTACHMENT',
|
|
41
|
-
amount_text: 'AU$9.99',
|
|
42
|
-
attachment_title: "Alicia's Workout Plan",
|
|
43
|
-
attachment_mime_type: 'video/mp4',
|
|
44
|
-
attachment_thumbnail: '/video-thumbnail-blurred.jpg',
|
|
45
|
-
attachment_detail: '1:20',
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
id: 'msg-4',
|
|
50
|
-
text: 'Looks amazing, unlocking now!',
|
|
51
|
-
user: storyUsers.visitor,
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
} satisfies TemplateProps
|
|
55
|
-
|
|
56
|
-
export const LockedTextAttachment = Template.bind({})
|
|
57
|
-
LockedTextAttachment.args = {
|
|
58
|
-
messages: [
|
|
59
|
-
{
|
|
60
|
-
id: 'msg-1',
|
|
61
|
-
text: 'Can you send me the details privately?',
|
|
62
|
-
user: storyUsers.visitor,
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
id: 'msg-2',
|
|
66
|
-
// Empty text: the real content is redacted server-side until purchase,
|
|
67
|
-
// so the bubble renders the blurred placeholder instead.
|
|
68
|
-
text: '',
|
|
69
|
-
user: storyUsers.creator,
|
|
70
|
-
metadata: {
|
|
71
|
-
custom_type: 'MESSAGE_ATTACHMENT',
|
|
72
|
-
amount_text: 'AU$4.99',
|
|
73
|
-
attachment_content_type: 'text',
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
id: 'msg-3',
|
|
78
|
-
text: '',
|
|
79
|
-
user: storyUsers.creator,
|
|
80
|
-
metadata: {
|
|
81
|
-
custom_type: 'MESSAGE_ATTACHMENT',
|
|
82
|
-
amount_text: 'AU$4.99',
|
|
83
|
-
payment_status: 'paid',
|
|
84
|
-
attachment_content_type: 'text',
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
id: 'msg-4',
|
|
89
|
-
// Buyer reveal: backend writes the real text back onto the message.
|
|
90
|
-
text: "Here's the address — see you Thursday at 7!",
|
|
91
|
-
user: storyUsers.creator,
|
|
92
|
-
metadata: {
|
|
93
|
-
custom_type: 'MESSAGE_ATTACHMENT',
|
|
94
|
-
amount_text: 'AU$4.99',
|
|
95
|
-
payment_status: 'paid',
|
|
96
|
-
attachment_content_type: 'text',
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
],
|
|
100
|
-
} satisfies TemplateProps
|
|
101
|
-
LockedTextAttachment.parameters = {
|
|
102
|
-
docs: {
|
|
103
|
-
description: {
|
|
104
|
-
story:
|
|
105
|
-
'A paid text-only message renders as a regular bubble with blurred placeholder copy (msg-2: pending, msg-3: purchased-but-not-yet-revealed) until the real text is revealed (msg-4).',
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { Meta } from '@storybook/react'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
Template,
|
|
5
|
-
type TemplateProps,
|
|
6
|
-
} from '../../stories/customMessageStoryTemplate'
|
|
7
|
-
import {
|
|
8
|
-
currentUserArgType,
|
|
9
|
-
storyUsers,
|
|
10
|
-
} from '../../stories/decorators/storyUser'
|
|
11
|
-
|
|
12
|
-
const meta: Meta = {
|
|
13
|
-
title: 'CustomMessage/Paid Message',
|
|
14
|
-
argTypes: currentUserArgType,
|
|
15
|
-
args: { currentUser: storyUsers.creator },
|
|
16
|
-
parameters: {
|
|
17
|
-
layout: 'fullscreen',
|
|
18
|
-
},
|
|
19
|
-
}
|
|
20
|
-
export default meta
|
|
21
|
-
|
|
22
|
-
// A paid message keeps the original small "Delivered with $X message" tag
|
|
23
|
-
// (gift icon + green text) under the bubble — unlike a tip, it never gets
|
|
24
|
-
// the pill treatment.
|
|
25
|
-
export const Default = Template.bind({})
|
|
26
|
-
Default.args = {
|
|
27
|
-
messages: [
|
|
28
|
-
{
|
|
29
|
-
id: 'msg-1',
|
|
30
|
-
text: "Here's the private coaching link we discussed!",
|
|
31
|
-
user: storyUsers.creator,
|
|
32
|
-
metadata: { custom_type: 'MESSAGE_PAID', amount_text: '$10' },
|
|
33
|
-
},
|
|
34
|
-
// Trailing reply so the paid message is no longer the latest own
|
|
35
|
-
// message — keeps the "✓✓ Delivered" stamp off this example.
|
|
36
|
-
{
|
|
37
|
-
id: 'msg-2',
|
|
38
|
-
text: 'Thank you so much!',
|
|
39
|
-
user: storyUsers.visitor,
|
|
40
|
-
},
|
|
41
|
-
],
|
|
42
|
-
} satisfies TemplateProps
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import type { Meta } from '@storybook/react'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
Template,
|
|
5
|
-
type TemplateProps,
|
|
6
|
-
} from '../../stories/customMessageStoryTemplate'
|
|
7
|
-
import {
|
|
8
|
-
currentUserArgType,
|
|
9
|
-
storyUsers,
|
|
10
|
-
} from '../../stories/decorators/storyUser'
|
|
11
|
-
|
|
12
|
-
const meta: Meta = {
|
|
13
|
-
title: 'CustomMessage/Tip Message',
|
|
14
|
-
argTypes: currentUserArgType,
|
|
15
|
-
args: { currentUser: storyUsers.creator },
|
|
16
|
-
parameters: {
|
|
17
|
-
layout: 'fullscreen',
|
|
18
|
-
},
|
|
19
|
-
}
|
|
20
|
-
export default meta
|
|
21
|
-
|
|
22
|
-
// Figma "Tip with message" (node 2353:14548): a sent tip pill stacked
|
|
23
|
-
// directly above its accompanying text bubble, tail + delivery status below.
|
|
24
|
-
export const TipWithMessage = Template.bind({})
|
|
25
|
-
TipWithMessage.args = {
|
|
26
|
-
messages: [
|
|
27
|
-
{
|
|
28
|
-
id: 'msg-1',
|
|
29
|
-
text: 'Thanks you so much!',
|
|
30
|
-
user: storyUsers.creator,
|
|
31
|
-
metadata: { custom_type: 'MESSAGE_TIP', amount_text: '$1' },
|
|
32
|
-
},
|
|
33
|
-
],
|
|
34
|
-
} satisfies TemplateProps
|
|
35
|
-
|
|
36
|
-
// Same "Tip with message" layout, but textless — only the tip pill renders,
|
|
37
|
-
// no second bubble.
|
|
38
|
-
export const TipWithoutMessage = Template.bind({})
|
|
39
|
-
TipWithoutMessage.args = {
|
|
40
|
-
messages: [
|
|
41
|
-
{
|
|
42
|
-
id: 'msg-1',
|
|
43
|
-
text: '',
|
|
44
|
-
user: storyUsers.creator,
|
|
45
|
-
metadata: { custom_type: 'MESSAGE_TIP', amount_text: '$1' },
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
|
-
} satisfies TemplateProps
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryFn } from '@storybook/react'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import { LocalMessage } from 'stream-chat'
|
|
4
|
-
|
|
5
|
-
import { now } from '../../../stories/decorators/storyTime'
|
|
6
|
-
|
|
7
|
-
import { TipMessage } from '.'
|
|
8
|
-
|
|
9
|
-
type ComponentProps = React.ComponentProps<typeof TipMessage>
|
|
10
|
-
|
|
11
|
-
const meta: Meta<ComponentProps> = {
|
|
12
|
-
title: 'TipMessage',
|
|
13
|
-
component: TipMessage,
|
|
14
|
-
parameters: {
|
|
15
|
-
layout: 'centered',
|
|
16
|
-
},
|
|
17
|
-
}
|
|
18
|
-
export default meta
|
|
19
|
-
|
|
20
|
-
interface MockMessageOptions {
|
|
21
|
-
metadata?: {
|
|
22
|
-
custom_type?: 'MESSAGE_TIP'
|
|
23
|
-
amount_text?: string
|
|
24
|
-
}
|
|
25
|
-
text?: string
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const createMockMessage = (options?: MockMessageOptions): LocalMessage =>
|
|
29
|
-
({
|
|
30
|
-
id: 'msg-1',
|
|
31
|
-
text: options?.text ?? '',
|
|
32
|
-
type: 'regular',
|
|
33
|
-
created_at: now(),
|
|
34
|
-
updated_at: now(),
|
|
35
|
-
metadata: options?.metadata,
|
|
36
|
-
}) as LocalMessage
|
|
37
|
-
|
|
38
|
-
const Template: StoryFn<ComponentProps> = (args) => {
|
|
39
|
-
return (
|
|
40
|
-
<div className="p-12">
|
|
41
|
-
<TipMessage {...args} />
|
|
42
|
-
</div>
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export const Standalone: StoryFn<ComponentProps> = Template.bind({})
|
|
47
|
-
Standalone.args = {
|
|
48
|
-
message: createMockMessage({
|
|
49
|
-
metadata: { custom_type: 'MESSAGE_TIP', amount_text: '$50.00' },
|
|
50
|
-
}),
|
|
51
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { LocalMessage } from 'stream-chat'
|
|
2
|
-
import { describe, expect, it } from 'vitest'
|
|
3
|
-
|
|
4
|
-
import { renderWithProviders, screen } from '../../../test/utils'
|
|
5
|
-
|
|
6
|
-
import { TipMessage } from '.'
|
|
7
|
-
|
|
8
|
-
interface MockMessageOptions {
|
|
9
|
-
metadata?: {
|
|
10
|
-
custom_type?: 'MESSAGE_TIP'
|
|
11
|
-
amount_text?: string
|
|
12
|
-
}
|
|
13
|
-
text?: string
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const createMockMessage = (options?: MockMessageOptions): LocalMessage =>
|
|
17
|
-
({
|
|
18
|
-
id: 'msg-1',
|
|
19
|
-
text: options?.text ?? 'Hello world',
|
|
20
|
-
type: 'regular',
|
|
21
|
-
created_at: new Date(),
|
|
22
|
-
updated_at: new Date(),
|
|
23
|
-
metadata: options?.metadata,
|
|
24
|
-
}) as LocalMessage
|
|
25
|
-
|
|
26
|
-
describe('TipMessage', () => {
|
|
27
|
-
it('renders tip pill regardless of accompanying text', () => {
|
|
28
|
-
renderWithProviders(
|
|
29
|
-
<TipMessage
|
|
30
|
-
message={createMockMessage({
|
|
31
|
-
metadata: { custom_type: 'MESSAGE_TIP', amount_text: '$5.50' },
|
|
32
|
-
})}
|
|
33
|
-
/>
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
const tipTag = screen.getByText('$5.50 tip!')
|
|
37
|
-
expect(tipTag.closest('.message-tip-pill')).not.toBeNull()
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('renders tip pill label for textless tip messages', () => {
|
|
41
|
-
renderWithProviders(
|
|
42
|
-
<TipMessage
|
|
43
|
-
message={createMockMessage({
|
|
44
|
-
text: '',
|
|
45
|
-
metadata: { custom_type: 'MESSAGE_TIP', amount_text: '$50.00' },
|
|
46
|
-
})}
|
|
47
|
-
/>
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
const tipTag = screen.getByText('$50.00 tip!')
|
|
51
|
-
expect(tipTag.closest('.message-tip-pill')).not.toBeNull()
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('returns null when amount_text is missing', () => {
|
|
55
|
-
const { container } = renderWithProviders(
|
|
56
|
-
<TipMessage
|
|
57
|
-
message={createMockMessage({
|
|
58
|
-
metadata: { custom_type: 'MESSAGE_TIP' },
|
|
59
|
-
})}
|
|
60
|
-
/>
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
expect(container.firstChild).toBeNull()
|
|
64
|
-
})
|
|
65
|
-
})
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { MESSAGE_CUSTOM_TYPE } from '@linktr.ee/messaging-taxonomy'
|
|
2
|
-
import { LocalMessage } from 'stream-chat'
|
|
3
|
-
|
|
4
|
-
/** Check if a message is a tip based on metadata */
|
|
5
|
-
export const isTipMessage = (message: LocalMessage): boolean => {
|
|
6
|
-
return message.metadata?.custom_type === MESSAGE_CUSTOM_TYPE.TIP
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/** Tip pill — the gift-emoji bubble a tip message attaches, with or without accompanying text */
|
|
10
|
-
export const TipMessage = ({ message }: { message: LocalMessage }) => {
|
|
11
|
-
const amountText = message.metadata?.amount_text
|
|
12
|
-
if (!amountText) return null
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<div className="message-tip-pill inline-flex items-end gap-0.5 max-w-[520px] rounded-[1.5rem] bg-[#43e660] px-4 py-3 text-white">
|
|
16
|
-
<span
|
|
17
|
-
className="flex h-5 items-center text-[20px] leading-none"
|
|
18
|
-
aria-hidden="true"
|
|
19
|
-
>
|
|
20
|
-
🎁
|
|
21
|
-
</span>
|
|
22
|
-
<span className="text-[14px] font-bold leading-[1.43] tracking-[0.28px] text-[rgba(4,4,3,0.9)]">
|
|
23
|
-
{amountText} tip!
|
|
24
|
-
</span>
|
|
25
|
-
</div>
|
|
26
|
-
)
|
|
27
|
-
}
|