fix(reels): story-share icon hides with collapse, tap caption toggles, bottom-anchor video
- ReelsStoryShareButton was rendered unconditionally above the expand/ collapse group, so it stayed visible while every other action icon hid on collapse; it's now the first item inside that same animated group and sized to match the rest (28px instead of 36px). - Clicking the caption body (not just the more/less button) now toggles expand/collapse in the reels variant of ExpandableCaption. - object-contain videos/poster default to centering, so on different viewport aspect ratios the visible bottom edge of the video (and anything anchored near it) landed at a different height; added object-bottom so it's always flush with the frame's bottom edge. - Action-icon column's bottom offset is now measured off the caption block's real rendered position (ResizeObserver + getBoundingClientRect) instead of the fixed bottom-36, so the collapse toggle stays level with the caption's first line regardless of caption length. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -77,9 +77,10 @@ export default function ExpandableCaption({
|
||||
<>
|
||||
{body.trim() ? (
|
||||
<div
|
||||
onClick={isReels ? onToggle : undefined}
|
||||
className={cn(
|
||||
"text-sm font-medium leading-6",
|
||||
isReels ? "text-white/95" : "text-justify",
|
||||
isReels ? "cursor-pointer text-white/95" : "text-justify",
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -37,6 +37,10 @@ interface ReelsPostActionsProps {
|
||||
/** وقتی onCommentClick پاس داده نشه، این کامپوننت خودش CommentsModal رو مدیریت میکنه —
|
||||
* والد (ReelsPostCard) برای مخفیکردن نوار پیشرفت ویدیو حین بازبودنِ این مودال باید بدونه */
|
||||
onCommentsModalOpenChange?: (open: boolean) => void;
|
||||
/** فاصلهی px ستون اکشنها از پایینِ کارت — والد (ReelsPostCard) این رو از رویِ خطِ
|
||||
* اولِ کپشنِ واقعی اندازه میگیره تا دکمهی جمعشونده همیشه روبروش بشینه؛ نبودش
|
||||
* (postPage یا قبل از اولین اندازهگیری) یعنی مقدارِ پیشفرضِ کلاسِ bottom-36 */
|
||||
columnBottom?: number;
|
||||
}
|
||||
|
||||
function ReelsProfileFollowButton({
|
||||
@@ -118,12 +122,15 @@ function ReelsStoryShareButton({
|
||||
className="inline-flex shrink-0 items-center justify-center disabled:opacity-60"
|
||||
aria-label={t("posts.addToStory")}
|
||||
>
|
||||
{/* اندازه و رنگ دقیقاً هماندازهی بقیهی آیکونهای ستون اکشنها (reelsIconSize=md:
|
||||
w-7/28px + همون فیلترِ invert brightness-125) — قبلاً ۳۶px بود، هماندازهی
|
||||
بقیه نبود */}
|
||||
<Image
|
||||
src={staticIconUrl("/images/icons/story.png")}
|
||||
alt=""
|
||||
width={36}
|
||||
height={36}
|
||||
className="h-9 w-9 invert brightness-125 drop-shadow-lg"
|
||||
width={28}
|
||||
height={28}
|
||||
className="invert brightness-125 drop-shadow-lg"
|
||||
unoptimized
|
||||
/>
|
||||
</button>
|
||||
@@ -139,6 +146,7 @@ export default function ReelsPostActions({
|
||||
sharingStory = false,
|
||||
onShareToStory,
|
||||
onCommentsModalOpenChange,
|
||||
columnBottom,
|
||||
}: ReelsPostActionsProps) {
|
||||
const { t } = useTranslation("common");
|
||||
const { user_id, user_name } = postData;
|
||||
@@ -286,6 +294,12 @@ export default function ReelsPostActions({
|
||||
|
||||
const reelsActions = (
|
||||
<>
|
||||
{onShareToStory ? (
|
||||
<ReelsStoryShareButton
|
||||
disabled={sharingStory}
|
||||
onClick={onShareToStory}
|
||||
/>
|
||||
) : null}
|
||||
<ReelsProfileFollowButton
|
||||
userName={user_name}
|
||||
profileImage={user?.profile_image}
|
||||
@@ -407,13 +421,10 @@ export default function ReelsPostActions({
|
||||
/* عرضِ ستون ثابت (w-12، هماندازهی آواتار که پهنترین آیتمشه) — وگرنه توی
|
||||
حالتِ جمعشده که آواتار نیست، ستون بهاندازهی آیکونِ باریکتر جمع میشد و
|
||||
دکمهی جمعشونده (که items-center وسطچینش میکنه) چند پیکسل به چپ میرفت */
|
||||
<div className="absolute bottom-36 left-3 z-30 flex w-12 flex-col items-center gap-4">
|
||||
{onShareToStory ? (
|
||||
<ReelsStoryShareButton
|
||||
disabled={sharingStory}
|
||||
onClick={onShareToStory}
|
||||
/>
|
||||
) : null}
|
||||
<div
|
||||
className="absolute bottom-36 left-3 z-30 flex w-12 flex-col items-center gap-4"
|
||||
style={columnBottom != null ? { bottom: columnBottom } : undefined}
|
||||
>
|
||||
{/* دکمهی جمعشونده همیشه باید آخرین فرزندِ این ستون باشه (نه هرچیزِ دیگهای زیرش)
|
||||
— این ستون absolute + bottom-36 هست، پس لبهی پایینِ خودِ ستون همیشه ثابته،
|
||||
اما وقتی فرزندی که زیرِ دکمهی toggle قرار داره (مثلاً منوی سهنقطه) شرطی
|
||||
|
||||
@@ -120,6 +120,23 @@ export default function ReelsPostCard({
|
||||
useEffect(() => {
|
||||
setCaptionExpanded(false);
|
||||
}, [postData._id]);
|
||||
|
||||
useEffect(() => {
|
||||
const captionEl = captionRef.current;
|
||||
const containerEl = containerRef.current;
|
||||
// اگه بلوکِ کپشن الان mount نیست (کپشنِ خالی، یا chrome/کامنتها مخفیش کرده)، آخرین
|
||||
// مقدارِ اندازهگیریشده رو نگه میداریم تا با جمعکردنِ آیکونها ستون نپره
|
||||
if (!captionEl || !containerEl) return;
|
||||
const update = () => {
|
||||
const containerRect = containerEl.getBoundingClientRect();
|
||||
const captionRect = captionEl.getBoundingClientRect();
|
||||
setActionsColumnBottom(containerRect.bottom - captionRect.top);
|
||||
};
|
||||
update();
|
||||
const ro = new ResizeObserver(update);
|
||||
ro.observe(captionEl);
|
||||
return () => ro.disconnect();
|
||||
}, [captionBody, captionLinks, captionExpanded]);
|
||||
const [imageLoading, setImageLoading] = useState(() => (files?.length ?? 0) > 0);
|
||||
const [commentsOpen, setCommentsOpen] = useState(false);
|
||||
// وقتی postPage=true، کامنتها توی CommentsModal جدا (نه شیت این کامپوننت) باز میشه
|
||||
@@ -137,6 +154,15 @@ export default function ReelsPostCard({
|
||||
|
||||
const videoRef = useRef<HTMLVideoElement | null>(null);
|
||||
const containerRef = useRef<HTMLElement | null>(null);
|
||||
const captionRef = useRef<HTMLDivElement | null>(null);
|
||||
// طبق خواستهی کاربر: دکمهی جمعشوندهی ستونِ اکشنها (ReelsPostActions) باید همیشه
|
||||
// دقیقاً روبروی خطِ اولِ کپشن باشه — چون طولِ کپشن/فونت فرق میکنه، بهجای عددِ ثابتِ
|
||||
// حدسی (bottom-36)، فاصلهی واقعیِ لبهی پایینِ کارت تا لبهی بالای بلوکِ کپشن رو
|
||||
// اندازه میگیریم (هر دو نسبت به همون container، چون وقتی کامنتها بستهست بلوکِ
|
||||
// مدیا هماندازهی کاملِ container ـه)
|
||||
const [actionsColumnBottom, setActionsColumnBottom] = useState<number | null>(
|
||||
null,
|
||||
);
|
||||
const viewStartedAtRef = useRef<number | null>(null);
|
||||
const watchCompleteTrackedRef = useRef(false);
|
||||
const holdTimerRef = useRef<number | null>(null);
|
||||
@@ -461,7 +487,11 @@ export default function ReelsPostCard({
|
||||
alt=""
|
||||
fill
|
||||
sizes="100vw"
|
||||
className="object-contain"
|
||||
// object-bottom: طبق خواستهی کاربر، لبهی پایینِ محتوای ویدیو باید همیشه
|
||||
// دقیقاً روی لبهی پایینِ کادر بشینه (نه وسطچین) — تا صرفنظر از نسبتِ
|
||||
// ابعادِ صفحهنمایشِ گوشی، این خط ثابت بمونه؛ پوستر هم باید با همون
|
||||
// لنگرگاه نمایش داده بشه، وگرنه لحظهی لود شدنِ ویدیو یه پرش دیده میشه
|
||||
className="object-contain object-bottom"
|
||||
priority={isActive || preloadPriority === "nearby"}
|
||||
draggable={!allowSave ? false : undefined}
|
||||
onContextMenu={
|
||||
@@ -478,7 +508,7 @@ export default function ReelsPostCard({
|
||||
preload={videoPreload}
|
||||
controlsList="nodownload"
|
||||
className={cn(
|
||||
"h-full w-full object-contain",
|
||||
"h-full w-full object-contain object-bottom",
|
||||
videoPoster && !videoReady && "opacity-0"
|
||||
)}
|
||||
onClick={handleVideoClick}
|
||||
@@ -691,7 +721,7 @@ export default function ReelsPostCard({
|
||||
</Link>
|
||||
</div>
|
||||
{(captionBody || captionLinks) && (
|
||||
<div className="pointer-events-auto mt-2">
|
||||
<div ref={captionRef} className="pointer-events-auto mt-2">
|
||||
<ExpandableCaption
|
||||
body={captionBody}
|
||||
links={captionLinks}
|
||||
@@ -720,6 +750,7 @@ export default function ReelsPostCard({
|
||||
useCommentsSheet ? () => setCommentsOpen(true) : undefined
|
||||
}
|
||||
onCommentsModalOpenChange={setCommentsModalOpen}
|
||||
columnBottom={actionsColumnBottom ?? undefined}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user