Skip to content

fix(shared): polish X tweet preview rendering and modal media behavior#5674

Open
tomeredlich wants to merge 5 commits intomainfrom
feat/social-twitter-preview-polish-clean
Open

fix(shared): polish X tweet preview rendering and modal media behavior#5674
tomeredlich wants to merge 5 commits intomainfrom
feat/social-twitter-preview-polish-clean

Conversation

@tomeredlich
Copy link
Contributor

@tomeredlich tomeredlich commented Mar 8, 2026

Summary

  • improve X/tweet preview consistency across feed, share, and post modal surfaces (single preview, standalone tweet rendering, and modal CTA copy updates)
  • add language-aware text direction handling for tweet content with LTR identity enforcement, plus hydration hardening for dynamic direction attrs
  • prevent placeholder images from rendering as tweet media and align modal embedded tweet media sizing with post modal preview rules
  • expand test coverage for embedded tweet rendering and social-twitter helper behaviors

Test plan

  • pnpm --filter shared exec jest "src/components/cards/socialTwitter/EmbeddedTweetPreview.spec.tsx" "src/components/cards/socialTwitter/SocialTwitterGrid.spec.tsx" "src/components/cards/socialTwitter/SocialTwitterList.spec.tsx" "src/components/cards/socialTwitter/socialTwitterHelpers.spec.tsx"

Made with Cursor

Preview domain

https://feat-social-twitter-preview-poli.preview.app.daily.dev

Refine social/X share rendering across cards and write flow by reducing duplicate preview content, handling hydration safely, and applying language-aware RTL direction with auto fallback for tweet text.

Made-with: Cursor
Show tweet-first layouts for standalone X posts, hide placeholder cover media in modals, and standardize X metadata/read-action copy for clearer, consistent presentation.

Made-with: Cursor
Ensure tweet media in post modals uses the same sizing rules as modal previews and broaden X preview detection for share links. This also adds regression coverage for embedded tweet media/identity rendering behavior.

Made-with: Cursor
… label

Update helper assertions to match current handle dedupe behavior and fixed `From x.com` label rendering so the focused social-twitter suite passes on main.

Made-with: Cursor
@vercel
Copy link

vercel bot commented Mar 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
daily-webapp Ready Ready Preview Mar 8, 2026 8:57am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
storybook Ignored Ignored Mar 8, 2026 8:57am

Request Review

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@tomeredlich
Copy link
Contributor Author

@claude check pr

@claude
Copy link
Contributor

claude bot commented Mar 8, 2026

Claude finished @tomeredlich's task in 2m 26s —— View job


PR Review

  • Gather context
  • Read changed files
  • Analyze code quality and correctness
  • Post review feedback

Good overall direction — unifying the embedded tweet preview surface, adding RTL support, and hardening hydration. A few issues worth fixing before merge:


Bugs

1. Unreachable code in getReadPostButtonTextpackages/shared/src/graphql/posts.ts:130-136

isXShareLikePost already returns true when post.type === PostType.SocialTwitter, so the subsequent isSocialTwitterPost branch is dead:

if (isXShareLikePost(post)) { return 'View on X'; }  // catches SocialTwitter too
if (isSocialTwitterPost(post)) { return 'View on X'; } // unreachable

Fix this →


2. Duplicate import in SocialTwitterPostContent.tsx — lines 25-26

import { cloudinaryPostImageCoverPlaceholder } from '../../lib/image';
import { isPlaceholderImage } from '../../lib/image';

These should be a single import statement.

Fix this →


3. titleHtml dropped in EmbeddedTweetPreview.tsx

The old code rendered post.sharedPost?.titleHtml via dangerouslySetInnerHTML for rich-text content (links, hashtags). The new code uses plain text only (post.sharedPost?.title || post.title). If tweets contain HTML-formatted content this will show raw unformatted text. Worth confirming whether titleHtml on embedded tweet posts is ever populated — if so, this is a regression.


Code Quality

4. Duplicated regex patterns across 3 files

The title-parsing regex /^(.*?)\s+\(@([^)]+)\):\s*(.+)$/s appears identically in:

  • WriteLinkPreview.tsx
  • SharePostContent.tsx
  • SocialTwitterPostContent.tsx

The repostPrefixPattern /^.*?reposted on x\.\s*/i is duplicated between SocialTwitterGrid.tsx and SocialTwitterList.tsx.

Both should be extracted to socialTwitterHelpers.tsx per the repo's code-duplication policy. Fix this →%3A%5Cs*(.%2B)%24%2Fs%20into%20a%20named%20export%20in%20socialTwitterHelpers.tsx%20and%20replace%20all%20three%20usages%20in%20WriteLinkPreview.tsx%2C%20SharePostContent.tsx%2C%20and%20SocialTwitterPostContent.tsx.%20Also%20extract%20repostPrefixPattern%20which%20is%20duplicated%20in%20SocialTwitterGrid.tsx%20and%20SocialTwitterList.tsx.)


5. readButtonText set redundantly in SocialTwitterPostContent.tsx

contentProps (line ~79) already sets readButtonText: 'Read on X' for BasePostContent, and PostSourceInfo at line ~185 also receives readButtonText="Read on X" as a direct prop. One of these is from the object spread, the other is an explicit override — verify the contentProps path actually flows through to PostHeaderActions to avoid maintaining two call sites for the same value.


6. Handle deduplication behavior change — socialTwitterHelpers.tsx:78-81

getUniqueHandles deduped case-insensitively (e.g. anthropicai and AnthropicAI → one entry). The replacement uses new Set() which is case-sensitive. The test description was updated to match the new behavior, but this is a functional change: the same Twitter account with inconsistent casing in source data will now appear twice in metadataHandles. If this is intentional it should be noted; if not, the old logic was correct.


7. metadata.bottomLabel always overridden in SocialTwitterList.tsx:153

The metadata memo conditionally sets bottomLabel: authorName when enableSourceHeader is true, but then the PostCardHeader call always spreads bottomLabel: metadataBottomLabel on top:

metadata={{
  ...metadata,
  dateFirst: true,
  bottomLabel: metadataBottomLabel,  // always overrides memo's bottomLabel
}}

This means enableSourceHeader's bottomLabel: authorName path is silently ignored. Intentional?


8. Hydration flash in WriteLinkPreview / SubmitExternalLink

The isHydrated guard means users see a minimal (URL-field only) preview that then jumps to the full tweet card after client hydration. This causes a visible layout shift. Consider whether a stable SSR-safe skeleton that matches the tweet preview dimensions would be preferable.


Minor

9. Media not shown for standalone tweets in grid (SocialTwitterGrid.tsx:214-221)

EmbeddedTweetPreview is always rendered but showMedia is never passed (defaults to false). Previously standalone tweets displayed their image via CardImage. Now media is never shown for any post in the grid view. Confirm this is the intended UX.

10. Type cast preview as unknown as Post in WriteLinkPreview.tsx

The double cast for ExternalLinkPreview → Post is a code smell. If ExternalLinkPreview satisfies enough of the Post shape to work here, it's worth documenting why with a comment, or extending the EmbeddedTweetPreview props to accept a looser type.


| Branch

Resolve lint_shared failures and clean up review feedback by removing unreachable read-button logic, consolidating duplicate imports, restoring case-insensitive handle deduping, extracting shared X-title/repost regex helpers, and preserving tweet titleHtml rendering in embedded previews.

Made-with: Cursor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants