@linktr.ee/messaging-react 3.5.7-rc-1783068568 → 3.5.7-rc-1783071111

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 (32) hide show
  1. package/dist/{Card-C_TfROjB.js → Card-B5lE5A2D.js} +3 -3
  2. package/dist/{Card-C_TfROjB.js.map → Card-B5lE5A2D.js.map} +1 -1
  3. package/dist/{Card-BhIG7T7c.cjs → Card-CAJnEq1K.cjs} +2 -2
  4. package/dist/{Card-BhIG7T7c.cjs.map → Card-CAJnEq1K.cjs.map} +1 -1
  5. package/dist/{Card-6FOd4_j_.cjs → Card-DYe76Xh3.cjs} +2 -2
  6. package/dist/{Card-6FOd4_j_.cjs.map → Card-DYe76Xh3.cjs.map} +1 -1
  7. package/dist/{Card-CA63InbA.cjs → Card-D_N5hclc.cjs} +2 -2
  8. package/dist/{Card-CA63InbA.cjs.map → Card-D_N5hclc.cjs.map} +1 -1
  9. package/dist/{Card-DN8P8IeT.js → Card-MBp3Wg6r.js} +2 -2
  10. package/dist/{Card-DN8P8IeT.js.map → Card-MBp3Wg6r.js.map} +1 -1
  11. package/dist/{Card-DgWCIgWw.js → Card-rblNjC58.js} +2 -2
  12. package/dist/{Card-DgWCIgWw.js.map → Card-rblNjC58.js.map} +1 -1
  13. package/dist/{LockedThumbnail-DNy4CoCm.js → LockedThumbnail-BqyPPVrO.js} +2 -2
  14. package/dist/{LockedThumbnail-DNy4CoCm.js.map → LockedThumbnail-BqyPPVrO.js.map} +1 -1
  15. package/dist/{LockedThumbnail-DPlNLdpK.cjs → LockedThumbnail-C7PpVZQ0.cjs} +2 -2
  16. package/dist/{LockedThumbnail-DPlNLdpK.cjs.map → LockedThumbnail-C7PpVZQ0.cjs.map} +1 -1
  17. package/dist/assets/index.css +1 -1
  18. package/dist/{index-CxGOKFnx.js → index-BXzwYXLT.js} +1596 -1788
  19. package/dist/index-BXzwYXLT.js.map +1 -0
  20. package/dist/index-BflWZq0e.cjs +2 -0
  21. package/dist/index-BflWZq0e.cjs.map +1 -0
  22. package/dist/index.cjs +1 -1
  23. package/dist/index.js +1 -1
  24. package/package.json +2 -2
  25. package/src/components/ChannelView.stories.tsx +11 -68
  26. package/src/components/ChannelView.test.tsx +13 -54
  27. package/src/components/ChannelView.tsx +28 -74
  28. package/src/styles.css +18 -0
  29. package/dist/index-ClREqfMm.cjs +0 -2
  30. package/dist/index-ClREqfMm.cjs.map +0 -1
  31. package/dist/index-CxGOKFnx.js.map +0 -1
  32. package/src/components/ChannelHeaderRedesign.tsx +0 -231
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-ClREqfMm.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.resolveConversationParticipant=e.resolveConversationParticipant;exports.resolveLinkAttachment=e.resolveLinkAttachment;exports.resolveMediaFromMessage=e.resolveMediaFromMessage;exports.resolveParticipantDisplayName=e.resolveParticipantDisplayName;exports.useComposerLocked=e.useComposerLocked;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-BflWZq0e.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.resolveConversationParticipant=e.resolveConversationParticipant;exports.resolveLinkAttachment=e.resolveLinkAttachment;exports.resolveMediaFromMessage=e.resolveMediaFromMessage;exports.resolveParticipantDisplayName=e.resolveParticipantDisplayName;exports.useComposerLocked=e.useComposerLocked;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 o, d as n, e as m, F as r, f as g, L as l, h as M, M as c, i as u, j as L, k as h, l as d, m as p, n as v, o as C, p as A, q as k, s as F, t as P, u as b, v as f, w as x, x as y, y as S, z as q, B as z, D as B, E as D, G as E } from "./index-CxGOKFnx.js";
1
+ import { a as e, b as t, C as i, c as o, d as n, e as m, F as r, f as g, L as l, h as M, M as c, i as u, j as L, k as h, l as d, m as p, n as v, o as C, p as A, q as k, s as F, t as P, u as b, v as f, w as x, x as y, y as S, z as q, B as z, D as B, E as D, G as E } from "./index-BXzwYXLT.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": "3.5.7-rc-1783068568",
3
+ "version": "3.5.7-rc-1783071111",
4
4
  "description": "React messaging components built on messaging-core for web applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@linktr.ee/component-library": "11.8.6",
41
- "@linktr.ee/messaging-core": "2.3.0-rc-1783068568",
41
+ "@linktr.ee/messaging-core": "2.3.0-rc-1783071111",
42
42
  "@phosphor-icons/react": "^2.1.10"
43
43
  },
44
44
  "devDependencies": {
@@ -16,7 +16,6 @@ import { ChannelView } from './ChannelView'
16
16
  import { ChannelEmptyState } from './MessagingShell/ChannelEmptyState'
17
17
 
18
18
  type ComponentProps = React.ComponentProps<typeof ChannelView>
19
- const SCROLLABLE_MESSAGE_COUNT = 10
20
19
 
21
20
  const meta: Meta<ComponentProps> = {
22
21
  title: 'ChannelView',
@@ -42,9 +41,7 @@ const createMockChannel = async (
42
41
  client: StreamChat,
43
42
  hasMessages = true,
44
43
  followerStatus?: string | boolean,
45
- isFrozen = false,
46
- participantCustomerTag?: string,
47
- hasScrollableMessages = false
44
+ isFrozen = false
48
45
  ) => {
49
46
  const participant = mockParticipants[0]
50
47
 
@@ -104,34 +101,6 @@ const createMockChannel = async (
104
101
  cid: 'messaging:storybook-channel-1',
105
102
  mentioned_users: [],
106
103
  },
107
- ...(hasScrollableMessages
108
- ? Array.from({ length: SCROLLABLE_MESSAGE_COUNT }, (_, index) => {
109
- const isParticipantMessage = index % 2 === 0
110
- const user = isParticipantMessage ? participant : mockUser
111
- const text = isParticipantMessage
112
- ? `Here is another message to show how the redesigned header floats above the thread (${index + 1}).`
113
- : `Replying so the conversation has enough content to scroll under the header (${index + 1}).`
114
-
115
- return {
116
- id: `msg-extra-${index + 1}`,
117
- text,
118
- type: 'regular' as const,
119
- created_at: minutesAgo(Math.max(1, 25 - index)),
120
- updated_at: minutesAgo(Math.max(1, 25 - index)),
121
- user,
122
- html: `<p>${text}</p>`,
123
- attachments: [],
124
- latest_reactions: [],
125
- own_reactions: [],
126
- reaction_counts: {},
127
- reaction_scores: {},
128
- reply_count: 0,
129
- status: 'received',
130
- cid: 'messaging:storybook-channel-1',
131
- mentioned_users: [],
132
- }
133
- })
134
- : []),
135
104
  ]
136
105
  : []
137
106
 
@@ -149,11 +118,7 @@ const createMockChannel = async (
149
118
  }
150
119
 
151
120
  // Create a real channel using the client
152
- const channel = client.channel(
153
- 'messaging',
154
- 'storybook-channel-1',
155
- channelData
156
- )
121
+ const channel = client.channel('messaging', 'storybook-channel-1', channelData)
157
122
 
158
123
  // Mock the watch method to return mocked data
159
124
  channel.watch = async () => {
@@ -177,7 +142,6 @@ const createMockChannel = async (
177
142
  user_id: participant.id,
178
143
  role: 'member',
179
144
  is_account: true,
180
- customerTag: participantCustomerTag,
181
145
  },
182
146
  }
183
147
  return {
@@ -205,7 +169,6 @@ const createMockChannel = async (
205
169
  }
206
170
 
207
171
  type TemplateProps = ComponentProps & {
208
- hasMessages?: boolean
209
172
  followerStatus?: string | boolean
210
173
  isFrozen?: boolean
211
174
  typingUser?: {
@@ -213,17 +176,12 @@ type TemplateProps = ComponentProps & {
213
176
  name?: string
214
177
  image?: string
215
178
  }
216
- participantCustomerTag?: string
217
- hasScrollableMessages?: boolean
218
179
  }
219
180
 
220
181
  const Template: StoryFn<TemplateProps> = (args) => {
221
182
  const {
222
- hasMessages = true,
223
183
  followerStatus,
224
184
  isFrozen = false,
225
- participantCustomerTag,
226
- hasScrollableMessages,
227
185
  typingUser,
228
186
  ...channelViewProps
229
187
  } = args
@@ -235,28 +193,16 @@ const Template: StoryFn<TemplateProps> = (args) => {
235
193
  client.user = mockUser
236
194
  return client
237
195
  })
238
-
196
+
239
197
  const [channel, setChannel] = React.useState<ChannelType | null>(null)
240
198
 
241
199
  useEffect(() => {
242
- createMockChannel(
243
- client,
244
- hasMessages,
245
- followerStatus,
246
- isFrozen,
247
- participantCustomerTag,
248
- hasScrollableMessages
249
- ).then((mockChannel) => {
250
- setChannel(mockChannel)
251
- })
252
- }, [
253
- client,
254
- hasMessages,
255
- followerStatus,
256
- isFrozen,
257
- participantCustomerTag,
258
- hasScrollableMessages,
259
- ])
200
+ createMockChannel(client, true, followerStatus, isFrozen).then(
201
+ (mockChannel) => {
202
+ setChannel(mockChannel)
203
+ }
204
+ )
205
+ }, [client, followerStatus, isFrozen])
260
206
 
261
207
  useEffect(() => {
262
208
  if (!channel || !typingUser) {
@@ -310,9 +256,6 @@ export const ParticipantIdentityTrigger: StoryFn<TemplateProps> = Template.bind(
310
256
  )
311
257
  ParticipantIdentityTrigger.args = {
312
258
  showBackButton: false,
313
- showStarButton: true,
314
- participantCustomerTag: 'CUSTOMER_PAID',
315
- hasScrollableMessages: true,
316
259
  onParticipantNameClick: () =>
317
260
  // eslint-disable-next-line no-alert
318
261
  window.alert('Participant details clicked — open Thread content'),
@@ -321,7 +264,7 @@ ParticipantIdentityTrigger.parameters = {
321
264
  docs: {
322
265
  description: {
323
266
  story:
324
- 'Header with `onParticipantNameClick` set: the redesigned floating header centers the avatar and name pill, shows paid/star badges when present, and fires the callback when clicked (shows an alert here). With the prop omitted (see Default) the previous header remains in use.',
267
+ 'Header with `onParticipantNameClick` set: the participant avatar and name render as one button with a trailing caret in both the mobile and desktop header layouts. Click the identity block to fire the callback (shows an alert here). With the prop omitted (see Default) the header remains non-interactive.',
325
268
  },
326
269
  },
327
270
  }
@@ -599,7 +542,7 @@ const EmptyTemplate: StoryFn<ComponentProps> = (args) => {
599
542
  client.user = mockUser
600
543
  return client
601
544
  })
602
-
545
+
603
546
  const [channel, setChannel] = React.useState<ChannelType | null>(null)
604
547
 
605
548
  useEffect(() => {
@@ -22,9 +22,7 @@ vi.mock('stream-chat-react', () => ({
22
22
  Window: ({ children }: { children: React.ReactNode }) => (
23
23
  <div data-testid="window">{children}</div>
24
24
  ),
25
- MessageList: ({ head }: { head?: React.ReactElement }) => (
26
- <div data-testid="message-list">{head}</div>
27
- ),
25
+ MessageList: () => <div data-testid="message-list" />,
28
26
  WithComponents: ({ children }: { children: React.ReactNode }) => (
29
27
  <>{children}</>
30
28
  ),
@@ -96,19 +94,15 @@ vi.mock('../hooks/useChannelStar', () => ({
96
94
  const createChannel = ({
97
95
  currentUserId = 'visitor-1',
98
96
  currentUserRole = 'owner',
99
- messages = [],
100
97
  otherUser = {
101
98
  id: 'linker-1' as const,
102
99
  name: 'Linker' as string | undefined,
103
100
  username: undefined as string | undefined,
104
101
  is_account: true,
105
102
  },
106
- otherMemberCustomerTag,
107
103
  }: {
108
104
  currentUserId?: 'visitor-1' | 'linker-1'
109
105
  currentUserRole?: 'owner' | 'member'
110
- messages?: Channel['state']['messages']
111
- otherMemberCustomerTag?: string
112
106
  otherUser?: {
113
107
  id: string
114
108
  name?: string
@@ -140,12 +134,11 @@ const createChannel = ({
140
134
  is_account:
141
135
  otherUser.is_account ?? currentUserId === 'visitor-1',
142
136
  },
143
- customerTag: otherMemberCustomerTag,
144
137
  role: currentUserRole === 'owner' ? 'member' : 'owner',
145
138
  },
146
139
  },
147
140
  membership: {},
148
- messages,
141
+ messages: [],
149
142
  },
150
143
  on: vi.fn(),
151
144
  off: vi.fn(),
@@ -210,7 +203,7 @@ describe('ChannelView', () => {
210
203
  )
211
204
  })
212
205
 
213
- it('renders the redesigned header button when onParticipantNameClick is provided', () => {
206
+ it('renders the participant name as a button that fires onParticipantNameClick', () => {
214
207
  const onParticipantNameClick = vi.fn()
215
208
  renderWithProviders(
216
209
  <ChannelView
@@ -219,63 +212,29 @@ describe('ChannelView', () => {
219
212
  />
220
213
  )
221
214
 
222
- const button = screen.getByRole('button', {
215
+ // Both the mobile and desktop header layouts render the button (one is
216
+ // hidden via CSS), so there is at least one match.
217
+ const buttons = screen.getAllByRole('button', {
223
218
  name: /view details for linker/i,
224
219
  })
225
- expect(button).toHaveClass(
220
+ expect(buttons.length).toBeGreaterThanOrEqual(1)
221
+ expect(buttons[0]).toHaveClass(
226
222
  'appearance-none',
227
223
  'border-0',
228
224
  'bg-transparent',
229
- 'flex-col'
225
+ 'rounded-[12px]',
226
+ 'px-1',
227
+ 'py-0.5'
230
228
  )
231
229
 
232
230
  const avatars = screen.getAllByTestId('avatar')
233
231
  expect(avatars.length).toBeGreaterThanOrEqual(1)
234
- expect(avatars[0].closest('button')).toBe(button)
235
- expect(screen.getByTestId('channel-actions-menu')).toBeInTheDocument()
232
+ expect(avatars[0].closest('button')).toBe(buttons[0])
236
233
 
237
- button.click()
234
+ buttons[0].click()
238
235
  expect(onParticipantNameClick).toHaveBeenCalledTimes(1)
239
236
  })
240
237
 
241
- it('renders redesigned header badges for paid and starred conversations', () => {
242
- mockIsStarred = true
243
-
244
- renderWithProviders(
245
- <ChannelView
246
- channel={createChannel({
247
- otherMemberCustomerTag: 'CUSTOMER_PAID',
248
- })}
249
- onParticipantNameClick={vi.fn()}
250
- showStarButton={true}
251
- />
252
- )
253
-
254
- expect(screen.getByLabelText('Paid customer')).toBeInTheDocument()
255
- expect(screen.getByLabelText('Starred conversation')).toBeInTheDocument()
256
- })
257
-
258
- it('renders the redesigned header outside MessageList head when all messages are deleted', () => {
259
- renderWithProviders(
260
- <ChannelView
261
- channel={createChannel({
262
- messages: [
263
- {
264
- id: 'deleted-message-1',
265
- type: 'deleted',
266
- } as Channel['state']['messages'][number],
267
- ],
268
- })}
269
- onParticipantNameClick={vi.fn()}
270
- />
271
- )
272
-
273
- const button = screen.getByRole('button', {
274
- name: /view details for linker/i,
275
- })
276
- expect(button.closest('[data-testid="message-list"]')).toBeNull()
277
- })
278
-
279
238
  it('renders the actions menu by default', () => {
280
239
  renderWithProviders(<ChannelView channel={createChannel()} />)
281
240
 
@@ -25,7 +25,6 @@ import { resolveParticipantDisplayName } from '../utils/resolveParticipantDispla
25
25
 
26
26
  import { Avatar } from './Avatar'
27
27
  import { ChannelActionsMenu } from './ChannelActionsMenu'
28
- import { ChannelHeaderRedesign } from './ChannelHeaderRedesign'
29
28
  import { CustomDateSeparator } from './CustomDateSeparator'
30
29
  import { CustomMessage } from './CustomMessage'
31
30
  import { CustomMessageActions } from './CustomMessage/CustomMessageActions'
@@ -465,34 +464,6 @@ const ChannelViewInner: React.FC<{
465
464
  dmAgentEnabled &&
466
465
  currentUserIsAccount === false &&
467
466
  participantIsAccount === true
468
- const hasVisibleMessages = channel.state.messages.some(
469
- (message) => message.type !== 'deleted'
470
- )
471
- const channelBanner = renderChannelBanner ? (
472
- <React.Fragment key="lt-channel-banner">
473
- {renderChannelBanner()}
474
- </React.Fragment>
475
- ) : null
476
- const redesignedHeader = onParticipantNameClick ? (
477
- <ChannelHeaderRedesign
478
- onBack={onBack}
479
- showBackButton={showBackButton}
480
- showChannelInfo={showChannelInfo}
481
- showStarButton={showStarButton}
482
- dmAgentEnabled={showDmAgentHeader}
483
- onLeaveConversation={onLeaveConversation}
484
- onBlockParticipant={onBlockParticipant}
485
- showDeleteConversation={showDeleteConversation}
486
- showBlockParticipant={showBlockParticipant}
487
- showReportParticipant={showReportParticipant}
488
- onDeleteConversationClick={onDeleteConversationClick}
489
- onBlockParticipantClick={onBlockParticipantClick}
490
- onReportParticipantClick={onReportParticipantClick}
491
- customChannelActions={customChannelActions}
492
- renderChannelActions={renderChannelActions}
493
- onParticipantNameClick={onParticipantNameClick}
494
- />
495
- ) : null
496
467
 
497
468
  // Prevents all message instances from unmounting when ChannelViewInner re-renders
498
469
  const MessageOverride = useCallback(
@@ -526,58 +497,41 @@ const ChannelViewInner: React.FC<{
526
497
  >
527
498
  <Window>
528
499
  {/* Custom Channel Header */}
529
- {!onParticipantNameClick && (
530
- <>
531
- <div key="lt-channel-header">
532
- <CustomChannelHeader
533
- onBack={onBack}
534
- showBackButton={showBackButton}
535
- showChannelInfo={showChannelInfo}
536
- showStarButton={showStarButton}
537
- dmAgentEnabled={showDmAgentHeader}
538
- onLeaveConversation={onLeaveConversation}
539
- onBlockParticipant={onBlockParticipant}
540
- showDeleteConversation={showDeleteConversation}
541
- showBlockParticipant={showBlockParticipant}
542
- showReportParticipant={showReportParticipant}
543
- onDeleteConversationClick={onDeleteConversationClick}
544
- onBlockParticipantClick={onBlockParticipantClick}
545
- onReportParticipantClick={onReportParticipantClick}
546
- customChannelActions={customChannelActions}
547
- renderChannelActions={renderChannelActions}
548
- renderHeaderTitleBadges={renderHeaderTitleBadges}
549
- />
550
- </div>
551
- {channelBanner}
552
- </>
553
- )}
500
+ <div key="lt-channel-header">
501
+ <CustomChannelHeader
502
+ onBack={onBack}
503
+ showBackButton={showBackButton}
504
+ showChannelInfo={showChannelInfo}
505
+ showStarButton={showStarButton}
506
+ dmAgentEnabled={showDmAgentHeader}
507
+ onLeaveConversation={onLeaveConversation}
508
+ onBlockParticipant={onBlockParticipant}
509
+ showDeleteConversation={showDeleteConversation}
510
+ showBlockParticipant={showBlockParticipant}
511
+ showReportParticipant={showReportParticipant}
512
+ onDeleteConversationClick={onDeleteConversationClick}
513
+ onBlockParticipantClick={onBlockParticipantClick}
514
+ onReportParticipantClick={onReportParticipantClick}
515
+ customChannelActions={customChannelActions}
516
+ renderChannelActions={renderChannelActions}
517
+ onParticipantNameClick={onParticipantNameClick}
518
+ renderHeaderTitleBadges={renderHeaderTitleBadges}
519
+ />
520
+ </div>
554
521
 
555
- {/* Stream only renders MessageList.head when there are messages, so
556
- empty redesigned channels need the header and banner in normal flow. */}
557
- {onParticipantNameClick && !hasVisibleMessages && (
558
- <div className="px-4 @lg:px-6">
559
- {redesignedHeader}
560
- {channelBanner}
561
- </div>
562
- )}
522
+ {/* Custom Banner/Summary */}
523
+ {renderChannelBanner ? (
524
+ <React.Fragment key="lt-channel-banner">
525
+ {renderChannelBanner()}
526
+ </React.Fragment>
527
+ ) : null}
563
528
 
564
529
  {/* Message List */}
565
530
  <div
566
531
  key="lt-channel-message-list"
567
532
  className="flex-1 overflow-hidden relative"
568
533
  >
569
- <MessageList
570
- head={
571
- onParticipantNameClick && hasVisibleMessages ? (
572
- <div className="sticky top-0 z-10">
573
- {redesignedHeader}
574
- {channelBanner}
575
- </div>
576
- ) : undefined
577
- }
578
- hideDeletedMessages
579
- hideNewMessageSeparator={false}
580
- />
534
+ <MessageList hideDeletedMessages hideNewMessageSeparator={false} />
581
535
  </div>
582
536
 
583
537
  {renderConversationFooter ? (
package/src/styles.css CHANGED
@@ -299,6 +299,24 @@
299
299
  background-color: transparent;
300
300
  }
301
301
 
302
+ /* iOS/WebKit soft-keyboard safety: complete the flex-column min-height chain.
303
+ WebKit (every iOS browser — Safari and in-app browsers alike) won't shrink a
304
+ flex child below its content height unless each ancestor in the column opts
305
+ out of the default `min-height: auto`. Stream sets `height: 100%` on this
306
+ container chain but no `min-height`, and only `.str-chat__main-panel-inner`
307
+ ships `min-height: 0`. Without completing the chain above it, a keyboard-
308
+ shrunk surface can't squeeze `main-panel`, so the composer's link-preview
309
+ list keeps its full height and pushes the textarea + send button below the
310
+ keyboard (the composer internals already handle their own squeeze via
311
+ `min-h-0` in CustomMessageInput — this lets that squeeze actually reach them).
312
+ Chromium is lenient here, so this only manifests on-device. */
313
+ .str-chat,
314
+ .str-chat__channel,
315
+ .str-chat__channel .str-chat__container,
316
+ .str-chat__channel .str-chat__container .str-chat__main-panel {
317
+ min-height: 0;
318
+ }
319
+
302
320
  .str-chat__message.str-chat__message--me {
303
321
  .str-chat__attachment-list .str-chat__message-attachment--card {
304
322
  color: white;