From 53dc813ecb1df740bb2168ff788b8fbeba1884ac Mon Sep 17 00:00:00 2001 From: Taylor Ho Date: Thu, 30 Jul 2026 17:01:36 -0700 Subject: [PATCH] feat(desktop): simplify rich preview expansion - Replace separate description and image controls with one Show more/Show less toggle that collapses both elements. - Expand rich preview text to the full available metadata and widen preview containers for readable 280-character tweets. - Add vertical padding beside the accent border and cap preview images at 300px while preserving responsive sizing. Co-authored-by: Taylor Ho Signed-off-by: Taylor Ho --- .../ui/rich-link-preview-attachment.tsx | 240 +++++++----------- 1 file changed, 97 insertions(+), 143 deletions(-) diff --git a/desktop/src/shared/ui/rich-link-preview-attachment.tsx b/desktop/src/shared/ui/rich-link-preview-attachment.tsx index 349fb540a..692129050 100644 --- a/desktop/src/shared/ui/rich-link-preview-attachment.tsx +++ b/desktop/src/shared/ui/rich-link-preview-attachment.tsx @@ -1,5 +1,5 @@ import { ChevronDown, ChevronUp, X } from "lucide-react"; -import { useEffect, useRef, useState } from "react"; +import { useState } from "react"; import type { ResolvedLinkPreview } from "@/shared/lib/useResolvedLinkPreviews"; import { cn } from "@/shared/lib/cn"; @@ -49,38 +49,16 @@ function TweetPreview({ onRemove?: () => void; preview: ResolvedLinkPreview; }) { - const [descriptionExpanded, setDescriptionExpanded] = useState(false); - const [descriptionOverflows, setDescriptionOverflows] = useState(false); - const [imageExpanded, setImageExpanded] = useState(true); - const descriptionRef = useRef(null); + const [contentExpanded, setContentExpanded] = useState(true); const reserveImage = preview.imageState !== "none"; const showImage = preview.imageState === "image"; + const hasExpandableContent = Boolean(preview.description) || reserveImage; const hostname = getHostname(preview); - useEffect(() => { - if (!preview.description) return; - - const description = descriptionRef.current; - if (!description || descriptionExpanded) return; - - const measure = () => { - setDescriptionOverflows( - description.scrollHeight > description.clientHeight + 1, - ); - }; - const frame = requestAnimationFrame(measure); - const observer = new ResizeObserver(measure); - observer.observe(description); - return () => { - cancelAnimationFrame(frame); - observer.disconnect(); - }; - }, [descriptionExpanded, preview.description]); - return (
{preview.title} - {preview.description ? ( - <> -
- {preview.description} -
- {descriptionOverflows ? ( - - ) : null} - - ) : null} - {reserveImage ? ( -
- {imageExpanded ? ( - -
- {showImage ? ( - {`Preview - ) : ( -
- )} -
-
- ) : null} - + {contentExpanded && preview.description ? ( +
+ {preview.description}
) : null} + {contentExpanded && reserveImage ? ( + +
+ {showImage ? ( + {`Preview + ) : ( +
+ )} +
+
+ ) : null} + {hasExpandableContent ? ( + + ) : null} {onRemove ? ( -
+
+ + ) : null} + {hasExpandableContent ? ( + ) : null} {onRemove ? (