feat(reels): user info/caption also hide on collapse; auto-scroll + speed; save-block deterrent
- Username/caption block, and the long-video progress bar, only checked
chromeHidden (the long-press hide-everything state) — not
actionsExpanded (the 3-dot-adjacent collapse toggle), so collapsing icons
never hid them. Now both also respect actionsExpanded.
- Wired playbackSpeed (via a playbackRate effect on the <video> ref, since
it isn't a JSX prop) and autoScrollEnabled (loop={!autoScrollEnabled} +
onEnded calling the new onRequestNext prop, which both reels feeds
(ExploreReelsView, PostFeedView) implement by scrollTo-ing to the next
item).
- Added onContextMenu prevention (+ draggable=false on the poster image)
when author_allow_save is false — this is a best-effort deterrent against
casual right-click/drag saving only. No browser exposes an API to block
actual OS-level screenshots; that isn't something any website can do.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -406,6 +406,13 @@ export default function ExploreReelsView({
|
||||
<ReelsPostCard
|
||||
postData={item.post}
|
||||
isActive={item.key === activeKey}
|
||||
onRequestNext={() => {
|
||||
if (!scrollRef.current || index + 1 >= items.length) return;
|
||||
scrollRef.current.scrollTo({
|
||||
top: (index + 1) * getReelsStepHeight(scrollRef.current),
|
||||
behavior: "smooth",
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<ReelsAcademyCard
|
||||
|
||||
@@ -800,6 +800,13 @@ export default function PostFeedView({
|
||||
isActive={String(post._id) === String(activePostId)}
|
||||
postPage={feedMode === "user" && showClose}
|
||||
preloadPriority={preloadPriority}
|
||||
onRequestNext={() => {
|
||||
if (!scrollRef.current || index + 1 >= displayPosts.length) return;
|
||||
scrollRef.current.scrollTo({
|
||||
top: (index + 1) * getReelsStepHeight(scrollRef.current),
|
||||
behavior: "smooth",
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</ReelsScrollSlot>
|
||||
);
|
||||
|
||||
@@ -68,6 +68,8 @@ interface ReelsPostCardProps {
|
||||
isActive?: boolean;
|
||||
postPage?: boolean;
|
||||
preloadPriority?: VideoPreloadPriority;
|
||||
/** وقتی پیمایش خودکار فعاله و این ریلز (فعال) به انتهای ویدیو رسید */
|
||||
onRequestNext?: () => void;
|
||||
}
|
||||
|
||||
export default function ReelsPostCard({
|
||||
@@ -75,6 +77,7 @@ export default function ReelsPostCard({
|
||||
isActive = false,
|
||||
postPage = false,
|
||||
preloadPriority = "idle",
|
||||
onRequestNext,
|
||||
}: ReelsPostCardProps) {
|
||||
const { t } = useTranslation("common");
|
||||
const {
|
||||
@@ -146,6 +149,11 @@ export default function ReelsPostCard({
|
||||
const social = usePostSocialState(postData);
|
||||
const requireAuth = useRequireAuth();
|
||||
const chromeHidden = exploreUi ? exploreUi.chromeHidden : uiHidden;
|
||||
// اطلاعات کاربر/کپشن هم باید با دکمهی جمعکردن آیکونها مخفی بشن، نه فقط با
|
||||
// hold روی تصویر (chromeHidden) — قبلاً این بلوک فقط chromeHidden رو چک میکرد
|
||||
const actionsExpanded = exploreUi ? exploreUi.actionsExpanded : true;
|
||||
const autoScrollEnabled = exploreUi ? exploreUi.autoScrollEnabled : false;
|
||||
const playbackSpeed = exploreUi ? exploreUi.playbackSpeed : 1;
|
||||
const allowSave = postData.author_allow_save !== false;
|
||||
|
||||
const useCommentsSheet = !postPage;
|
||||
@@ -155,7 +163,8 @@ export default function ReelsPostCard({
|
||||
videoDuration > LONG_VIDEO_SEC &&
|
||||
isActive &&
|
||||
!chromeHidden &&
|
||||
!anyCommentsOpen;
|
||||
!anyCommentsOpen &&
|
||||
actionsExpanded;
|
||||
|
||||
const mediaFiles =
|
||||
files?.map((file) => ({
|
||||
@@ -315,6 +324,12 @@ export default function ReelsPostCard({
|
||||
setVideoPaused(false);
|
||||
}, [videoSrc]);
|
||||
|
||||
// <video> عنصرِ HTML سرعتِ پخش رو بهعنوان JSX prop نمیگیره، باید مستقیم روی خودِ
|
||||
// المنت ست بشه — هم موقع تغییرِ سرعت از منو، هم موقع لود شدنِ منبعِ جدید
|
||||
useEffect(() => {
|
||||
if (videoRef.current) videoRef.current.playbackRate = playbackSpeed;
|
||||
}, [playbackSpeed, videoSrc]);
|
||||
|
||||
useEffect(() => {
|
||||
if (type !== "video" || !videoSrc) return;
|
||||
if (preloadPriority === "active" || preloadPriority === "nearby") {
|
||||
@@ -440,20 +455,30 @@ export default function ReelsPostCard({
|
||||
sizes="100vw"
|
||||
className="object-contain"
|
||||
priority={isActive || preloadPriority === "nearby"}
|
||||
draggable={!allowSave ? false : undefined}
|
||||
onContextMenu={
|
||||
!allowSave ? (e) => e.preventDefault() : undefined
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<video
|
||||
ref={videoRef}
|
||||
src={videoSrc ?? mediaFiles[0]?.src}
|
||||
poster={videoPoster}
|
||||
loop
|
||||
loop={!autoScrollEnabled}
|
||||
playsInline
|
||||
preload={videoPreload}
|
||||
controlsList="nodownload"
|
||||
className={cn(
|
||||
"h-full w-full object-contain",
|
||||
videoPoster && !videoReady && "opacity-0"
|
||||
)}
|
||||
onClick={handleVideoClick}
|
||||
// مرورگر نمیتونه از اسکرینشات واقعی جلوگیری کنه (هیچ API مرورگری برای این
|
||||
// وجود نداره)؛ این فقط راههای سادهی دانلود/ذخیره (کلیک راست، درگ) رو میبندد
|
||||
onContextMenu={
|
||||
!allowSave ? (e) => e.preventDefault() : undefined
|
||||
}
|
||||
onLoadedData={() => {
|
||||
setImageLoading(false);
|
||||
setVideoReady(true);
|
||||
@@ -469,6 +494,9 @@ export default function ReelsPostCard({
|
||||
onProgress={handleVideoTimeUpdate}
|
||||
onPlay={() => setVideoPaused(false)}
|
||||
onPause={() => setVideoPaused(true)}
|
||||
onEnded={() => {
|
||||
if (isActive && autoScrollEnabled) onRequestNext?.();
|
||||
}}
|
||||
muted={muted}
|
||||
/>
|
||||
{videoPaused && isActive && !commentsOpen && (
|
||||
@@ -578,7 +606,7 @@ export default function ReelsPostCard({
|
||||
)}
|
||||
|
||||
<AnimatePresence>
|
||||
{!chromeHidden && !anyCommentsOpen && (
|
||||
{!chromeHidden && !anyCommentsOpen && actionsExpanded && (
|
||||
<>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
|
||||
Reference in New Issue
Block a user