@linktr.ee/messaging-react 3.18.0-rc-1785370958 → 3.18.0

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/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-BiPvv2eN.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.MessageBubble=e.MessageBubble;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.optimizeMessagingAttachmentUrl=e.optimizeMessagingAttachmentUrl;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-BQ4mQDo2.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.MessageBubble=e.MessageBubble;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.optimizeMessagingAttachmentUrl=e.optimizeMessagingAttachmentUrl;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.d.ts CHANGED
@@ -697,6 +697,11 @@ export declare interface LinkAttachmentBaseProps {
697
697
  * text bars and ignores `accentColor` (there is no image yet). `'failed'`
698
698
  * renders the fallback chin with whatever text is available. Defaults to
699
699
  * `'ready'`.
700
+ *
701
+ * `'loading'` outranks the hero shape that `mimeType` + `sourceUrl` would
702
+ * otherwise select: a card mid-scrape does not yet know what the asset is, so
703
+ * it renders the skeleton rather than a live `<audio>` / `<video>` player
704
+ * beside the skeleton's own text bars.
700
705
  */
701
706
  status?: LinkAttachmentStatus;
702
707
  }
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 p, m as d, n as v, p as C, q as A, s as k, t as F, u as b, v as P, w as f, x, o as y, y as z, z as B, B as S, D as q, E as D, G as E, H as V, I as w } from "./index-N1OOJmAH.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 p, m as d, n as v, p as C, q as A, s as k, t as F, u as b, v as P, w as f, x, o as y, y as z, z as B, B as S, D as q, E as D, G as E, H as V, I as w } from "./index-CsPg1TYf.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.18.0-rc-1785370958",
3
+ "version": "3.18.0",
4
4
  "description": "React messaging components built on messaging-core for web applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@linktr.ee/component-library": "11.8.6",
53
- "@linktr.ee/messaging-core": "2.3.0-rc-1785370958",
53
+ "@linktr.ee/messaging-core": "^2.2.0",
54
54
  "@phosphor-icons/react": "^2.1.10"
55
55
  },
56
56
  "devDependencies": {
@@ -261,6 +261,41 @@ describe('LinkAttachment accent chin (MES-1349)', () => {
261
261
  expect(screen.getByText(CARD_PROPS.title)).toHaveStyle({ color: '#040403' })
262
262
  })
263
263
 
264
+ it('lets a loading status outrank the audio hero on all three cards', () => {
265
+ // `status` and `mimeType`/`sourceUrl` are independent public props, and a
266
+ // caller can set both. Before this, `CardThumbnail`'s audio fast path ran
267
+ // ahead of the loading branch, so Sent/Received mounted a live `<audio>`
268
+ // player beside `CardBody`'s skeleton bars and Composer's own audio path
269
+ // dropped the skeleton and its `aria-busy` region altogether.
270
+ const LOADING_AUDIO = {
271
+ mimeType: 'audio/mpeg',
272
+ sourceUrl: 'https://cdn.example.com/clip.mp3',
273
+ status: 'loading' as const,
274
+ }
275
+
276
+ for (const card of [
277
+ <LinkAttachment.Sent key="s" {...LOADING_AUDIO} />,
278
+ <LinkAttachment.Received key="r" {...LOADING_AUDIO} />,
279
+ <LinkAttachment.Composer
280
+ key="c"
281
+ {...LOADING_AUDIO}
282
+ onDismiss={() => {}}
283
+ />,
284
+ ]) {
285
+ const { container, unmount } = renderWithProviders(card)
286
+
287
+ expect(container.querySelector('audio')).toBeNull()
288
+ const busy = screen.getByLabelText('Loading link preview')
289
+ expect(busy).toHaveAttribute('aria-busy', 'true')
290
+ // The skeleton keeps its 250px box: the audio shell is intrinsic-height,
291
+ // and a `flex-1` hero inside it would collapse to zero.
292
+ expect(busy.className).toContain('h-[250px]')
293
+ expect(container.querySelectorAll('.animate-pulse')).toHaveLength(3)
294
+
295
+ unmount()
296
+ }
297
+ })
298
+
264
299
  it('paints the bubble tail in the accent for the last card in a run only', async () => {
265
300
  const { unmount } = renderWithProviders(
266
301
  <LinkAttachment.Sent
@@ -104,8 +104,11 @@ const ComposerCard: React.FC<ComposerCardProps> = ({
104
104
 
105
105
  // Audio cards collapse to just the native player — render the dismiss
106
106
  // button as an inline sibling so it always has its own space and never
107
- // overlaps the audio control's volume/menu buttons.
108
- if (isAudio) {
107
+ // overlaps the audio control's volume/menu buttons. Skipped while loading:
108
+ // this path has no `CardBody` and no `busy`, so taking it during a scrape
109
+ // would drop the skeleton and its `aria-busy` region entirely, and we do not
110
+ // yet know the asset is audio anyway.
111
+ if (isAudio && !chin.isLoading) {
109
112
  return (
110
113
  <CardShell variant="dark" bgClassName={AUDIO_BG_CLASS}>
111
114
  <div className="flex items-center gap-2 pr-3">
@@ -73,9 +73,13 @@ const ReceivedCard: React.FC<ReceivedCardProps> = ({
73
73
  ? normalizedUrl
74
74
  : undefined
75
75
  const shellOnClick = cta == null && !isPlayingMedia ? onClick : undefined
76
- const audioBg = isPlayableAudio(mimeType, sourceUrl)
77
- ? AUDIO_BG_CLASS
78
- : undefined
76
+ // A loading card is not yet an audio card: the scrape that would tell us has
77
+ // not returned, so it keeps the ordinary 250px skeleton rather than the
78
+ // intrinsic-height audio shell (whose `flex-1` hero would collapse to 0).
79
+ const audioBg =
80
+ status !== 'loading' && isPlayableAudio(mimeType, sourceUrl)
81
+ ? AUDIO_BG_CLASS
82
+ : undefined
79
83
 
80
84
  // When a CTA is set the shell isn't interactive — the CTA owns the
81
85
  // click target. Forward the card-level `onClick` to the CTA so
@@ -76,7 +76,10 @@ const SentCard: React.FC<SentCardProps> = ({
76
76
  const shellOnClick =
77
77
  cta == null && !isPlayingMedia && shellHref != null ? onClick : undefined
78
78
 
79
- const isAudio = isPlayableAudio(mimeType, sourceUrl)
79
+ // A loading card is not yet an audio card: the scrape that would tell us has
80
+ // not returned, so it keeps the ordinary 250px skeleton rather than the
81
+ // intrinsic-height audio shell (whose `flex-1` hero would collapse to 0).
82
+ const isAudio = status !== 'loading' && isPlayableAudio(mimeType, sourceUrl)
80
83
  const chin = useChinPalette({
81
84
  variant: 'dark',
82
85
  accentColor,
@@ -95,7 +95,11 @@ const CardThumbnail: React.FC<CardThumbnailProps> = ({
95
95
  const isPlayableVideo = !!sourceUrl && sourceType === 'video'
96
96
  const optimizedThumbnailUrl = optimizeMessagingAttachmentUrl(thumbnailUrl)
97
97
 
98
- if (isPlayableAudio(mimeType, sourceUrl)) {
98
+ // `!isLoading` first, deliberately: while the scrape is in flight we do not
99
+ // yet know what the asset is, so honouring a `mimeType` the caller happens to
100
+ // have set already would mount a live player next to `CardBody`'s skeleton
101
+ // bars. The loading branch below owns every hero shape (MES-1349).
102
+ if (!isLoading && isPlayableAudio(mimeType, sourceUrl)) {
99
103
  // Audio collapses the hero entirely — the native player sits inside
100
104
  // the card chrome with a bit of padding so the card background
101
105
  // (typically `bg-[#F2F3F4]`) is visible around it.
@@ -95,6 +95,11 @@ export interface LinkAttachmentBaseProps {
95
95
  * text bars and ignores `accentColor` (there is no image yet). `'failed'`
96
96
  * renders the fallback chin with whatever text is available. Defaults to
97
97
  * `'ready'`.
98
+ *
99
+ * `'loading'` outranks the hero shape that `mimeType` + `sourceUrl` would
100
+ * otherwise select: a card mid-scrape does not yet know what the asset is, so
101
+ * it renders the skeleton rather than a live `<audio>` / `<video>` player
102
+ * beside the skeleton's own text bars.
98
103
  */
99
104
  status?: LinkAttachmentStatus
100
105
  }