@ -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 ,
VideoPlayer Container ,
VideoContent Container ,
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 < boolean > ( false ) ;
const [ superlikeList , setSuperlikelist ] = useState < any [ ] > ( [ ] ) ;
const [ loadingSuperLikes , setLoadingSuperLikes ] = useState < boolean > ( false ) ;
const { addSuperlikeRawDataGetToList } = useFetchSuperLikes ( ) ;
const containerRef = useRef < refType > ( null ) ;
@ -149,6 +81,8 @@ export const VideoContent = () => {
const [ descriptionHeight , setDescriptionHeight ] = useState < null | number > (
null
) ;
const [ videoData , setVideoData ] = useState < any > ( null ) ;
const [ isVideoLoaded , setIsVideoLoaded ] = useState < boolean > ( 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 < any > ( 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 < HTMLDivElement > ) = > {
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 ;
const isCorrectTarget = e . currentTarget == e . target ;
if ( focusRef && isCorrectTarget ) {
focusRef . focus ( { preventScroll : true } ) ;
}
} ;
return (
< >
< Box
sx = { {
display : "flex" ,
alignItems : "center" ,
marginLeft : "5% " ,
flexDirection : "column" ,
padding : "0px 10px" ,
} }
onClick = { focusVideo }
>
< VideoPlayerContainer
sx = { {
width : "55vw" ,
aspectRatio : "16/9" ,
} }
>
{ videoReference ? (
< Box
sx = { {
aspectRatio : "16/9" ,
} }
>
< VideoPlayerContainer >
< VideoPlayer
name = { videoReference ? . name }
service = { videoReference ? . service }
@ -424,13 +363,21 @@ export const VideoContent = () => {
video : { aspectRatio : "16 / 9" } ,
} }
/ >
< / Box >
< / VideoPlayerContainer >
) : isVideoLoaded ? (
< img
src = { DeletedVideo }
width = { "70%" }
height = { "37%" }
style = { { marginLeft : "5%" } }
/ >
) : (
< img src = { DeletedVideo } width = { "100%" } height = { "100%" } / >
< Box sx = { { width : "55vw" , aspectRatio : "16/9" } } > < / Box >
) }
< VideoContentContainer >
< Box
sx = { {
width : "100%" ,
width : "8 0%" ,
display : "grid" ,
gridTemplateColumns : "1fr 1fr" ,
marginTop : "15px" ,
@ -512,6 +459,7 @@ export const VideoContent = () => {
/ >
< / >
) }
{ videoData ? . filename && (
< FileAttachmentContainer >
< FileAttachmentFont > Save to Disk < / FileAttachmentFont >
< FileElement
@ -520,7 +468,9 @@ export const VideoContent = () => {
filename : saveAsFilename ,
mimeType : videoData?.videoType || '"video/mp4' ,
} }
title = { videoData ? . filename || videoData ? . title ? . slice ( 0 , 20 ) }
title = {
videoData ? . filename || videoData ? . title ? . slice ( 0 , 20 )
}
customStyles = { {
display : "flex" ,
alignItems : "center" ,
@ -530,6 +480,7 @@ export const VideoContent = () => {
< DownloadIcon / >
< / FileElement >
< / FileAttachmentContainer >
) }
< / Box >
< / Box >
< Box
@ -566,21 +517,28 @@ export const VideoContent = () => {
) }
< Spacer height = "30px" / >
{ videoData ? . fullDescription && (
< Box
sx = { {
background : "#333333" ,
borderRadius : "5px" ,
padding : "5px" ,
width : "10 0%" ,
width : "7 0%" ,
cursor : ! descriptionHeight
? "default"
: isExpandedDescription
? "default"
: "pointer" ,
position : "relative" ,
marginBottom : "30px" ,
} }
className = {
! descriptionHeight ? "" : isExpandedDescription ? "" : "hover-click"
! descriptionHeight
? ""
: isExpandedDescription
? ""
: "hover-click"
}
>
{ descriptionHeight && ! isExpandedDescription && (
@ -606,7 +564,7 @@ export const VideoContent = () => {
? "auto"
: isExpandedDescription
? "auto"
: "1 00px",
: "2 00px" ,
overflow : "hidden" ,
} }
>
@ -624,7 +582,7 @@ export const VideoContent = () => {
< / VideoDescription >
) }
< / Box >
{ descriptionHeight && (
{ descriptionHeight >= descriptionThreshold && (
< Typography
onClick = { ( ) = > {
setIsExpandedDescription ( prev = > ! prev ) ;
@ -641,7 +599,10 @@ export const VideoContent = () => {
< / Typography >
) }
< / Box >
< / VideoPlayerContainer >
) }
{ id && channelName && (
< >
< SuperLikesSection
/* eslint-disable-next-line @typescript-eslint/no-empty-function */
getMore = { ( ) = > { } }
@ -650,17 +611,11 @@ export const VideoContent = () => {
postId = { id || "" }
postName = { channelName || "" }
/ >
< Box
sx = { {
display : "flex" ,
gap : "20px" ,
width : "100%" ,
maxWidth : "1200px" ,
} }
>
< CommentSection postId = { id || "" } postName = { channelName || "" } / >
< / >
) }
< / VideoContentContainer >
< / Box >
< / Box >
< / >
) ;
} ;