diff --git a/src/components/posts/ExpandableCaption.tsx b/src/components/posts/ExpandableCaption.tsx index e64280e..6a41c82 100644 --- a/src/components/posts/ExpandableCaption.tsx +++ b/src/components/posts/ExpandableCaption.tsx @@ -77,9 +77,10 @@ export default function ExpandableCaption({ <> {body.trim() ? (
diff --git a/src/components/posts/ReelsPostActions.tsx b/src/components/posts/ReelsPostActions.tsx index 6d7ca9f..e63868f 100644 --- a/src/components/posts/ReelsPostActions.tsx +++ b/src/components/posts/ReelsPostActions.tsx @@ -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 بود، هم‌اندازه‌ی + بقیه نبود */} @@ -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 ? ( + + ) : null} - {onShareToStory ? ( - - ) : null} +
{/* دکمه‌ی جمع‌شونده همیشه باید آخرین فرزندِ این ستون باشه (نه هرچیزِ دیگه‌ای زیرش) — این ستون absolute + bottom-36 هست، پس لبه‌ی پایینِ خودِ ستون همیشه ثابته، اما وقتی فرزندی که زیرِ دکمه‌ی toggle قرار داره (مثلاً منوی سه‌نقطه) شرطی diff --git a/src/components/posts/ReelsPostCard.tsx b/src/components/posts/ReelsPostCard.tsx index ceedc38..d0468b5 100644 --- a/src/components/posts/ReelsPostCard.tsx +++ b/src/components/posts/ReelsPostCard.tsx @@ -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(null); const containerRef = useRef(null); + const captionRef = useRef(null); + // طبق خواسته‌ی کاربر: دکمه‌ی جمع‌شونده‌ی ستونِ اکشن‌ها (ReelsPostActions) باید همیشه + // دقیقاً روبروی خطِ اولِ کپشن باشه — چون طولِ کپشن/فونت فرق می‌کنه، به‌جای عددِ ثابتِ + // حدسی (bottom-36)، فاصله‌ی واقعیِ لبه‌ی پایینِ کارت تا لبه‌ی بالای بلوکِ کپشن رو + // اندازه می‌گیریم (هر دو نسبت به همون container، چون وقتی کامنت‌ها بسته‌ست بلوکِ + // مدیا هم‌اندازه‌ی کاملِ container ـه) + const [actionsColumnBottom, setActionsColumnBottom] = useState( + null, + ); const viewStartedAtRef = useRef(null); const watchCompleteTrackedRef = useRef(false); const holdTimerRef = useRef(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({
{(captionBody || captionLinks) && ( -
+
setCommentsOpen(true) : undefined } onCommentsModalOpenChange={setCommentsModalOpen} + columnBottom={actionsColumnBottom ?? undefined} /> )}