@linktr.ee/messaging-react 2.0.1 → 2.1.0-rc-1778695491
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-CFFNq49v.js +163 -0
- package/dist/Card-CFFNq49v.js.map +1 -0
- package/dist/Card-CsJvUF_b.js +107 -0
- package/dist/Card-CsJvUF_b.js.map +1 -0
- package/dist/Card-D32U6KfZ.js +85 -0
- package/dist/Card-D32U6KfZ.js.map +1 -0
- package/dist/Card-DlMSDSdm.js +132 -0
- package/dist/Card-DlMSDSdm.js.map +1 -0
- package/dist/Card-DlSSJPip.js +60 -0
- package/dist/Card-DlSSJPip.js.map +1 -0
- package/dist/Card-zGbhRBwv.js +48 -0
- package/dist/Card-zGbhRBwv.js.map +1 -0
- package/dist/CardThumbnail-DTBuRQHF.js +239 -0
- package/dist/CardThumbnail-DTBuRQHF.js.map +1 -0
- package/dist/LockedThumbnail-DpJx169C.js +220 -0
- package/dist/LockedThumbnail-DpJx169C.js.map +1 -0
- package/dist/{index-Bex7eg3v.js → index-DfcRe-Hj.js} +618 -607
- package/dist/index-DfcRe-Hj.js.map +1 -0
- package/dist/index.d.ts +217 -28
- package/dist/index.js +16 -15
- package/package.json +1 -1
- package/src/components/CustomMessage/index.tsx +2 -3
- package/src/components/LinkAttachment/LinkAttachment.stories.tsx +307 -0
- package/src/components/LinkAttachment/components/Composer/Card.tsx +117 -0
- package/src/components/LinkAttachment/components/Composer/index.ts +2 -0
- package/src/components/LinkAttachment/components/Received/Card.tsx +132 -0
- package/src/components/LinkAttachment/components/Received/index.ts +2 -0
- package/src/components/LinkAttachment/components/Sent/Card.tsx +57 -0
- package/src/components/LinkAttachment/components/Sent/index.ts +2 -0
- package/src/components/LinkAttachment/components/_shared/CardBody.tsx +117 -0
- package/src/components/LinkAttachment/components/_shared/CardCta.tsx +69 -0
- package/src/components/LinkAttachment/components/_shared/CardShell.tsx +120 -0
- package/src/components/LinkAttachment/components/_shared/CardThumbnail.tsx +156 -0
- package/src/components/LinkAttachment/components/_shared/normalizeExternalHref.ts +56 -0
- package/src/components/LinkAttachment/index.tsx +68 -0
- package/src/components/LinkAttachment/types.ts +69 -0
- package/src/components/LockedAttachment/LockedAttachment.stories.tsx +230 -89
- package/src/components/LockedAttachment/components/Composer/Card.tsx +221 -0
- package/src/components/LockedAttachment/components/Composer/index.ts +2 -0
- package/src/components/LockedAttachment/components/Received/Card.tsx +191 -0
- package/src/components/LockedAttachment/components/Received/CardActions.tsx +91 -0
- package/src/components/LockedAttachment/components/Received/index.ts +2 -0
- package/src/components/LockedAttachment/components/Sent/Card.tsx +177 -0
- package/src/components/LockedAttachment/components/Sent/index.ts +2 -0
- package/src/components/LockedAttachment/components/_shared/CardBody.tsx +94 -0
- package/src/components/LockedAttachment/components/_shared/GalleryThumbnail.tsx +178 -0
- package/src/components/LockedAttachment/components/_shared/LockBadge.tsx +39 -0
- package/src/components/LockedAttachment/components/_shared/LockedCardShell.tsx +36 -0
- package/src/components/LockedAttachment/components/_shared/LockedThumbnail.tsx +128 -0
- package/src/components/LockedAttachment/index.tsx +43 -12
- package/src/components/LockedAttachment/types.ts +17 -0
- package/src/components/MediaMessage/index.tsx +8 -2
- package/src/index.ts +15 -1
- package/dist/Card-BKP9ml9O.js +0 -138
- package/dist/Card-BKP9ml9O.js.map +0 -1
- package/dist/Card-Bk_4lVzP.js +0 -127
- package/dist/Card-Bk_4lVzP.js.map +0 -1
- package/dist/index-Bex7eg3v.js.map +0 -1
- package/src/components/LockedAttachment/components/Creator/Card.tsx +0 -210
- package/src/components/LockedAttachment/components/Creator/index.tsx +0 -2
- package/src/components/LockedAttachment/components/Visitor/Card.tsx +0 -155
- package/src/components/LockedAttachment/components/Visitor/CardActions.tsx +0 -62
- package/src/components/LockedAttachment/components/Visitor/LockBadge.tsx +0 -12
- 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
|
|
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
|
-
|
|
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
|
-
|
|
305
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
6
|
-
|
|
5
|
+
n as ChannelEmptyState,
|
|
6
|
+
i as ChannelList,
|
|
7
7
|
o as ChannelView,
|
|
8
8
|
g as CustomMessageProvider,
|
|
9
9
|
m as FaqList,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
l as
|
|
10
|
+
M as FaqListItem,
|
|
11
|
+
r as LinkAttachment,
|
|
12
|
+
l as LockedAttachment,
|
|
13
|
+
h as MediaMessage,
|
|
13
14
|
u as MessageVoteButtons,
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
L as MessagingProvider,
|
|
16
|
+
c as MessagingShell,
|
|
16
17
|
d as formatRelativeTime,
|
|
17
18
|
C as getMessageDisplayText,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
@@ -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.
|
|
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.
|
|
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}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArticleIcon,
|
|
3
|
+
MusicNotesIcon,
|
|
4
|
+
PlayIcon,
|
|
5
|
+
QuestionIcon,
|
|
6
|
+
} from '@phosphor-icons/react'
|
|
7
|
+
import type { Meta, StoryFn } from '@storybook/react'
|
|
8
|
+
import React from 'react'
|
|
9
|
+
|
|
10
|
+
import LinkAttachment from '.'
|
|
11
|
+
|
|
12
|
+
const IMAGE_THUMBNAIL = '/image-thumbnail.jpg'
|
|
13
|
+
const VIDEO_SOURCE = '/video-source.mp4'
|
|
14
|
+
const VIDEO_POSTER = '/video-thumbnail.jpg'
|
|
15
|
+
const AUDIO_SOURCE = '/audio-source.mp3'
|
|
16
|
+
const PDF_SOURCE = '/document-source.pdf'
|
|
17
|
+
|
|
18
|
+
const meta: Meta = {
|
|
19
|
+
title: 'LinkAttachment',
|
|
20
|
+
parameters: { layout: 'fullscreen' },
|
|
21
|
+
}
|
|
22
|
+
export default meta
|
|
23
|
+
|
|
24
|
+
const Table = ({ children }: { children: React.ReactNode }) => (
|
|
25
|
+
<div className="min-h-screen w-full bg-[#F9F7F4] p-12">
|
|
26
|
+
<table className="border-separate border-spacing-4">{children}</table>
|
|
27
|
+
</div>
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
const TableHead = ({ columns }: { columns: string[] }) => (
|
|
31
|
+
<thead>
|
|
32
|
+
<tr>
|
|
33
|
+
<th className="pb-2 text-left text-xs font-medium text-black/40" />
|
|
34
|
+
{columns.map((column) => (
|
|
35
|
+
<th
|
|
36
|
+
key={column}
|
|
37
|
+
className="pb-2 text-left text-xs font-medium text-black/40"
|
|
38
|
+
>
|
|
39
|
+
{column}
|
|
40
|
+
</th>
|
|
41
|
+
))}
|
|
42
|
+
</tr>
|
|
43
|
+
</thead>
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
const RowLabel = ({ children }: { children: React.ReactNode }) => (
|
|
47
|
+
<td className="pr-4 pt-2 text-right align-top text-xs font-medium text-black/40">
|
|
48
|
+
{children}
|
|
49
|
+
</td>
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
const APP_ICON_BASE =
|
|
53
|
+
'inline-flex size-4 items-center justify-center overflow-hidden rounded-[4px]'
|
|
54
|
+
|
|
55
|
+
const SpotifyBadge = () => (
|
|
56
|
+
<span
|
|
57
|
+
aria-hidden
|
|
58
|
+
className={`${APP_ICON_BASE} bg-[#1ed760] text-white`}
|
|
59
|
+
title="Spotify"
|
|
60
|
+
>
|
|
61
|
+
<MusicNotesIcon className="size-3" weight="fill" />
|
|
62
|
+
</span>
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
const TikTokBadge = () => (
|
|
66
|
+
<span
|
|
67
|
+
aria-hidden
|
|
68
|
+
className={`${APP_ICON_BASE} bg-[#101211] text-white`}
|
|
69
|
+
title="TikTok"
|
|
70
|
+
>
|
|
71
|
+
<PlayIcon className="size-3" weight="fill" />
|
|
72
|
+
</span>
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
const FaqBadge = () => (
|
|
76
|
+
<span
|
|
77
|
+
aria-hidden
|
|
78
|
+
className={`${APP_ICON_BASE} bg-[#061492] text-white`}
|
|
79
|
+
title="FAQ"
|
|
80
|
+
>
|
|
81
|
+
<QuestionIcon className="size-3" weight="bold" />
|
|
82
|
+
</span>
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
const FormBadge = () => (
|
|
86
|
+
<span
|
|
87
|
+
aria-hidden
|
|
88
|
+
className={`${APP_ICON_BASE} bg-[#2665d6] text-white`}
|
|
89
|
+
title="Form"
|
|
90
|
+
>
|
|
91
|
+
<ArticleIcon className="size-3" weight="fill" />
|
|
92
|
+
</span>
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
const LINK_APPS: Array<{
|
|
96
|
+
key: string
|
|
97
|
+
appIcon: React.ReactNode
|
|
98
|
+
title: string
|
|
99
|
+
description: string
|
|
100
|
+
url?: string
|
|
101
|
+
ctaLabel?: string
|
|
102
|
+
}> = [
|
|
103
|
+
{
|
|
104
|
+
key: 'spotify',
|
|
105
|
+
appIcon: <SpotifyBadge />,
|
|
106
|
+
title: 'My Playlist',
|
|
107
|
+
description: 'A handpicked workout mix I made for my clients.',
|
|
108
|
+
url: 'tr.ee/briemix',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
key: 'tiktok',
|
|
112
|
+
appIcon: <TikTokBadge />,
|
|
113
|
+
title: 'My TikTok',
|
|
114
|
+
description: 'New form-check clips every week — follow along.',
|
|
115
|
+
url: 'tr.ee/brietok',
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
key: 'faq',
|
|
119
|
+
appIcon: <FaqBadge />,
|
|
120
|
+
title: 'Brie’s FAQ',
|
|
121
|
+
description: 'Get answers on my process and what to expect.',
|
|
122
|
+
ctaLabel: 'View FAQs',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
key: 'form',
|
|
126
|
+
appIcon: <FormBadge />,
|
|
127
|
+
title: 'Fitness Assessment Fillout',
|
|
128
|
+
description: 'Share information about your journey and I’ll work on...',
|
|
129
|
+
ctaLabel: 'Complete form',
|
|
130
|
+
},
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* "Attachments" section of the Figma board — image / video / audio /
|
|
135
|
+
* file attachments shown across the three messaging states. These are
|
|
136
|
+
* pure media cards: the hero fills the card and there's no title /
|
|
137
|
+
* description / link metadata. Playable types use inline native controls:
|
|
138
|
+
* - **Image** — full-bleed thumbnail; Received opens an image preview.
|
|
139
|
+
* - **Video** — inline `<video controls>` with a poster.
|
|
140
|
+
* - **Audio** — inline `<audio controls>` over the audio type-icon.
|
|
141
|
+
* - **PDF** — type-icon placeholder; Received opens the PDF in the
|
|
142
|
+
* browser's native viewer.
|
|
143
|
+
* - **File** — generic file type-icon; Received opens a preview.
|
|
144
|
+
* - **Placeholder** — empty draft state with the image type-icon.
|
|
145
|
+
*/
|
|
146
|
+
const ATTACHMENT_ROWS: Array<{
|
|
147
|
+
label: string
|
|
148
|
+
/** Drives the placeholder type-icon and the inline player switch. */
|
|
149
|
+
mimeType?: string
|
|
150
|
+
/** Thumbnail (image source or video poster). */
|
|
151
|
+
thumbnailUrl?: string
|
|
152
|
+
/** Playable media URL — when set with a video/audio mime, renders inline. */
|
|
153
|
+
sourceUrl?: string
|
|
154
|
+
/**
|
|
155
|
+
* Override the Received `onClick` handler. Used by the PDF row to open
|
|
156
|
+
* the PDF in the browser's native viewer.
|
|
157
|
+
*/
|
|
158
|
+
onReceivedClick?: () => void
|
|
159
|
+
}> = [
|
|
160
|
+
{ label: 'Image', mimeType: 'image/jpeg', thumbnailUrl: IMAGE_THUMBNAIL },
|
|
161
|
+
{
|
|
162
|
+
label: 'Video',
|
|
163
|
+
mimeType: 'video/mp4',
|
|
164
|
+
thumbnailUrl: VIDEO_POSTER,
|
|
165
|
+
sourceUrl: VIDEO_SOURCE,
|
|
166
|
+
},
|
|
167
|
+
{ label: 'Audio', mimeType: 'audio/mpeg', sourceUrl: AUDIO_SOURCE },
|
|
168
|
+
{
|
|
169
|
+
label: 'PDF',
|
|
170
|
+
mimeType: 'application/pdf',
|
|
171
|
+
onReceivedClick: () => window.open(PDF_SOURCE, '_blank', 'noopener'),
|
|
172
|
+
},
|
|
173
|
+
{ label: 'File', mimeType: 'application/octet-stream' },
|
|
174
|
+
{ label: 'Placeholder' },
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
export const Attachments: StoryFn = () => (
|
|
178
|
+
<Table>
|
|
179
|
+
<TableHead columns={['Composer', 'Sent', 'Received']} />
|
|
180
|
+
<tbody>
|
|
181
|
+
{ATTACHMENT_ROWS.map(
|
|
182
|
+
({ label, mimeType, thumbnailUrl, sourceUrl, onReceivedClick }) => (
|
|
183
|
+
<tr key={label}>
|
|
184
|
+
<RowLabel>{label}</RowLabel>
|
|
185
|
+
<td className="align-top">
|
|
186
|
+
<LinkAttachment.Composer
|
|
187
|
+
mimeType={mimeType}
|
|
188
|
+
thumbnailUrl={thumbnailUrl}
|
|
189
|
+
sourceUrl={sourceUrl}
|
|
190
|
+
onDismiss={() => alert(`Dismissed ${label}`)}
|
|
191
|
+
/>
|
|
192
|
+
</td>
|
|
193
|
+
<td className="align-top">
|
|
194
|
+
<LinkAttachment.Sent
|
|
195
|
+
mimeType={mimeType}
|
|
196
|
+
thumbnailUrl={thumbnailUrl}
|
|
197
|
+
sourceUrl={sourceUrl}
|
|
198
|
+
/>
|
|
199
|
+
</td>
|
|
200
|
+
<td className="align-top">
|
|
201
|
+
<LinkAttachment.Received
|
|
202
|
+
mimeType={mimeType}
|
|
203
|
+
thumbnailUrl={thumbnailUrl}
|
|
204
|
+
sourceUrl={sourceUrl}
|
|
205
|
+
onClick={
|
|
206
|
+
onReceivedClick ?? (() => alert(`Open ${label} preview`))
|
|
207
|
+
}
|
|
208
|
+
/>
|
|
209
|
+
</td>
|
|
210
|
+
</tr>
|
|
211
|
+
)
|
|
212
|
+
)}
|
|
213
|
+
</tbody>
|
|
214
|
+
</Table>
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
type LinkAppLayout = 'featured' | 'classic'
|
|
218
|
+
type LinkAppState = 'Received' | 'Sent' | 'Composer'
|
|
219
|
+
|
|
220
|
+
const LINK_APP_ROWS: Array<{ layout: LinkAppLayout; state: LinkAppState }> = [
|
|
221
|
+
{ layout: 'featured', state: 'Received' },
|
|
222
|
+
{ layout: 'featured', state: 'Sent' },
|
|
223
|
+
{ layout: 'featured', state: 'Composer' },
|
|
224
|
+
{ layout: 'classic', state: 'Received' },
|
|
225
|
+
{ layout: 'classic', state: 'Sent' },
|
|
226
|
+
{ layout: 'classic', state: 'Composer' },
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
const renderLinkAppCard = (
|
|
230
|
+
layout: LinkAppLayout,
|
|
231
|
+
state: LinkAppState,
|
|
232
|
+
app: (typeof LINK_APPS)[number]
|
|
233
|
+
) => {
|
|
234
|
+
const { key, appIcon, title, description, url, ctaLabel } = app
|
|
235
|
+
const thumbnailUrl = layout === 'featured' ? IMAGE_THUMBNAIL : undefined
|
|
236
|
+
const ctaWithHandler = ctaLabel
|
|
237
|
+
? { label: ctaLabel, onClick: () => alert(`Tapped ${ctaLabel}`) }
|
|
238
|
+
: undefined
|
|
239
|
+
const ctaSilent = ctaLabel ? { label: ctaLabel } : undefined
|
|
240
|
+
|
|
241
|
+
if (state === 'Received') {
|
|
242
|
+
return (
|
|
243
|
+
<LinkAttachment.Received
|
|
244
|
+
layout={layout}
|
|
245
|
+
appIcon={appIcon}
|
|
246
|
+
title={title}
|
|
247
|
+
description={description}
|
|
248
|
+
thumbnailUrl={thumbnailUrl}
|
|
249
|
+
url={url}
|
|
250
|
+
cta={ctaWithHandler}
|
|
251
|
+
/>
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
if (state === 'Sent') {
|
|
255
|
+
return (
|
|
256
|
+
<LinkAttachment.Sent
|
|
257
|
+
layout={layout}
|
|
258
|
+
appIcon={appIcon}
|
|
259
|
+
title={title}
|
|
260
|
+
description={description}
|
|
261
|
+
thumbnailUrl={thumbnailUrl}
|
|
262
|
+
url={url}
|
|
263
|
+
cta={ctaSilent}
|
|
264
|
+
/>
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
return (
|
|
268
|
+
<LinkAttachment.Composer
|
|
269
|
+
layout={layout}
|
|
270
|
+
appIcon={appIcon}
|
|
271
|
+
title={title}
|
|
272
|
+
description={description}
|
|
273
|
+
thumbnailUrl={thumbnailUrl}
|
|
274
|
+
url={url}
|
|
275
|
+
cta={ctaSilent}
|
|
276
|
+
onDismiss={() => alert(`Dismissed ${key}`)}
|
|
277
|
+
/>
|
|
278
|
+
)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* "LinkApps" section of the Figma board — link previews with a brand
|
|
283
|
+
* badge prefixing the title and either a URL footer (Spotify, TikTok) or
|
|
284
|
+
* a CTA button (FAQ, Form). Each app is shown in both the **Featured**
|
|
285
|
+
* (hero image) and **Classic** (compact, no hero) layouts across all
|
|
286
|
+
* three messaging states.
|
|
287
|
+
*/
|
|
288
|
+
export const LinkApps: StoryFn = () => (
|
|
289
|
+
<Table>
|
|
290
|
+
<TableHead columns={LINK_APPS.map(({ key }) => key)} />
|
|
291
|
+
<tbody>
|
|
292
|
+
{LINK_APP_ROWS.map(({ layout, state }) => {
|
|
293
|
+
const label = `${layout === 'featured' ? 'Featured' : 'Classic'} (${state})`
|
|
294
|
+
return (
|
|
295
|
+
<tr key={label}>
|
|
296
|
+
<RowLabel>{label}</RowLabel>
|
|
297
|
+
{LINK_APPS.map((app) => (
|
|
298
|
+
<td key={app.key} className="align-top">
|
|
299
|
+
{renderLinkAppCard(layout, state, app)}
|
|
300
|
+
</td>
|
|
301
|
+
))}
|
|
302
|
+
</tr>
|
|
303
|
+
)
|
|
304
|
+
})}
|
|
305
|
+
</tbody>
|
|
306
|
+
</Table>
|
|
307
|
+
)
|