@linktr.ee/messaging-react 2.0.0 → 2.0.1-rc-1778694826

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 (72) hide show
  1. package/dist/Card-CFFNq49v.js +163 -0
  2. package/dist/Card-CFFNq49v.js.map +1 -0
  3. package/dist/Card-CsJvUF_b.js +107 -0
  4. package/dist/Card-CsJvUF_b.js.map +1 -0
  5. package/dist/Card-D32U6KfZ.js +85 -0
  6. package/dist/Card-D32U6KfZ.js.map +1 -0
  7. package/dist/Card-DlMSDSdm.js +132 -0
  8. package/dist/Card-DlMSDSdm.js.map +1 -0
  9. package/dist/Card-DlSSJPip.js +60 -0
  10. package/dist/Card-DlSSJPip.js.map +1 -0
  11. package/dist/Card-zGbhRBwv.js +48 -0
  12. package/dist/Card-zGbhRBwv.js.map +1 -0
  13. package/dist/CardThumbnail-DTBuRQHF.js +239 -0
  14. package/dist/CardThumbnail-DTBuRQHF.js.map +1 -0
  15. package/dist/LockedThumbnail-DpJx169C.js +220 -0
  16. package/dist/LockedThumbnail-DpJx169C.js.map +1 -0
  17. package/dist/assets/index.css +1 -1
  18. package/dist/{index-Brz9orsI.js → index-DfcRe-Hj.js} +939 -889
  19. package/dist/index-DfcRe-Hj.js.map +1 -0
  20. package/dist/index.d.ts +217 -28
  21. package/dist/index.js +16 -15
  22. package/package.json +1 -1
  23. package/src/components/ChannelView.test.tsx +11 -0
  24. package/src/components/ChannelView.tsx +35 -32
  25. package/src/components/CustomMessage/index.tsx +2 -3
  26. package/src/components/CustomTypingIndicator/CustomTypingIndicator.stories.tsx +57 -17
  27. package/src/components/CustomTypingIndicator/CustomTypingIndicator.test.tsx +187 -0
  28. package/src/components/CustomTypingIndicator/DmAgentContext.ts +3 -0
  29. package/src/components/CustomTypingIndicator/index.tsx +101 -37
  30. package/src/components/LinkAttachment/LinkAttachment.stories.tsx +307 -0
  31. package/src/components/LinkAttachment/components/Composer/Card.tsx +117 -0
  32. package/src/components/LinkAttachment/components/Composer/index.ts +2 -0
  33. package/src/components/LinkAttachment/components/Received/Card.tsx +132 -0
  34. package/src/components/LinkAttachment/components/Received/index.ts +2 -0
  35. package/src/components/LinkAttachment/components/Sent/Card.tsx +57 -0
  36. package/src/components/LinkAttachment/components/Sent/index.ts +2 -0
  37. package/src/components/LinkAttachment/components/_shared/CardBody.tsx +117 -0
  38. package/src/components/LinkAttachment/components/_shared/CardCta.tsx +69 -0
  39. package/src/components/LinkAttachment/components/_shared/CardShell.tsx +120 -0
  40. package/src/components/LinkAttachment/components/_shared/CardThumbnail.tsx +156 -0
  41. package/src/components/LinkAttachment/components/_shared/normalizeExternalHref.ts +56 -0
  42. package/src/components/LinkAttachment/index.tsx +68 -0
  43. package/src/components/LinkAttachment/types.ts +69 -0
  44. package/src/components/LockedAttachment/LockedAttachment.stories.tsx +230 -89
  45. package/src/components/LockedAttachment/components/Composer/Card.tsx +221 -0
  46. package/src/components/LockedAttachment/components/Composer/index.ts +2 -0
  47. package/src/components/LockedAttachment/components/Received/Card.tsx +191 -0
  48. package/src/components/LockedAttachment/components/Received/CardActions.tsx +91 -0
  49. package/src/components/LockedAttachment/components/Received/index.ts +2 -0
  50. package/src/components/LockedAttachment/components/Sent/Card.tsx +177 -0
  51. package/src/components/LockedAttachment/components/Sent/index.ts +2 -0
  52. package/src/components/LockedAttachment/components/_shared/CardBody.tsx +94 -0
  53. package/src/components/LockedAttachment/components/_shared/GalleryThumbnail.tsx +178 -0
  54. package/src/components/LockedAttachment/components/_shared/LockBadge.tsx +39 -0
  55. package/src/components/LockedAttachment/components/_shared/LockedCardShell.tsx +36 -0
  56. package/src/components/LockedAttachment/components/_shared/LockedThumbnail.tsx +128 -0
  57. package/src/components/LockedAttachment/index.tsx +43 -12
  58. package/src/components/LockedAttachment/types.ts +17 -0
  59. package/src/components/MediaMessage/index.tsx +8 -2
  60. package/src/index.ts +15 -1
  61. package/src/styles.css +7 -0
  62. package/dist/Card-BHknCeHw.js +0 -138
  63. package/dist/Card-BHknCeHw.js.map +0 -1
  64. package/dist/Card-DT7_ms2p.js +0 -127
  65. package/dist/Card-DT7_ms2p.js.map +0 -1
  66. package/dist/index-Brz9orsI.js.map +0 -1
  67. package/src/components/LockedAttachment/components/Creator/Card.tsx +0 -210
  68. package/src/components/LockedAttachment/components/Creator/index.tsx +0 -2
  69. package/src/components/LockedAttachment/components/Visitor/Card.tsx +0 -155
  70. package/src/components/LockedAttachment/components/Visitor/CardActions.tsx +0 -62
  71. package/src/components/LockedAttachment/components/Visitor/LockBadge.tsx +0 -12
  72. package/src/components/LockedAttachment/components/Visitor/index.ts +0 -2
package/dist/index.d.ts CHANGED
@@ -241,15 +241,38 @@ export declare interface ChannelViewProps {
241
241
  sendButton?: ComponentType<any>;
242
242
  }
243
243
 
244
- export declare interface CreatorCardProps extends LockedAttachmentBaseProps {
244
+ export declare interface ComposerCardProps extends LockedAttachmentBaseProps {
245
+ /** Placeholder shown in the title slot before the composer types one. */
245
246
  placeholderTitle?: string;
247
+ /** Placeholder shown in the amount slot before one is configured. */
246
248
  placeholderAmountText?: string;
247
- isUnlocking?: boolean;
249
+ /**
250
+ * When provided, renders a dismiss X in the thumbnail corner. Called when
251
+ * the composer clicks it to remove the attachment.
252
+ */
248
253
  onDismiss?: () => void;
254
+ /** Fired the first time the composer taps the thumbnail to preview. */
249
255
  onPreviewClick?: () => void;
256
+ /**
257
+ * Lazily loads the underlying source so the composer can preview the
258
+ * attachment they're about to send. Called the first time the thumbnail is
259
+ * tapped; the returned source is cached and reused on subsequent toggles.
260
+ */
250
261
  onFetchSource?: () => Promise<LockedAttachmentSource | void>;
262
+ /**
263
+ * When provided, renders a pencil button in the body bottom-right that the
264
+ * composer can use to edit the attachment metadata (e.g. open the price /
265
+ * gallery editor). Matches the Composer "Button" instance in Figma.
266
+ */
267
+ onEditClick?: () => void;
251
268
  }
252
269
 
270
+ /**
271
+ * @deprecated Renamed to `SentCardProps`. Drafting usages (with `onDismiss`)
272
+ * should migrate to `ComposerCardProps`.
273
+ */
274
+ export declare type CreatorCardProps = SentCardProps;
275
+
253
276
  export declare const CustomMessageProvider: Provider<Partial<CustomMessageRegistry>>;
254
277
 
255
278
  export declare interface CustomMessageRegistry {
@@ -300,9 +323,142 @@ export declare function getMessageDisplayText({ message, viewerLanguage, }: {
300
323
 
301
324
  export declare function isLinkAttachment(a: Attachment): boolean;
302
325
 
326
+ /**
327
+ * Link attachments (image / file media + 1P/3P Link Apps) shown in the chat
328
+ * thread. Mirrors the `LockedAttachment` API — render `LinkAttachment.Composer`
329
+ * while drafting, `LinkAttachment.Sent` after posting, and
330
+ * `LinkAttachment.Received` in the recipient's thread. Maps to the
331
+ * "Attachments" and "LinkApps" sections of the messaging design system.
332
+ *
333
+ * Two visual layouts via the `layout` prop:
334
+ * - **Featured** (default) — 180px hero thumbnail above the body. Used by
335
+ * image / file Attachments and by hero-image LinkApps (Spotify with
336
+ * cover art, TikTok with a frame, etc.).
337
+ * - **Classic** — compact card with no hero thumbnail; title /
338
+ * description / URL / CTA only. Used for LinkApp embeds without
339
+ * artwork (FAQ, Form) and any link preview that lacks OG imagery.
340
+ *
341
+ * Image / file Attachments use `layout="featured"` and skip the title /
342
+ * description / URL body entirely (`CardBody` collapses to nothing when no
343
+ * text content is provided). LinkApps always carry a title + description
344
+ * and prefix the title with an `appIcon` brand badge.
345
+ */
346
+ export declare const LinkAttachment: {
347
+ Composer: (props: LinkAttachmentComposerCardProps) => JSX_2.Element;
348
+ Sent: (props: LinkAttachmentSentCardProps) => JSX_2.Element;
349
+ Received: (props: LinkAttachmentReceivedCardProps) => JSX_2.Element;
350
+ };
351
+
352
+ /**
353
+ * Shared props for the three `LinkAttachment.*` variants (Composer, Sent,
354
+ * Received). Maps to the "Attachments" + "LinkApps" sections of the messaging
355
+ * design system in Figma — a 280px-wide card with an optional 180px hero
356
+ * thumbnail, a title (optionally prefixed with a brand badge for Link Apps),
357
+ * a description, and either a URL footer or a CTA button.
358
+ */
359
+ export declare interface LinkAttachmentBaseProps {
360
+ title?: string;
361
+ /** Placeholder shown in the title slot before one is configured (dark variants only). */
362
+ placeholderTitle?: string;
363
+ /** Secondary description rendered below the title. */
364
+ description?: string;
365
+ /**
366
+ * Optional URL displayed at the bottom of the card (e.g. `tr.ee/briemix`).
367
+ * Ignored when `cta` is provided. Also used as the navigation target for
368
+ * the Received card when no `cta` is set.
369
+ */
370
+ url?: string;
371
+ /** MIME type of the hero thumbnail — drives the type icon for empty states. */
372
+ mimeType?: string;
373
+ /** Hero thumbnail (180px tall) shown above the title block. */
374
+ thumbnailUrl?: string;
375
+ /**
376
+ * Source URL for playable media (video, audio). When provided alongside a
377
+ * video / audio `mimeType`, the hero region renders an inline player with
378
+ * native controls instead of the static thumbnail / type-icon.
379
+ */
380
+ sourceUrl?: string;
381
+ /**
382
+ * Visual layout — `'featured'` keeps the 180px hero thumbnail above the
383
+ * body, `'classic'` drops the hero entirely for a compact text-only
384
+ * card. Defaults to `'featured'`.
385
+ */
386
+ layout?: LinkAttachmentLayout;
387
+ /**
388
+ * Optional 16x16 brand badge rendered before the title (used by Link Apps:
389
+ * Spotify, TikTok, FAQ, Form, etc.). Consumers render whatever they want
390
+ * — typically a colored 4px-rounded square containing a glyph or `<img>`.
391
+ */
392
+ appIcon?: default_2.ReactNode;
393
+ /**
394
+ * Optional call-to-action rendered below the description. When set,
395
+ * replaces the URL footer (e.g. FAQ "View FAQs", Form "Complete form").
396
+ */
397
+ cta?: LinkAttachmentCta;
398
+ }
399
+
400
+ export declare interface LinkAttachmentComposerCardProps extends LinkAttachmentBaseProps {
401
+ /**
402
+ * When provided, renders a dismiss X in the thumbnail corner. Called when
403
+ * the composer clicks it to remove the attachment.
404
+ */
405
+ onDismiss?: () => void;
406
+ /**
407
+ * When provided, renders a pencil button to the right of the description
408
+ * that the composer can use to edit the attachment metadata.
409
+ */
410
+ onEditClick?: () => void;
411
+ }
412
+
413
+ export declare interface LinkAttachmentCta {
414
+ label: string;
415
+ /** When set, the CTA renders as an `<a>` opening in a new tab. */
416
+ href?: string;
417
+ /** When set, called on click (in addition to `href` navigation if provided). */
418
+ onClick?: () => void;
419
+ }
420
+
421
+ /**
422
+ * Visual layout for a `LinkAttachment.*` card.
423
+ *
424
+ * - `featured` — full card with a 180px hero thumbnail above the body.
425
+ * The default; matches the "Attachments" frames and the hero-image
426
+ * "LinkApps" frames in Figma.
427
+ * - `classic` — compact card without a hero thumbnail. Title /
428
+ * description / URL / CTA only. Used for Link App embeds that don't
429
+ * carry artwork (and for plain link previews without OG imagery).
430
+ */
431
+ export declare type LinkAttachmentLayout = 'featured' | 'classic';
432
+
433
+ export declare interface LinkAttachmentReceivedCardProps extends LinkAttachmentBaseProps {
434
+ /**
435
+ * Fired when the recipient activates the card. Behavior depends on how
436
+ * the card is configured:
437
+ * - **Link app with a CTA** (FAQ / Form): the CTA owns navigation;
438
+ * `onClick` fires when the recipient taps the CTA itself, alongside
439
+ * `cta.onClick` (use for analytics).
440
+ * - **Link app with a URL** (Spotify / TikTok / generic link): the card
441
+ * chrome is an `<a target="_blank">` opening `url` — `onClick` fires
442
+ * alongside the navigation (use for analytics).
443
+ * - **Image / file / placeholder attachment**: the card has no URL, so
444
+ * it renders as a button. `onClick` is the consumer's hook for
445
+ * opening an image / file preview (lightbox).
446
+ * - **Video / audio attachment**: the shell stays non-interactive so
447
+ * the native media controls remain operable — `onClick` is ignored
448
+ * in this configuration.
449
+ */
450
+ onClick?: () => void;
451
+ }
452
+
453
+ export declare interface LinkAttachmentSentCardProps extends LinkAttachmentBaseProps {
454
+ }
455
+
303
456
  export declare const LockedAttachment: {
304
- Creator: (props: CreatorCardProps) => JSX_2.Element;
305
- Visitor: (props: VisitorCardProps) => JSX_2.Element;
457
+ Composer: (props: ComposerCardProps) => JSX_2.Element;
458
+ Sent: (props: SentCardProps) => JSX_2.Element;
459
+ Received: (props: ReceivedCardProps) => JSX_2.Element;
460
+ Creator: (props: SentCardProps) => JSX_2.Element;
461
+ Visitor: (props: ReceivedCardProps) => JSX_2.Element;
306
462
  };
307
463
 
308
464
  declare interface LockedAttachmentBaseProps {
@@ -312,6 +468,14 @@ declare interface LockedAttachmentBaseProps {
312
468
  detail?: string;
313
469
  amountText?: string;
314
470
  paymentStatus?: PaymentStatus;
471
+ /**
472
+ * When provided with 2+ items, the card renders as a mixed-media carousel
473
+ * (e.g. a couple of photos + a video) instead of a single thumbnail. Each
474
+ * item brings its own thumbnail and optional source so that
475
+ * `LockedAttachment.Composer` / `.Sent` / `.Received` can all share the
476
+ * same carousel chrome.
477
+ */
478
+ gallery?: LockedAttachmentGalleryItem[];
315
479
  }
316
480
 
317
481
  export declare interface LockedAttachmentContextValue {
@@ -321,6 +485,15 @@ export declare interface LockedAttachmentContextValue {
321
485
  onFetchSource?: (message: LocalMessage, channel: Channel) => Promise<LockedAttachmentSource | void>;
322
486
  }
323
487
 
488
+ export declare interface LockedAttachmentGalleryItem {
489
+ /** MIME type of this carousel item — drives the per-item play / lock affordance. */
490
+ mimeType: string;
491
+ /** Poster image used for both the locked (blurred) and unlocked preview state. */
492
+ thumbnailUrl?: string;
493
+ /** Underlying source (image or video URL) — shown only when unlocked. */
494
+ sourceUrl?: string;
495
+ }
496
+
324
497
  export declare interface LockedAttachmentSource {
325
498
  /** Proxied URL used by the media player for in-app playback. */
326
499
  sourceUrl: string;
@@ -501,10 +674,48 @@ export declare interface Participant {
501
674
  */
502
675
  declare type PaymentStatus = 'pending' | 'paid' | 'failed' | 'refunded';
503
676
 
677
+ export declare interface ReceivedCardProps extends LockedAttachmentBaseProps {
678
+ /**
679
+ * Called when the recipient clicks Unlock on an unpaid attachment.
680
+ * Use this to open a checkout flow. Omit to hide the Unlock button.
681
+ */
682
+ onUnlockClick?: () => void;
683
+ /**
684
+ * Called to fetch the attachment source — fired automatically when
685
+ * `paymentStatus` transitions to `'paid'`, or immediately on click when
686
+ * `paymentStatus` is already `'paid'`. Return a `LockedAttachmentSource`
687
+ * to unlock the card.
688
+ */
689
+ onFetchSource?: () => Promise<LockedAttachmentSource | void>;
690
+ /**
691
+ * Called when the recipient clicks Download on an unlocked card.
692
+ * Omit to hide the Download button.
693
+ */
694
+ onDownloadClick?: () => void;
695
+ /**
696
+ * When true, shows a loading spinner on the Unlock button.
697
+ * Driven by the LockedAttachmentContext (e.g. checkout in progress).
698
+ */
699
+ isUnlocking?: boolean;
700
+ }
701
+
504
702
  export declare function resolveLinkAttachment(message: LocalMessage): Attachment | undefined;
505
703
 
506
704
  export declare function resolveMediaFromMessage(message: LocalMessage): MediaMessageResolved | null;
507
705
 
706
+ export declare interface SentCardProps extends LockedAttachmentBaseProps {
707
+ /** Placeholder shown in the title slot when no title is set. */
708
+ placeholderTitle?: string;
709
+ /** Fired the first time the sender taps the thumbnail to preview their own attachment. */
710
+ onPreviewClick?: () => void;
711
+ /**
712
+ * Lazily loads the underlying source so the sender can preview the attachment.
713
+ * Called the first time the thumbnail is tapped; the returned source is cached
714
+ * and reused on subsequent toggles.
715
+ */
716
+ onFetchSource?: () => Promise<LockedAttachmentSource | void>;
717
+ }
718
+
508
719
  export declare function useCustomMessage<K extends keyof CustomMessageRegistry>(key: K): CustomMessageRegistry[K];
509
720
 
510
721
  /**
@@ -528,30 +739,8 @@ declare interface UseMessageVoteResult {
528
739
  */
529
740
  export declare const useMessaging: () => MessagingContextValue;
530
741
 
531
- export declare interface VisitorCardProps extends LockedAttachmentBaseProps {
532
- /**
533
- * Called when the visitor clicks Unlock on an unpaid attachment.
534
- * Use this to open a checkout flow. Omit to hide the Unlock button.
535
- */
536
- onUnlockClick?: () => void;
537
- /**
538
- * Called to fetch the attachment source — fired automatically when
539
- * paymentStatus transitions to 'paid', or immediately on click when
540
- * paymentStatus is already 'paid'. Return a LockedAttachmentSource to
541
- * unlock the card.
542
- */
543
- onFetchSource?: () => Promise<LockedAttachmentSource | void>;
544
- /**
545
- * Called when the visitor clicks Download on an unlocked card.
546
- * Omit to hide the Download button.
547
- */
548
- onDownloadClick?: () => void;
549
- /**
550
- * When true, shows loading dots on the Unlock button.
551
- * Driven by the LockedAttachmentContext (e.g. checkout in progress, payment processing).
552
- */
553
- isUnlocking?: boolean;
554
- }
742
+ /** @deprecated Renamed to `ReceivedCardProps`. */
743
+ export declare type VisitorCardProps = ReceivedCardProps;
555
744
 
556
745
  export declare type VoteSelection = 'up' | 'down' | null;
557
746
 
package/dist/index.js CHANGED
@@ -1,26 +1,27 @@
1
- import { b as e, c as t, C as i, d as n, e as o, f as g, F as m, g as r, L as M, M as l, h as u, i as h, j as L, k as d, l as C, m as c, n as v, r as A, o as k, u as p, p as F, q as f } from "./index-Brz9orsI.js";
1
+ import { a as e, b as t, C as n, c as i, d as o, e as g, F as m, f as M, L as r, h as l, M as h, i as u, j as L, k as c, l as d, m as C, n as v, o as A, p as k, q as p, u as F, s as f, t as q } from "./index-DfcRe-Hj.js";
2
2
  export {
3
3
  e as ActionButton,
4
4
  t as Avatar,
5
- i as ChannelEmptyState,
6
- n as ChannelList,
5
+ n as ChannelEmptyState,
6
+ i as ChannelList,
7
7
  o as ChannelView,
8
8
  g as CustomMessageProvider,
9
9
  m as FaqList,
10
- r as FaqListItem,
11
- M as LockedAttachment,
12
- l as MediaMessage,
10
+ M as FaqListItem,
11
+ r as LinkAttachment,
12
+ l as LockedAttachment,
13
+ h as MediaMessage,
13
14
  u as MessageVoteButtons,
14
- h as MessagingProvider,
15
- L as MessagingShell,
15
+ L as MessagingProvider,
16
+ c as MessagingShell,
16
17
  d as formatRelativeTime,
17
18
  C as getMessageDisplayText,
18
- c as isLinkAttachment,
19
- v as normalizeLanguageCode,
20
- A as resolveLinkAttachment,
21
- k as resolveMediaFromMessage,
22
- p as useCustomMessage,
23
- F as useMessageVote,
24
- f as useMessaging
19
+ v as isLinkAttachment,
20
+ A as normalizeLanguageCode,
21
+ k as resolveLinkAttachment,
22
+ p as resolveMediaFromMessage,
23
+ F as useCustomMessage,
24
+ f as useMessageVote,
25
+ q as useMessaging
25
26
  };
26
27
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linktr.ee/messaging-react",
3
- "version": "2.0.0",
3
+ "version": "2.0.1-rc-1778694826",
4
4
  "description": "React messaging components built on messaging-core for web applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -28,6 +28,17 @@ vi.mock('stream-chat-react', () => ({
28
28
  ),
29
29
  useMessageContext: () => ({ message: { id: 'message-1', text: 'hello' } }),
30
30
  useChannelStateContext: () => ({ channel: activeChannel }),
31
+ useChatContext: () => ({ client: { user: { id: 'visitor-1' } } }),
32
+ useTypingContext: () => ({ typing: {} }),
33
+ useAIState: () => ({ aiState: 'AI_STATE_IDLE' }),
34
+ AIStates: {
35
+ Error: 'AI_STATE_ERROR',
36
+ ExternalSources: 'AI_STATE_EXTERNAL_SOURCES',
37
+ Generating: 'AI_STATE_GENERATING',
38
+ Idle: 'AI_STATE_IDLE',
39
+ Stop: 'AI_STATE_STOP',
40
+ Thinking: 'AI_STATE_THINKING',
41
+ },
31
42
  }))
32
43
 
33
44
  vi.mock('../providers/MessagingProvider', () => ({
@@ -28,6 +28,7 @@ import { CustomMessage } from './CustomMessage'
28
28
  import { CustomMessageInput } from './CustomMessageInput'
29
29
  import { CustomSystemMessage } from './CustomSystemMessage'
30
30
  import CustomTypingIndicator from './CustomTypingIndicator'
31
+ import { DmAgentEnabledContext } from './CustomTypingIndicator/DmAgentContext'
31
32
  import { ChannelEmptyState } from './MessagingShell/ChannelEmptyState'
32
33
  import { LoadingState } from './MessagingShell/LoadingState'
33
34
 
@@ -495,38 +496,40 @@ export const ChannelView = React.memo<ChannelViewProps>(
495
496
  className
496
497
  )}
497
498
  >
498
- <Channel
499
- channel={channel}
500
- MessageSystem={CustomSystemMessage}
501
- EmptyStateIndicator={CustomChannelEmptyState}
502
- LoadingIndicator={LoadingState}
503
- DateSeparator={CustomDateSeparator}
504
- TypingIndicator={CustomTypingIndicator}
505
- doSendMessageRequest={doSendMessageRequest}
506
- {...(sendButton ? { SendButton: sendButton } : {})}
507
- >
508
- <ChannelViewInner
509
- onBack={onBack}
510
- showBackButton={showBackButton}
511
- renderMessageInputActions={renderMessageInputActions}
512
- renderConversationFooter={renderConversationFooter}
513
- onLeaveConversation={onLeaveConversation}
514
- onBlockParticipant={onBlockParticipant}
515
- CustomChannelEmptyState={CustomChannelEmptyState}
516
- showDeleteConversation={showDeleteConversation}
517
- onDeleteConversationClick={onDeleteConversationClick}
518
- onBlockParticipantClick={onBlockParticipantClick}
519
- onReportParticipantClick={onReportParticipantClick}
520
- showStarButton={showStarButton}
521
- dmAgentEnabled={dmAgentEnabled}
522
- chatbotVotingEnabled={chatbotVotingEnabled}
523
- renderChannelBanner={renderChannelBanner}
524
- customProfileContent={customProfileContent}
525
- customChannelActions={customChannelActions}
526
- renderMessage={renderMessage}
527
- viewerLanguage={viewerLanguage}
528
- />
529
- </Channel>
499
+ <DmAgentEnabledContext.Provider value={dmAgentEnabled ?? false}>
500
+ <Channel
501
+ channel={channel}
502
+ MessageSystem={CustomSystemMessage}
503
+ EmptyStateIndicator={CustomChannelEmptyState}
504
+ LoadingIndicator={LoadingState}
505
+ DateSeparator={CustomDateSeparator}
506
+ TypingIndicator={CustomTypingIndicator}
507
+ doSendMessageRequest={doSendMessageRequest}
508
+ {...(sendButton ? { SendButton: sendButton } : {})}
509
+ >
510
+ <ChannelViewInner
511
+ onBack={onBack}
512
+ showBackButton={showBackButton}
513
+ renderMessageInputActions={renderMessageInputActions}
514
+ renderConversationFooter={renderConversationFooter}
515
+ onLeaveConversation={onLeaveConversation}
516
+ onBlockParticipant={onBlockParticipant}
517
+ CustomChannelEmptyState={CustomChannelEmptyState}
518
+ showDeleteConversation={showDeleteConversation}
519
+ onDeleteConversationClick={onDeleteConversationClick}
520
+ onBlockParticipantClick={onBlockParticipantClick}
521
+ onReportParticipantClick={onReportParticipantClick}
522
+ showStarButton={showStarButton}
523
+ dmAgentEnabled={dmAgentEnabled}
524
+ chatbotVotingEnabled={chatbotVotingEnabled}
525
+ renderChannelBanner={renderChannelBanner}
526
+ customProfileContent={customProfileContent}
527
+ customChannelActions={customChannelActions}
528
+ renderMessage={renderMessage}
529
+ viewerLanguage={viewerLanguage}
530
+ />
531
+ </Channel>
532
+ </DmAgentEnabledContext.Provider>
530
533
  </div>
531
534
  )
532
535
  }
@@ -227,21 +227,20 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
227
227
  {isAttachment ? (
228
228
  <div className="str-chat__message-bubble-wrapper">
229
229
  {isMine ? (
230
- <LockedAttachment.Creator
230
+ <LockedAttachment.Sent
231
231
  title={message.metadata?.attachment_title}
232
232
  mimeType={message.metadata?.attachment_mime_type}
233
233
  thumbnailUrl={message.metadata?.attachment_thumbnail}
234
234
  amountText={message.metadata?.amount_text}
235
235
  detail={message.metadata?.attachment_detail}
236
236
  paymentStatus={message.metadata?.payment_status}
237
- isUnlocking={isUnlocking(message.id)}
238
237
  onPreviewClick={() => onUnlockClick?.(message, channel)}
239
238
  onFetchSource={async () =>
240
239
  await onFetchSource?.(message, channel)
241
240
  }
242
241
  />
243
242
  ) : (
244
- <LockedAttachment.Visitor
243
+ <LockedAttachment.Received
245
244
  title={message.metadata?.attachment_title}
246
245
  mimeType={message.metadata?.attachment_mime_type}
247
246
  thumbnailUrl={message.metadata?.attachment_thumbnail}
@@ -2,16 +2,21 @@ import type { Meta, StoryFn } from '@storybook/react'
2
2
  import React from 'react'
3
3
  import type { Event } from 'stream-chat'
4
4
  import {
5
+ AIStates,
5
6
  ChannelStateProvider,
6
7
  ChatProvider,
7
8
  TypingProvider,
8
9
  } from 'stream-chat-react'
9
10
 
11
+ import { DmAgentEnabledContext } from './DmAgentContext'
12
+
10
13
  import CustomTypingIndicator from '.'
11
14
 
12
15
  type StoryProps = {
13
16
  typingEventsEnabled?: boolean
14
17
  typing?: Record<string, Event>
18
+ aiState?: string
19
+ dmAgentEnabled?: boolean
15
20
  }
16
21
 
17
22
  const currentUser = {
@@ -33,10 +38,38 @@ const defaultTyping: Record<string, Event> = {
33
38
  } as Event,
34
39
  }
35
40
 
41
+ type ListenerMap = Record<string, (event: { ai_state: string; cid: string }) => void>
42
+
43
+ const createMockChannel = ({ aiState }: { aiState?: string }) => {
44
+ const listeners: ListenerMap = {}
45
+ const channel = {
46
+ cid: 'messaging:test',
47
+ state: {
48
+ members: {
49
+ [currentUser.id]: { user: currentUser },
50
+ [typingUser.id]: { user: typingUser },
51
+ },
52
+ },
53
+ on(eventType: string, handler: ListenerMap[string]) {
54
+ listeners[eventType] = handler
55
+ // Fire the requested AI state synchronously so the hook picks it up.
56
+ if (eventType === 'ai_indicator.update' && aiState) {
57
+ handler({ ai_state: aiState, cid: 'messaging:test' })
58
+ }
59
+ return { unsubscribe: () => {} }
60
+ },
61
+ }
62
+ return channel
63
+ }
64
+
36
65
  const StoryWrapper: React.FC<StoryProps> = ({
37
66
  typingEventsEnabled = true,
38
- typing = defaultTyping,
67
+ typing = {},
68
+ aiState,
69
+ dmAgentEnabled = true,
39
70
  }) => {
71
+ const channel = createMockChannel({ aiState })
72
+
40
73
  const chatContextValue = {
41
74
  client: {
42
75
  user: currentUser,
@@ -57,18 +90,7 @@ const StoryWrapper: React.FC<StoryProps> = ({
57
90
  }
58
91
 
59
92
  const channelStateValue = {
60
- channel: {
61
- state: {
62
- members: {
63
- [currentUser.id]: {
64
- user: currentUser,
65
- },
66
- [typingUser.id]: {
67
- user: typingUser,
68
- },
69
- },
70
- },
71
- },
93
+ channel,
72
94
  channelCapabilities: {},
73
95
  channelConfig: {
74
96
  typing_events: typingEventsEnabled,
@@ -85,9 +107,11 @@ const StoryWrapper: React.FC<StoryProps> = ({
85
107
  <ChatProvider value={chatContextValue as never}>
86
108
  <ChannelStateProvider value={channelStateValue as never}>
87
109
  <TypingProvider value={{ typing }}>
88
- <div className="relative h-20 w-[200px] bg-[#f4f4f4] p-3">
89
- <CustomTypingIndicator />
90
- </div>
110
+ <DmAgentEnabledContext.Provider value={dmAgentEnabled}>
111
+ <div className="relative h-20 w-[200px] bg-[#f4f4f4] p-3">
112
+ <CustomTypingIndicator />
113
+ </div>
114
+ </DmAgentEnabledContext.Provider>
91
115
  </TypingProvider>
92
116
  </ChannelStateProvider>
93
117
  </ChatProvider>
@@ -104,7 +128,9 @@ const meta: Meta<StoryProps> = {
104
128
  export default meta
105
129
 
106
130
  export const Default: StoryFn<StoryProps> = (args) => <StoryWrapper {...args} />
107
- Default.args = {}
131
+ Default.args = {
132
+ typing: defaultTyping,
133
+ }
108
134
 
109
135
  export const HiddenWhenNoTyping: StoryFn<StoryProps> = (args) => (
110
136
  <StoryWrapper {...args} />
@@ -112,3 +138,17 @@ export const HiddenWhenNoTyping: StoryFn<StoryProps> = (args) => (
112
138
  HiddenWhenNoTyping.args = {
113
139
  typing: {},
114
140
  }
141
+
142
+ export const AiAgentThinking: StoryFn<StoryProps> = (args) => (
143
+ <StoryWrapper {...args} />
144
+ )
145
+ AiAgentThinking.args = {
146
+ aiState: AIStates.Thinking,
147
+ }
148
+
149
+ export const AiAgentGenerating: StoryFn<StoryProps> = (args) => (
150
+ <StoryWrapper {...args} />
151
+ )
152
+ AiAgentGenerating.args = {
153
+ aiState: AIStates.Generating,
154
+ }