From a16639be84f5fdbe764accd66441e0657e133226 Mon Sep 17 00:00:00 2001 From: IrohDW Date: Wed, 7 Aug 2024 12:17:08 -0600 Subject: [PATCH] Updates to User Interface Layout of VideoContent.tsx and PlaylistContent.tsx updated, so it will feel more familiar to users. Video Description boxes will not show the expand/contract button if the contracted description shows all text. The height of the contracted description is doubled. Deleted Video Image no longer appears in VideoContent.tsx until after the video is loaded. A new hotkey 'o' on the Video Player lets users switch between maintaining aspect ratio or filling all available space Description and file downloader don't appear if video is deleted In PlaylistContent.tsx and VideoContent.tsx, clicking anywhere other than the description or commentEditors will focus the video so that hotkeys will work on it --- .../PublishVideo/PublishVideo-styles.tsx | 3 +- .../common/Comments/CommentSection.tsx | 18 +- .../common/Comments/Comments-styles.tsx | 8 +- .../common/Notifications/Notifications.tsx | 20 +- .../common/SuperLikesList/Comments-styles.tsx | 6 +- .../SuperLikesList/SuperLikesSection.tsx | 24 +- .../common/VideoPlayer/VideoPlayer.tsx | 23 +- .../PlaylistContent-styles.tsx | 1 - .../PlaylistContent/PlaylistContent.tsx | 221 +++--- .../VideoContent/VideoContent-functions.ts | 56 ++ .../VideoContent/VideoContent-styles.tsx | 11 +- .../VideoContent/VideoContent.tsx | 627 ++++++++---------- src/state/features/persistSlice.ts | 1 + src/wrappers/GlobalWrapper.tsx | 15 +- 14 files changed, 533 insertions(+), 501 deletions(-) create mode 100644 src/pages/ContentPages/VideoContent/VideoContent-functions.ts diff --git a/src/components/Publish/PublishVideo/PublishVideo-styles.tsx b/src/components/Publish/PublishVideo/PublishVideo-styles.tsx index bf3d011..ed76029 100644 --- a/src/components/Publish/PublishVideo/PublishVideo-styles.tsx +++ b/src/components/Publish/PublishVideo/PublishVideo-styles.tsx @@ -175,8 +175,7 @@ export const CrowdfundTitle = styled(Typography)(({ theme }) => ({ export const CrowdfundSubTitleRow = styled(Box)({ display: "flex", alignItems: "center", - justifyContent: "center", - width: "100%", + justifyContent: "start", flexDirection: "row", }); diff --git a/src/components/common/Comments/CommentSection.tsx b/src/components/common/Comments/CommentSection.tsx index 99f3fb0..e49dd83 100644 --- a/src/components/common/Comments/CommentSection.tsx +++ b/src/components/common/Comments/CommentSection.tsx @@ -28,8 +28,8 @@ interface CommentSectionProps { const Panel = styled("div")` display: flex; flex-direction: column; - justify-content: center; - align-items: center; + justify-content: start; + align-items: start; width: 100%; padding-bottom: 10px; height: 100%; @@ -57,7 +57,7 @@ export const CommentSection = ({ postId, postName }: CommentSectionProps) => { const { user } = useSelector((state: RootState) => state.auth); const [newMessages, setNewMessages] = useState(0); const [loadingComments, setLoadingComments] = useState(false); - + // console.log("postId is: ", postId, " postName is: ", postName); const onSubmit = (obj?: any, isEdit?: boolean) => { if (isEdit) { setListComments((prev: any[]) => { @@ -113,6 +113,7 @@ export const CommentSection = ({ postId, postName }: CommentSectionProps) => { )}_reply_${removeBaseCommentId.slice( -6 )}&limit=0&includemetadata=false&offset=${offset}&reverse=false&excludeblocked=true`; + const response = await fetch(url, { method: "GET", headers: { @@ -120,6 +121,7 @@ export const CommentSection = ({ postId, postName }: CommentSectionProps) => { }, }); const responseData = await response.json(); + const comments: any[] = []; for (const comment of responseData) { if (comment.identifier && comment.name) { @@ -163,6 +165,9 @@ export const CommentSection = ({ postId, postName }: CommentSectionProps) => { }, }); const responseData = await response.json(); + // console.log("url is: ", url); + // console.log("response is: ", responseData); + let comments: any[] = []; for (const comment of responseData) { if (comment.identifier && comment.name) { @@ -222,18 +227,13 @@ export const CommentSection = ({ postId, postName }: CommentSectionProps) => { return ( <> - - Comments - {loadingComments ? ( ) : listComments.length === 0 ? ( - - There are no comments yet. Be the first to comment! - + <> ) : ( {structuredCommentList.map((comment: any) => { diff --git a/src/components/common/Comments/Comments-styles.tsx b/src/components/common/Comments/Comments-styles.tsx index e215c9d..9cda045 100644 --- a/src/components/common/Comments/Comments-styles.tsx +++ b/src/components/common/Comments/Comments-styles.tsx @@ -93,7 +93,7 @@ export const StyledCardComment = styled(Typography)(({ theme }) => ({ fontWeight: 400, color: theme.palette.text.primary, fontSize: "19px", - wordBreak: "break-word" + wordBreak: "break-word", })); export const TitleText = styled(Typography)({ @@ -159,7 +159,7 @@ export const BlockIconContainer = styled(Box)({ }); export const CommentsContainer = styled(Box)({ - width: "90%", + width: "70%", maxWidth: "1000px", display: "flex", flexDirection: "column", @@ -180,7 +180,7 @@ export const CommentContainer = styled(Box)({ export const NoCommentsRow = styled(Box)({ display: "flex", alignItems: "center", - justifyContent: "center", + justifyContent: "start", flex: "1", padding: "10px 0px", fontFamily: "Mulish", @@ -218,7 +218,7 @@ export const CommentActionButtonRow = styled(Box)({ export const CommentEditorContainer = styled(Box)({ width: "100%", display: "flex", - justifyContent: "center", + justifyContent: "start", }); export const CommentDateText = styled(Typography)(({ theme }) => ({ diff --git a/src/components/common/Notifications/Notifications.tsx b/src/components/common/Notifications/Notifications.tsx index 2fc29c0..f964a55 100644 --- a/src/components/common/Notifications/Notifications.tsx +++ b/src/components/common/Notifications/Notifications.tsx @@ -16,15 +16,15 @@ import React, { useState, } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { RootState } from "../../../state/store"; -import NotificationsIcon from "@mui/icons-material/Notifications"; -import { formatDate } from "../../../utils/time"; -import ThumbUpIcon from "@mui/icons-material/ThumbUp"; import { extractSigValue, getPaymentInfo, isTimestampWithinRange, -} from "../../../pages/ContentPages/VideoContent/VideoContent"; +} from "../../../pages/ContentPages/VideoContent/VideoContent-functions.ts"; +import { RootState } from "../../../state/store"; +import NotificationsIcon from "@mui/icons-material/Notifications"; +import { formatDate } from "../../../utils/time"; +import ThumbUpIcon from "@mui/icons-material/ThumbUp"; import { useNavigate } from "react-router-dom"; import localForage from "localforage"; import moment from "moment"; @@ -148,7 +148,7 @@ export const Notifications = () => { ) { let urlReference = null; try { - let idForUrl = extractIdValue(comment?.metadata?.description); + const idForUrl = extractIdValue(comment?.metadata?.description); const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&identifier=${idForUrl}&limit=1&includemetadata=false&reverse=false&excludeblocked=true&offset=0&name=${username}`; const response2 = await fetch(url, { method: "GET", @@ -160,7 +160,9 @@ export const Notifications = () => { if (responseSearch.length > 0) { urlReference = responseSearch[0]; } - } catch (error) {} + } catch (error) { + console.log(error); + } // const url = `/arbitrary/BLOG_COMMENT/${comment.name}/${comment.identifier}`; // const response = await fetch(url, { // method: "GET", @@ -180,7 +182,9 @@ export const Notifications = () => { }, ]; } - } catch (error) {} + } catch (error) { + console.log(error); + } } } setNotifications(prev => { diff --git a/src/components/common/SuperLikesList/Comments-styles.tsx b/src/components/common/SuperLikesList/Comments-styles.tsx index 0f544df..333221a 100644 --- a/src/components/common/SuperLikesList/Comments-styles.tsx +++ b/src/components/common/SuperLikesList/Comments-styles.tsx @@ -93,7 +93,7 @@ export const StyledCardComment = styled(Typography)(({ theme }) => ({ fontWeight: 400, color: theme.palette.text.primary, fontSize: "19px", - wordBreak: "break-word" + wordBreak: "break-word", })); export const TitleText = styled(Typography)({ @@ -159,7 +159,7 @@ export const BlockIconContainer = styled(Box)({ }); export const CommentsContainer = styled(Box)({ - width: "90%", + width: "70%", maxWidth: "1000px", display: "flex", flexDirection: "column", @@ -180,7 +180,7 @@ export const CommentContainer = styled(Box)({ export const NoCommentsRow = styled(Box)({ display: "flex", alignItems: "center", - justifyContent: "center", + justifyContent: "start", flex: "1", padding: "10px 0px", fontFamily: "Mulish", diff --git a/src/components/common/SuperLikesList/SuperLikesSection.tsx b/src/components/common/SuperLikesList/SuperLikesSection.tsx index fb5d6de..4ae547e 100644 --- a/src/components/common/SuperLikesList/SuperLikesSection.tsx +++ b/src/components/common/SuperLikesList/SuperLikesSection.tsx @@ -24,15 +24,15 @@ interface CommentSectionProps { postId: string; postName: string; superlikes: any[]; - getMore: () => void; + getMore?: () => void; loadingSuperLikes: boolean; } const Panel = styled("div")` display: flex; flex-direction: column; - justify-content: center; - align-items: center; + justify-content: start; + align-items: start; width: 100%; padding-bottom: 10px; height: 100%; @@ -199,30 +199,18 @@ export const SuperLikesSection = ({ return ( <> - - - Super Likes - - {loadingComments || loadingSuperLikes ? ( ) : listComments.length === 0 ? ( - - There are no super likes yet. Be the first! - + <> ) : ( {structuredCommentList.map((comment: any) => { let hasHash = false; - let message = { ...comment }; + const message = { ...comment }; let hash = {}; if (hashMapSuperlikes[comment?.identifier]) { message.message = @@ -249,7 +237,7 @@ export const SuperLikesSection = ({ { - getMore(); + if (getMore) getMore(); }} variant="contained" size="small" diff --git a/src/components/common/VideoPlayer/VideoPlayer.tsx b/src/components/common/VideoPlayer/VideoPlayer.tsx index 82e9e19..99e923b 100644 --- a/src/components/common/VideoPlayer/VideoPlayer.tsx +++ b/src/components/common/VideoPlayer/VideoPlayer.tsx @@ -34,7 +34,11 @@ import { VideoElement, } from "./VideoPlayer-styles.ts"; import CSS from "csstype"; -import { setReduxPlaybackRate } from "../../../state/features/persistSlice.ts"; +import { + setReduxPlaybackRate, + setStretchVideoSetting, + StretchVideoType, +} from "../../../state/features/persistSlice.ts"; export interface VideoStyles { videoContainer?: CSS.Properties; @@ -101,6 +105,10 @@ export const VideoPlayer = React.forwardRef( const [anchorEl, setAnchorEl] = useState(null); const [showControlsFullScreen, setShowControlsFullScreen] = useState(true); + const [videoObjectFit, setVideoObjectFit] = useState( + persistSelector.stretchVideoSetting + ); + const videoPlaying = useSelector( (state: RootState) => state.global.videoPlaying ); @@ -597,10 +605,21 @@ export const VideoPlayer = React.forwardRef( } }; + const toggleStretchVideoSetting = () => { + const newStretchVideoSetting = + persistSelector.stretchVideoSetting === "contain" ? "fill" : "contain"; + + setVideoObjectFit(newStretchVideoSetting); + dispatch(setStretchVideoSetting(newStretchVideoSetting)); + }; const keyboardShortcutsDown = (e: React.KeyboardEvent) => { e.preventDefault(); switch (e.key) { + case "o": + toggleStretchVideoSetting(); + break; + case Key.Add: increaseSpeed(false); break; @@ -825,7 +844,7 @@ export const VideoPlayer = React.forwardRef( startPlay ? { ...videoStyles?.video, - objectFit: persistSelector.stretchVideoSetting, + objectFit: videoObjectFit, } : { height: "100%", ...videoStyles } } diff --git a/src/pages/ContentPages/PlaylistContent/PlaylistContent-styles.tsx b/src/pages/ContentPages/PlaylistContent/PlaylistContent-styles.tsx index 7c3bce6..01776ab 100644 --- a/src/pages/ContentPages/PlaylistContent/PlaylistContent-styles.tsx +++ b/src/pages/ContentPages/PlaylistContent/PlaylistContent-styles.tsx @@ -2,7 +2,6 @@ import { styled } from "@mui/system"; import { Box, Grid, Typography, Checkbox } from "@mui/material"; export const VideoPlayerContainer = styled(Box)(({ theme }) => ({ - maxWidth: "95%", display: "flex", flexDirection: "column", alignItems: "flex-start", diff --git a/src/pages/ContentPages/PlaylistContent/PlaylistContent.tsx b/src/pages/ContentPages/PlaylistContent/PlaylistContent.tsx index 401b650..0c1e501 100644 --- a/src/pages/ContentPages/PlaylistContent/PlaylistContent.tsx +++ b/src/pages/ContentPages/PlaylistContent/PlaylistContent.tsx @@ -20,6 +20,11 @@ import AttachFileIcon from "@mui/icons-material/AttachFile"; import DownloadIcon from "@mui/icons-material/Download"; import mockImg from "../../../test/mockimg.jpg"; +import { + extractSigValue, + getPaymentInfo, + isTimestampWithinRange, +} from "../VideoContent/VideoContent-functions.ts"; import { AuthorTextComment, FileAttachmentContainer, @@ -48,11 +53,6 @@ import { DisplayHtml } from "../../../components/common/TextEditor/DisplayHtml.t import FileElement from "../../../components/common/FileElement.tsx"; import { SuperLike } from "../../../components/common/ContentButtons/SuperLike.tsx"; import { useFetchSuperLikes } from "../../../hooks/useFetchSuperLikes.tsx"; -import { - extractSigValue, - getPaymentInfo, - isTimestampWithinRange, -} from "../VideoContent/VideoContent.tsx"; import { SuperLikesSection } from "../../../components/common/SuperLikesList/SuperLikesSection.tsx"; import { QTUBE_VIDEO_BASE, @@ -196,6 +196,7 @@ export const PlaylistContent = () => { setVideoData(combinedData); dispatch(addToHashMap(combinedData)); + checkforPlaylist(name, id); } } @@ -275,11 +276,8 @@ export const PlaylistContent = () => { const fullId = vid ? `${vid.identifier}-${vid.name}` : undefined; const existingVideo = hashMapVideos[fullId]; - if (existingVideo) { - setVideoData(existingVideo); - } else { - getVideoData(vid?.name, vid?.identifier); - } + if (existingVideo) setVideoData(existingVideo); + else getVideoData(vid?.name, vid?.identifier); } } } @@ -298,13 +296,12 @@ export const PlaylistContent = () => { } }, [id, channelName]); + const descriptionThreshold = 200; useEffect(() => { if (contentRef.current) { const height = contentRef.current.offsetHeight; - if (height > 100) { - // Assuming 100px is your threshold - setDescriptionHeight(100); - } + if (height > descriptionThreshold) + setDescriptionHeight(descriptionThreshold); } }, [videoData]); @@ -409,13 +406,27 @@ export const PlaylistContent = () => { }, [getComments, videoData?.id, nameAddress]); const focusVideo = (e: React.MouseEvent) => { - const focusRef = containerRef.current?.getContainerRef()?.current; - const isCorrectTarget = e.currentTarget == e.target; - if (focusRef && isCorrectTarget) { + console.log("in focusVideo"); + const target = e.target as Element; + + const textTagNames = ["TEXTAREA", "P", "H[1-6]", "STRONG", "svg", "A"]; + const noText = + textTagNames.findIndex(s => { + return target?.tagName.match(s); + }) < 0; + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const clickOnEmptySpace = !target?.onclick && noText; + + console.log("tagName is: ", target?.tagName); + + if (target == e.currentTarget || clickOnEmptySpace) { + console.log("in correctTarget"); + const focusRef = containerRef.current?.getContainerRef()?.current; focusRef.focus({ preventScroll: true }); } }; - return ( { alignItems: "center", flexDirection: "column", padding: "0px 10px", + marginLeft: "5%", }} onClick={focusVideo} > @@ -492,7 +504,6 @@ export const PlaylistContent = () => { gridTemplateColumns: "1fr 1fr", }} > - {" "} { )} - - {descriptionHeight && !isExpandedDescription && ( - { - if (isExpandedDescription) return; - setIsExpandedDescription(true); - }} - /> - )} + {videoData?.fullDescription && ( - {videoData?.htmlDescription ? ( - - ) : ( - - {videoData?.fullDescription} - + onClick={() => { + if (isExpandedDescription) return; + setIsExpandedDescription(true); + }} + /> )} - - {descriptionHeight && ( - { - setIsExpandedDescription(prev => !prev); - }} + - {isExpandedDescription ? "Show less" : "...more"} - - )} - + {videoData?.htmlDescription ? ( + + ) : ( + + {videoData?.fullDescription} + + )} + + {descriptionHeight >= descriptionThreshold && ( + { + setIsExpandedDescription(prev => !prev); + }} + sx={{ + fontWeight: "bold", + fontSize: "16px", + cursor: "pointer", + paddingLeft: "15px", + paddingTop: "15px", + }} + > + {isExpandedDescription ? "Show less" : "...more"} + + )} + + )} )} + {videoData?.id && videoData?.user && ( + + )} + {videoData?.id && channelName && ( + + )} - {}} - loadingSuperLikes={loadingSuperLikes} - superlikes={superlikeList} - postId={videoData?.id || ""} - postName={videoData?.user || ""} - /> - - - ); }; diff --git a/src/pages/ContentPages/VideoContent/VideoContent-functions.ts b/src/pages/ContentPages/VideoContent/VideoContent-functions.ts new file mode 100644 index 0000000..d313932 --- /dev/null +++ b/src/pages/ContentPages/VideoContent/VideoContent-functions.ts @@ -0,0 +1,56 @@ +export function isTimestampWithinRange(resTimestamp, resCreated) { + // Calculate the absolute difference in milliseconds + const difference = Math.abs(resTimestamp - resCreated); + + // 2 minutes in milliseconds + const twoMinutesInMilliseconds = 3 * 60 * 1000; + + // Check if the difference is within 2 minutes + return difference <= twoMinutesInMilliseconds; +} + +export function extractSigValue(metadescription) { + // Function to extract the substring within double asterisks + function extractSubstring(str) { + const match = str.match(/\*\*(.*?)\*\*/); + return match ? match[1] : null; + } + + // Function to extract the 'sig' value + function extractSig(str) { + const regex = /sig:(.*?)(;|$)/; + const match = str.match(regex); + return match ? match[1] : null; + } + + // Extracting the relevant substring + const relevantSubstring = extractSubstring(metadescription); + + if (relevantSubstring) { + // Extracting the 'sig' value + return extractSig(relevantSubstring); + } else { + return null; + } +} + +export const getPaymentInfo = async (signature: string) => { + try { + const url = `/transactions/signature/${signature}`; + const response = await fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + // Coin payment info must be added to responseData so we can display it to the user + const responseData = await response.json(); + if (responseData && !responseData.error) { + return responseData; + } else { + throw new Error("unable to get payment"); + } + } catch (error) { + throw new Error("unable to get payment"); + } +}; diff --git a/src/pages/ContentPages/VideoContent/VideoContent-styles.tsx b/src/pages/ContentPages/VideoContent/VideoContent-styles.tsx index 42d130f..5c1794d 100644 --- a/src/pages/ContentPages/VideoContent/VideoContent-styles.tsx +++ b/src/pages/ContentPages/VideoContent/VideoContent-styles.tsx @@ -1,12 +1,15 @@ import { styled } from "@mui/system"; import { Box, Grid, Typography, Checkbox } from "@mui/material"; -export const VideoPlayerContainer = styled(Box)(({ theme }) => ({ - maxWidth: "95%", - width: "1000px", +export const VideoContentContainer = styled(Box)(({ theme }) => ({ display: "flex", flexDirection: "column", - alignItems: "flex-start", + alignItems: "start", +})); + +export const VideoPlayerContainer = styled(Box)(({ theme }) => ({ + width: "55vw", + marginLeft: "5%", })); export const VideoTitle = styled(Typography)(({ theme }) => ({ diff --git a/src/pages/ContentPages/VideoContent/VideoContent.tsx b/src/pages/ContentPages/VideoContent/VideoContent.tsx index 60d1947..22573e9 100644 --- a/src/pages/ContentPages/VideoContent/VideoContent.tsx +++ b/src/pages/ContentPages/VideoContent/VideoContent.tsx @@ -1,26 +1,45 @@ +import DownloadIcon from "@mui/icons-material/Download"; +import { Avatar, Box, Typography, useTheme } from "@mui/material"; import React, { - useState, + useCallback, + useEffect, useMemo, useRef, - useEffect, - useCallback, + useState, } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useNavigate, useParams } from "react-router-dom"; -import ResponsiveImage from "../../../components/ResponsiveImage.tsx"; -import { setIsLoadingGlobal } from "../../../state/features/globalSlice.ts"; -import { Avatar, Box, Typography, useTheme } from "@mui/material"; +import DeletedVideo from "../../../assets/img/DeletedVideo.jpg"; +import { CommentSection } from "../../../components/common/Comments/CommentSection.tsx"; +import { FollowButton } from "../../../components/common/ContentButtons/FollowButton.tsx"; +import { LikeAndDislike } from "../../../components/common/ContentButtons/LikeAndDislike.tsx"; +import { SubscribeButton } from "../../../components/common/ContentButtons/SubscribeButton.tsx"; +import { SuperLike } from "../../../components/common/ContentButtons/SuperLike.tsx"; +import FileElement from "../../../components/common/FileElement.tsx"; +import { SuperLikesSection } from "../../../components/common/SuperLikesList/SuperLikesSection.tsx"; +import { DisplayHtml } from "../../../components/common/TextEditor/DisplayHtml.tsx"; import { refType, VideoPlayer, } from "../../../components/common/VideoPlayer/VideoPlayer.tsx"; -import { RootState } from "../../../state/store.ts"; +import { + QTUBE_VIDEO_BASE, + SUPER_LIKE_BASE, +} from "../../../constants/Identifiers.ts"; +import { + minPriceSuperlike, + titleFormatterOnSave, +} from "../../../constants/Misc.ts"; +import { useFetchSuperLikes } from "../../../hooks/useFetchSuperLikes.tsx"; +import { setIsLoadingGlobal } from "../../../state/features/globalSlice.ts"; import { addToHashMap } from "../../../state/features/videoSlice.ts"; -import AttachFileIcon from "@mui/icons-material/AttachFile"; -import DownloadIcon from "@mui/icons-material/Download"; -import DeletedVideo from "../../../assets/img/DeletedVideo.jpg"; - -import mockImg from "../../../test/mockimg.jpg"; +import { RootState } from "../../../state/store.ts"; +import { formatDate } from "../../../utils/time.ts"; +import { + extractSigValue, + getPaymentInfo, + isTimestampWithinRange, +} from "./VideoContent-functions.ts"; import { AuthorTextComment, FileAttachmentContainer, @@ -29,98 +48,10 @@ import { StyledCardColComment, StyledCardHeaderComment, VideoDescription, - VideoPlayerContainer, + VideoContentContainer, VideoTitle, + VideoPlayerContainer, } from "./VideoContent-styles.tsx"; -import { setUserAvatarHash } from "../../../state/features/globalSlice.ts"; -import { - formatDate, - formatDateSeconds, - formatTimestampSeconds, -} from "../../../utils/time.ts"; -import { NavbarName } from "../../../components/layout/Navbar/Navbar-styles.tsx"; -import { CommentSection } from "../../../components/common/Comments/CommentSection.tsx"; -import { - CrowdfundSubTitle, - CrowdfundSubTitleRow, -} from "../../../components/Publish/PublishVideo/PublishVideo-styles.tsx"; -import { Playlists } from "../../../components/Playlists/Playlists.tsx"; -import { DisplayHtml } from "../../../components/common/TextEditor/DisplayHtml.tsx"; -import FileElement from "../../../components/common/FileElement.tsx"; -import { SuperLike } from "../../../components/common/ContentButtons/SuperLike.tsx"; -import { CommentContainer } from "../../../components/common/Comments/Comments-styles.tsx"; -import { Comment } from "../../../components/common/Comments/Comment.tsx"; -import { SuperLikesSection } from "../../../components/common/SuperLikesList/SuperLikesSection.tsx"; -import { useFetchSuperLikes } from "../../../hooks/useFetchSuperLikes.tsx"; -import { - FOR_SUPER_LIKE, - QTUBE_VIDEO_BASE, - SUPER_LIKE_BASE, -} from "../../../constants/Identifiers.ts"; -import { - minPriceSuperlike, - titleFormatterOnSave, -} from "../../../constants/Misc.ts"; -import { SubscribeButton } from "../../../components/common/ContentButtons/SubscribeButton.tsx"; -import { FollowButton } from "../../../components/common/ContentButtons/FollowButton.tsx"; -import { LikeAndDislike } from "../../../components/common/ContentButtons/LikeAndDislike.tsx"; - -export function isTimestampWithinRange(resTimestamp, resCreated) { - // Calculate the absolute difference in milliseconds - const difference = Math.abs(resTimestamp - resCreated); - - // 2 minutes in milliseconds - const twoMinutesInMilliseconds = 3 * 60 * 1000; - - // Check if the difference is within 2 minutes - return difference <= twoMinutesInMilliseconds; -} - -export function extractSigValue(metadescription) { - // Function to extract the substring within double asterisks - function extractSubstring(str) { - const match = str.match(/\*\*(.*?)\*\*/); - return match ? match[1] : null; - } - - // Function to extract the 'sig' value - function extractSig(str) { - const regex = /sig:(.*?)(;|$)/; - const match = str.match(regex); - return match ? match[1] : null; - } - - // Extracting the relevant substring - const relevantSubstring = extractSubstring(metadescription); - - if (relevantSubstring) { - // Extracting the 'sig' value - return extractSig(relevantSubstring); - } else { - return null; - } -} - -export const getPaymentInfo = async (signature: string) => { - try { - const url = `/transactions/signature/${signature}`; - const response = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - // Coin payment info must be added to responseData so we can display it to the user - const responseData = await response.json(); - if (responseData && !responseData.error) { - return responseData; - } else { - throw new Error("unable to get payment"); - } - } catch (error) { - throw new Error("unable to get payment"); - } -}; export const VideoContent = () => { const { name: channelName, id } = useParams(); @@ -130,6 +61,7 @@ export const VideoContent = () => { useState(false); const [superlikeList, setSuperlikelist] = useState([]); const [loadingSuperLikes, setLoadingSuperLikes] = useState(false); + const { addSuperlikeRawDataGetToList } = useFetchSuperLikes(); const containerRef = useRef(null); @@ -149,6 +81,8 @@ export const VideoContent = () => { const [descriptionHeight, setDescriptionHeight] = useState( null ); + const [videoData, setVideoData] = useState(null); + const [isVideoLoaded, setIsVideoLoaded] = useState(false); const userAvatarHash = useSelector( (state: RootState) => state.global.userAvatarHash @@ -183,8 +117,6 @@ export const VideoContent = () => { const navigate = useNavigate(); const theme = useTheme(); - const [videoData, setVideoData] = useState(null); - const saveAsFilename = useMemo(() => { // nb. we prefer to construct the local filename to use for // saving, from the video "title" when possible @@ -224,6 +156,7 @@ export const VideoContent = () => { videoReference?.name && videoReference?.service ) { + setIsVideoLoaded(true); return videoReference; } else { return null; @@ -302,13 +235,12 @@ export const VideoContent = () => { } }, [id, channelName]); + const descriptionThreshold = 200; useEffect(() => { if (contentRef.current) { const height = contentRef.current.offsetHeight; - if (height > 100) { - // Assuming 100px is your threshold - setDescriptionHeight(100); - } + if (height > descriptionThreshold) + setDescriptionHeight(descriptionThreshold); } }, [videoData]); @@ -382,35 +314,42 @@ export const VideoContent = () => { ); const focusVideo = (e: React.MouseEvent) => { - const focusRef = containerRef.current?.getContainerRef()?.current; - const isCorrectTarget = e.currentTarget == e.target; - if (focusRef && isCorrectTarget) { + console.log("in focusVideo"); + const target = e.target as Element; + + const textTagNames = ["TEXTAREA", "P", "H[1-6]", "STRONG", "svg", "A"]; + const noText = + textTagNames.findIndex(s => { + return target?.tagName.match(s); + }) < 0; + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const clickOnEmptySpace = !target?.onclick && noText; + + console.log("tagName is: ", target?.tagName); + // clicking on link in superlikes bar shows deleted video when loading + + if (target == e.currentTarget || clickOnEmptySpace) { + console.log("in correctTarget"); + const focusRef = containerRef.current?.getContainerRef()?.current; focusRef.focus({ preventScroll: true }); } }; return ( - - + {videoReference ? ( - + { video: { aspectRatio: "16 / 9" }, }} /> - + + ) : isVideoLoaded ? ( + ) : ( - + )} - - - - + + + { - navigate(`/channel/${channelName}`); + "& .MuiCardHeader-content": { + overflow: "hidden", + }, }} > - - - - { navigate(`/channel/${channelName}`); }} > - {channelName} - {channelName !== userName && ( - <> - - - - )} - - - + + + + { + navigate(`/channel/${channelName}`); + }} + > + {channelName} + {channelName !== userName && ( + <> + + + + )} + + + + + + {videoData && ( + <> + + { + setSuperlikelist(prev => [val, ...prev]); + }} + /> + + )} + {videoData?.filename && ( + + Save to Disk + + + + + )} + - {videoData && ( - <> - - { - setSuperlikelist(prev => [val, ...prev]); - }} - /> - - )} - - Save to Disk - - - - - - - - - {videoData?.title} - - - - {videoData?.created && ( - - {formatDate(videoData.created)} - - )} - - - - {descriptionHeight && !isExpandedDescription && ( - { - if (isExpandedDescription) return; - setIsExpandedDescription(true); + textAlign: "start", }} - /> - )} - - {videoData?.htmlDescription ? ( - - ) : ( - - {videoData?.fullDescription} - - )} + > + {videoData?.title} + - {descriptionHeight && ( + + {videoData?.created && ( { - setIsExpandedDescription(prev => !prev); - }} + variant="h6" sx={{ - fontWeight: "bold", fontSize: "16px", - cursor: "pointer", - paddingLeft: "15px", - paddingTop: "15px", }} + color={theme.palette.text.primary} > - {isExpandedDescription ? "Show less" : "...more"} + {formatDate(videoData.created)} )} - - - {}} - loadingSuperLikes={loadingSuperLikes} - superlikes={superlikeList} - postId={id || ""} - postName={channelName || ""} - /> - - + + {videoData?.fullDescription && ( + + {descriptionHeight && !isExpandedDescription && ( + { + if (isExpandedDescription) return; + setIsExpandedDescription(true); + }} + /> + )} + + {videoData?.htmlDescription ? ( + + ) : ( + + {videoData?.fullDescription} + + )} + + {descriptionHeight >= descriptionThreshold && ( + { + setIsExpandedDescription(prev => !prev); + }} + sx={{ + fontWeight: "bold", + fontSize: "16px", + cursor: "pointer", + paddingLeft: "15px", + paddingTop: "15px", + }} + > + {isExpandedDescription ? "Show less" : "...more"} + + )} + + )} + + {id && channelName && ( + <> + {}} + loadingSuperLikes={loadingSuperLikes} + superlikes={superlikeList} + postId={id || ""} + postName={channelName || ""} + /> + + + )} + - + ); }; diff --git a/src/state/features/persistSlice.ts b/src/state/features/persistSlice.ts index 1b02954..70c21b3 100644 --- a/src/state/features/persistSlice.ts +++ b/src/state/features/persistSlice.ts @@ -77,6 +77,7 @@ export const persistSlice = createSlice({ export const { setHomePageSelectedTab, + setStretchVideoSetting, subscribe, unSubscribe, setReduxPlaybackRate, diff --git a/src/wrappers/GlobalWrapper.tsx b/src/wrappers/GlobalWrapper.tsx index c80f223..29ebe83 100644 --- a/src/wrappers/GlobalWrapper.tsx +++ b/src/wrappers/GlobalWrapper.tsx @@ -6,6 +6,11 @@ import React, { useMemo, } from "react"; import { useDispatch, useSelector } from "react-redux"; +import { + extractSigValue, + getPaymentInfo, + isTimestampWithinRange, +} from "../pages/ContentPages/VideoContent/VideoContent-functions.ts"; import { addUser } from "../state/features/authSlice"; import NavBar from "../components/layout/Navbar/Navbar"; @@ -21,11 +26,6 @@ import { RequestQueue } from "../utils/queue"; import { EditVideo } from "../components/Publish/EditVideo/EditVideo"; import { EditPlaylist } from "../components/Publish/EditPlaylist/EditPlaylist"; import ConsentModal from "../components/common/ConsentModal"; -import { - extractSigValue, - getPaymentInfo, - isTimestampWithinRange, -} from "../pages/ContentPages/VideoContent/VideoContent"; import { useFetchSuperLikes } from "../hooks/useFetchSuperLikes"; import { SUPER_LIKE_BASE } from "../constants/Identifiers.ts"; import { minPriceSuperlike } from "../constants/Misc.ts"; @@ -185,7 +185,9 @@ const GlobalWrapper: React.FC = ({ children, setTheme }) => { ]; validCount++; } - } catch (error) {} + } catch (error) { + console.log(error); + } } } } @@ -194,7 +196,6 @@ const GlobalWrapper: React.FC = ({ children, setTheme }) => { dispatch(setSuperlikesAll(comments)); } catch (error) { console.error(error); - } finally { } }, []);