@linktr.ee/messaging-react 3.6.1 → 3.6.2-rc-1783304704

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 (34) hide show
  1. package/dist/{Card-d0_xEfdM.cjs → Card-6FOd4_j_.cjs} +2 -2
  2. package/dist/{Card-d0_xEfdM.cjs.map → Card-6FOd4_j_.cjs.map} +1 -1
  3. package/dist/{Card-DcUPqd79.cjs → Card-BhIG7T7c.cjs} +2 -2
  4. package/dist/{Card-DcUPqd79.cjs.map → Card-BhIG7T7c.cjs.map} +1 -1
  5. package/dist/{Card-BuR6mbUu.cjs → Card-CA63InbA.cjs} +2 -2
  6. package/dist/{Card-BuR6mbUu.cjs.map → Card-CA63InbA.cjs.map} +1 -1
  7. package/dist/{Card-B0lzpuYf.js → Card-C_TfROjB.js} +3 -3
  8. package/dist/{Card-B0lzpuYf.js.map → Card-C_TfROjB.js.map} +1 -1
  9. package/dist/{Card-BrC7mKtr.js → Card-DN8P8IeT.js} +2 -2
  10. package/dist/{Card-BrC7mKtr.js.map → Card-DN8P8IeT.js.map} +1 -1
  11. package/dist/{Card-CxS7GNqp.js → Card-DgWCIgWw.js} +2 -2
  12. package/dist/{Card-CxS7GNqp.js.map → Card-DgWCIgWw.js.map} +1 -1
  13. package/dist/{LockedThumbnail-BaDgF8qJ.js → LockedThumbnail-DNy4CoCm.js} +2 -2
  14. package/dist/{LockedThumbnail-BaDgF8qJ.js.map → LockedThumbnail-DNy4CoCm.js.map} +1 -1
  15. package/dist/{LockedThumbnail-BDtKMJjz.cjs → LockedThumbnail-DPlNLdpK.cjs} +2 -2
  16. package/dist/{LockedThumbnail-BDtKMJjz.cjs.map → LockedThumbnail-DPlNLdpK.cjs.map} +1 -1
  17. package/dist/index-ClREqfMm.cjs +2 -0
  18. package/dist/index-ClREqfMm.cjs.map +1 -0
  19. package/dist/{index-BPfehHDD.js → index-CxGOKFnx.js} +3786 -3876
  20. package/dist/index-CxGOKFnx.js.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/ChannelHeaderRedesign.tsx +231 -0
  25. package/src/components/ChannelView.stories.tsx +68 -11
  26. package/src/components/ChannelView.test.tsx +54 -13
  27. package/src/components/ChannelView.tsx +74 -28
  28. package/src/components/CustomMessage/index.tsx +0 -67
  29. package/dist/index-BPfehHDD.js.map +0 -1
  30. package/dist/index-G4pnfm4m.cjs +0 -2
  31. package/dist/index-G4pnfm4m.cjs.map +0 -1
  32. package/src/components/CustomMessage/StreamAttachmentMessage.stories.tsx +0 -178
  33. package/src/components/CustomMessage/StreamAttachmentMessage.test.tsx +0 -186
  34. package/src/components/CustomMessage/StreamAttachmentMessage.tsx +0 -417
@@ -1,178 +0,0 @@
1
- import type { Meta, StoryFn } from '@storybook/react'
2
- import classNames from 'classnames'
3
- import React from 'react'
4
- import type { Attachment } from 'stream-chat'
5
-
6
- import {
7
- currentUserArgType,
8
- storyUsers,
9
- type StoryUser,
10
- } from '../../stories/decorators/storyUser'
11
-
12
- import StreamAttachmentMessage from './StreamAttachmentMessage'
13
-
14
- /* ──────────────────────────────────────────────────────────────────
15
- * Stories for `StreamAttachmentMessage` — the toolkit-owned renderer
16
- * that `CustomMessage` uses for every generic Stream-attachment message
17
- * (link OG previews + image / video / audio / pdf / file media).
18
- *
19
- * Unlike `MessageAttachmentConversations.stories.tsx` — which hand-builds
20
- * `MessageAttachment.*` JSX directly — these stories feed raw Stream
21
- * `Attachment[]` shapes through `StreamAttachmentMessage` so they exercise
22
- * the mapping the component actually owns: ordered segment building,
23
- * Stream-attachment → `LinkAttachment` prop mapping, caption
24
- * de-duplication (including emoji preservation), and media clustering.
25
- *
26
- * The component renders bare rows; this wrapper just anchors the run to
27
- * the correct chat edge so sent/received read the way they do in-app.
28
- * ────────────────────────────────────────────────────────────────── */
29
-
30
- const Bubbles: React.FC<{ isMyMessage: boolean; children: React.ReactNode }> = ({
31
- isMyMessage,
32
- children,
33
- }) => (
34
- <div className="mx-auto max-w-2xl p-12">
35
- <div
36
- className={classNames(
37
- 'flex flex-col gap-[2px]',
38
- isMyMessage ? 'items-end' : 'items-start'
39
- )}
40
- >
41
- {children}
42
- </div>
43
- </div>
44
- )
45
-
46
- const Render = (attachments: Attachment[], text?: string): StoryFn<StoryArgs> =>
47
- function StreamAttachmentStory({ currentUser }) {
48
- const isMyMessage = currentUser.id === storyUsers.creator.id
49
- return (
50
- <Bubbles isMyMessage={isMyMessage}>
51
- <StreamAttachmentMessage
52
- groupPosition="single"
53
- isMyMessage={isMyMessage}
54
- message={{ attachments, text }}
55
- />
56
- </Bubbles>
57
- )
58
- }
59
-
60
- const HERO = 'https://picsum.photos/seed/og-hero/1200/630'
61
- const PHOTO = (seed: string) => `https://picsum.photos/seed/${seed}/720/720`
62
- const VIDEO_SRC = '/video-source.mp4'
63
- const VIDEO_POSTER = '/video-thumbnail.jpg'
64
- const PDF_SRC = 'https://www.w3.org/WAI/WCAG21/wcag21.pdf'
65
-
66
- const linkAttachment = (overrides: Partial<Attachment> = {}): Attachment => ({
67
- type: 'link',
68
- og_scrape_url: 'https://linktr.ee/marketplace',
69
- title: 'Linktree Marketplace',
70
- text: 'Browse the best apps to customize your link in bio.',
71
- image_url: HERO,
72
- ...overrides,
73
- })
74
-
75
- const imageAttachment = (seed: string): Attachment => ({
76
- type: 'image',
77
- image_url: PHOTO(seed),
78
- })
79
-
80
- interface StoryArgs {
81
- currentUser: StoryUser
82
- }
83
-
84
- const meta: Meta<StoryArgs> = {
85
- title: 'CustomMessage/StreamAttachmentMessage',
86
- argTypes: currentUserArgType,
87
- args: { currentUser: storyUsers.creator },
88
- parameters: {
89
- layout: 'fullscreen',
90
- // Story render output embeds live React elements; pin source generation
91
- // to the static story code (matches MessageAttachmentConversations).
92
- docs: { source: { type: 'code' } },
93
- },
94
- }
95
- export default meta
96
-
97
- /** Link share with an OG image → the featured card layout. */
98
- export const LinkFeatured = Render([linkAttachment()])
99
-
100
- /** Link share with no thumbnail → the classic (compact) card layout. */
101
- export const LinkClassic = Render([
102
- linkAttachment({ image_url: undefined, thumb_url: undefined }),
103
- ])
104
-
105
- /**
106
- * Link share with an emoji-only caption alongside the URL. The emoji is
107
- * real content, so the caption is preserved as a separate bubble beneath
108
- * the card (regression guard for the caption-dedup predicate).
109
- */
110
- export const LinkWithEmojiCaption = Render(
111
- [linkAttachment({ og_scrape_url: 'https://mrdrnose.itch.io/votv' })],
112
- 'https://mrdrnose.itch.io/votv 👽'
113
- )
114
-
115
- /**
116
- * Caption that is just the link URL → suppressed (the card already shows
117
- * the URL, so no duplicate text bubble).
118
- */
119
- export const LinkWithUrlOnlyCaption = Render(
120
- [linkAttachment()],
121
- 'https://linktr.ee/marketplace'
122
- )
123
-
124
- /** Single image attachment. */
125
- export const SingleImage = Render([imageAttachment('single')])
126
-
127
- /** Several images in one message → a clustered image bubble. */
128
- export const ImageCluster = Render([
129
- imageAttachment('cluster-1'),
130
- imageAttachment('cluster-2'),
131
- imageAttachment('cluster-3'),
132
- ])
133
-
134
- /** Video attachment (poster + play affordance). */
135
- export const Video = Render([
136
- {
137
- type: 'video',
138
- asset_url: VIDEO_SRC,
139
- thumb_url: VIDEO_POSTER,
140
- mime_type: 'video/mp4',
141
- title: 'trailer.mp4',
142
- },
143
- ])
144
-
145
- /** PDF document attachment with a caption. */
146
- export const Pdf = Render(
147
- [
148
- {
149
- type: 'file',
150
- asset_url: PDF_SRC,
151
- mime_type: 'application/pdf',
152
- title: 'launch-checklist.pdf',
153
- file_size: 142_336,
154
- },
155
- ],
156
- 'Section 4 has the details.'
157
- )
158
-
159
- /** Generic (non-PDF) file attachment with a download affordance. */
160
- export const GenericFile = Render([
161
- {
162
- type: 'file',
163
- asset_url: 'https://cdn.example.com/brand-kit.zip',
164
- mime_type: 'application/zip',
165
- title: 'brand-kit.zip',
166
- file_size: 11_500_000,
167
- },
168
- ])
169
-
170
- /**
171
- * Mixed message — media followed by a link share with a caption. Exercises
172
- * ordered-segment building across kinds: the image cluster renders first,
173
- * the link card second, and the caption attaches to the last bubble.
174
- */
175
- export const MixedMediaAndLink = Render(
176
- [imageAttachment('mixed-1'), imageAttachment('mixed-2'), linkAttachment()],
177
- 'Check these out 🔥'
178
- )
@@ -1,186 +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 StreamAttachmentMessage, {
7
- linkCaptionDuplicatesAttachmentUrl,
8
- linkCardPropsFromStreamAttachment,
9
- } from './StreamAttachmentMessage'
10
-
11
- describe('linkCardPropsFromStreamAttachment', () => {
12
- it('uses the featured layout when a thumbnail is present', () => {
13
- expect(
14
- linkCardPropsFromStreamAttachment({
15
- type: 'link',
16
- og_scrape_url: 'https://linktr.ee/someone',
17
- image_url: 'https://cdn.example.com/thumb.jpg',
18
- }).layout
19
- ).toBe('featured')
20
- })
21
-
22
- it('uses the classic layout for a plain link with no thumbnail', () => {
23
- expect(
24
- linkCardPropsFromStreamAttachment({
25
- type: 'link',
26
- og_scrape_url: 'https://linktr.ee/someone',
27
- }).layout
28
- ).toBe('classic')
29
- })
30
-
31
- it('keeps the featured layout for a playable clip with no thumbnail so the player renders', () => {
32
- expect(
33
- linkCardPropsFromStreamAttachment({
34
- type: 'link',
35
- og_scrape_url: 'https://linktr.ee/someone',
36
- asset_url: 'https://cdn.example.com/clip.mp3',
37
- mime_type: 'audio/mpeg',
38
- }).layout
39
- ).toBe('featured')
40
- })
41
- })
42
-
43
- describe('linkCaptionDuplicatesAttachmentUrl', () => {
44
- it('suppresses captions that are just the link plus punctuation', () => {
45
- expect(
46
- linkCaptionDuplicatesAttachmentUrl(
47
- 'https://tr.ee/abc !',
48
- 'https://tr.ee/abc'
49
- )
50
- ).toBe(true)
51
- })
52
-
53
- it('keeps captions that add emoji after the link', () => {
54
- expect(
55
- linkCaptionDuplicatesAttachmentUrl(
56
- 'https://tr.ee/abc 😎',
57
- 'https://tr.ee/abc'
58
- )
59
- ).toBe(false)
60
- })
61
- })
62
-
63
- describe('StreamAttachmentMessage', () => {
64
- it('renders a sent link card and suppresses a URL-only caption', () => {
65
- renderWithProviders(
66
- <StreamAttachmentMessage
67
- groupPosition="single"
68
- isMyMessage={true}
69
- message={{
70
- attachments: [
71
- {
72
- type: 'link',
73
- og_scrape_url: 'https://linktr.ee/someone',
74
- title: 'My Linktree',
75
- text: 'Check out my links',
76
- image_url: 'https://cdn.example.com/thumb.jpg',
77
- },
78
- ],
79
- text: 'https://linktr.ee/someone !',
80
- }}
81
- />
82
- )
83
-
84
- expect(
85
- screen.getByRole('link', { name: /my linktree/i })
86
- ).toHaveAttribute('href', 'https://linktr.ee/someone')
87
- expect(screen.queryByText('https://linktr.ee/someone !')).toBeNull()
88
- })
89
-
90
- it('keeps an emoji-only caption instead of dropping it', () => {
91
- renderWithProviders(
92
- <StreamAttachmentMessage
93
- groupPosition="single"
94
- isMyMessage={false}
95
- message={{
96
- attachments: [
97
- {
98
- type: 'link',
99
- og_scrape_url: 'https://linktr.ee/someone',
100
- title: 'My Linktree',
101
- },
102
- ],
103
- text: 'https://linktr.ee/someone 😎',
104
- }}
105
- />
106
- )
107
-
108
- expect(screen.getByText('https://linktr.ee/someone 😎')).toBeInTheDocument()
109
- })
110
-
111
- it('renders mixed link and media attachments in order', () => {
112
- const { container } = renderWithProviders(
113
- <StreamAttachmentMessage
114
- groupPosition="single"
115
- isMyMessage={true}
116
- message={{
117
- attachments: [
118
- {
119
- type: 'link',
120
- og_scrape_url: 'https://linktr.ee/someone',
121
- title: 'My Linktree',
122
- },
123
- {
124
- type: 'image',
125
- image_url: 'https://cdn.example.com/photo.jpg',
126
- title: 'Vacation',
127
- },
128
- ],
129
- text: 'caption',
130
- }}
131
- />
132
- )
133
-
134
- const link = screen.getByRole('link', { name: /my linktree/i })
135
- const imageBubble = screen.getByTestId('image-attachment')
136
-
137
- expect(link.compareDocumentPosition(imageBubble)).toBe(
138
- Node.DOCUMENT_POSITION_FOLLOWING
139
- )
140
- expect(container).toHaveTextContent('caption')
141
- })
142
-
143
- // The message text must never be dropped when a message carries an
144
- // attachment — only genuine URL-only captions are suppressed.
145
- it('renders the accompanying message text next to a link attachment', () => {
146
- renderWithProviders(
147
- <StreamAttachmentMessage
148
- groupPosition="single"
149
- isMyMessage={true}
150
- message={{
151
- attachments: [
152
- {
153
- type: 'link',
154
- og_scrape_url: 'https://linktr.ee/someone',
155
- title: 'My Linktree',
156
- },
157
- ],
158
- text: 'you have to see this',
159
- }}
160
- />
161
- )
162
-
163
- expect(screen.getByText('you have to see this')).toBeInTheDocument()
164
- })
165
-
166
- it('renders the accompanying message text next to a media attachment', () => {
167
- const { container } = renderWithProviders(
168
- <StreamAttachmentMessage
169
- groupPosition="single"
170
- isMyMessage={false}
171
- message={{
172
- attachments: [
173
- {
174
- type: 'image',
175
- image_url: 'https://cdn.example.com/photo.jpg',
176
- title: 'Vacation',
177
- },
178
- ],
179
- text: 'from the trip',
180
- }}
181
- />
182
- )
183
-
184
- expect(container).toHaveTextContent('from the trip')
185
- })
186
- })