diff --git a/src/components/EditPlaylist/EditPlaylist.tsx b/src/components/EditPlaylist/EditPlaylist.tsx index 4be28c5..adacf2b 100644 --- a/src/components/EditPlaylist/EditPlaylist.tsx +++ b/src/components/EditPlaylist/EditPlaylist.tsx @@ -366,7 +366,12 @@ export const EditPlaylist = () => { ); } - + dispatch( + setNotification({ + msg: "Playlist published", + alertType: "success", + }) + ); onClose(); } catch (error: any) { @@ -394,21 +399,7 @@ export const EditPlaylist = () => { } } - const handleOnchange = (index: number, type: string, value: string) => { - // setFiles((prev) => { - // let formattedValue = value - // console.log({type}) - // if(type === 'title'){ - // formattedValue = value.replace(/[^a-zA-Z0-9\s]/g, "") - // } - // const copyFiles = [...prev]; - // copyFiles[index] = { - // ...copyFiles[index], - // [type]: formattedValue, - // }; - // return copyFiles; - // }); - }; + const handleOptionCategoryChangeVideos = ( event: SelectChangeEvent diff --git a/src/components/common/Notifications/Notifications.tsx b/src/components/common/Notifications/Notifications.tsx index f3f968f..eea0d13 100644 --- a/src/components/common/Notifications/Notifications.tsx +++ b/src/components/common/Notifications/Notifications.tsx @@ -115,7 +115,7 @@ export const Notifications = () => { let urlReference = null try { let idForUrl = extractIdValue(comment?.metadata?.description) - const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&identifier=${idForUrl}&limit=1&includemetadata=false&reverse=true&excludeblocked=true&offset=0&name=${username}`; + 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", headers: { diff --git a/src/components/common/SuperLike/SuperLike.tsx b/src/components/common/SuperLike/SuperLike.tsx index bfb2d26..b8c8b08 100644 --- a/src/components/common/SuperLike/SuperLike.tsx +++ b/src/components/common/SuperLike/SuperLike.tsx @@ -19,7 +19,13 @@ import { useDispatch, useSelector } from "react-redux"; import { setNotification } from "../../../state/features/notificationsSlice"; import ShortUniqueId from "short-unique-id"; import { objectToBase64 } from "../../../utils/toBase64"; -import { FOR, FOR_SUPER_LIKE, QTUBE_VIDEO_BASE, SUPER_LIKE_BASE, minPriceSuperlike } from "../../../constants"; +import { + FOR, + FOR_SUPER_LIKE, + QTUBE_VIDEO_BASE, + SUPER_LIKE_BASE, + minPriceSuperlike, +} from "../../../constants"; import { CommentInput } from "../Comments/Comments-styles"; import { CrowdfundActionButton, @@ -33,7 +39,14 @@ import { RootState } from "../../../state/store"; const uid = new ShortUniqueId({ length: 4 }); -export const SuperLike = ({ onSuccess, name, service, identifier, totalAmount, numberOfSuperlikes }) => { +export const SuperLike = ({ + onSuccess, + name, + service, + identifier, + totalAmount, + numberOfSuperlikes, +}) => { const [isOpen, setIsOpen] = useState(false); const [amount, setAmount] = useState(10); const [comment, setComment] = useState(""); @@ -56,8 +69,8 @@ export const SuperLike = ({ onSuccess, name, service, identifier, totalAmount, n async function publishSuperLike() { try { - if(!username) throw new Error("You need a name to publish") - if(!name) throw new Error("Could not retrieve content creator's name"); + if (!username) throw new Error("You need a name to publish"); + if (!name) throw new Error("Could not retrieve content creator's name"); let resName = await qortalRequest({ action: "GET_NAME_DATA", @@ -65,13 +78,15 @@ export const SuperLike = ({ onSuccess, name, service, identifier, totalAmount, n }); const address = resName.owner; - if(!identifier) throw new Error("Could not retrieve id of video post"); - if (comment.length > 200) throw new Error("Comment needs to be under 200 characters") + if (!identifier) throw new Error("Could not retrieve id of video post"); + // if (comment.length > 200) throw new Error("Comment needs to be under 200 characters") if (!address) throw new Error("Could not retrieve content creator's address"); if (!amount || amount < minPriceSuperlike) - throw new Error(`The amount needs to be at least ${minPriceSuperlike} QORT`); + throw new Error( + `The amount needs to be at least ${minPriceSuperlike} QORT` + ); let listOfPublishes = []; @@ -82,9 +97,12 @@ export const SuperLike = ({ onSuccess, name, service, identifier, totalAmount, n amount: amount, }); - - - let metadescription = `**sig:${res.signature};${FOR}:${name}_${FOR_SUPER_LIKE};nm:${name.slice(0,20)};id:${identifier.slice(-30)}**`; + let metadescription = `**sig:${ + res.signature + };${FOR}:${name}_${FOR_SUPER_LIKE};nm:${name.slice( + 0, + 20 + )};id:${identifier.slice(-30)}**`; const id = uid(); const identifierSuperLike = `${SUPER_LIKE_BASE}${identifier.slice( @@ -92,14 +110,25 @@ export const SuperLike = ({ onSuccess, name, service, identifier, totalAmount, n 39 )}_${id}`; + const superLikeToBase64 = await objectToBase64({ + comment, + transactionReference: res.signature, + notificationInformation: { + name, + identifier, + for: `${name}_${FOR_SUPER_LIKE}`, + }, + about: + "Super likes are a way to suppert your favorite content creators. Attach a message to the Super like and have your message seen before normal comments. There is a minimum amount for a Super like. Each Super like is verified before displaying to make there aren't any non-paid Super likes", + }); // Description is obtained from raw data - const base64 = utf8ToBase64(comment); + // const base64 = utf8ToBase64(comment); const requestBodyJson: any = { action: "PUBLISH_QDN_RESOURCE", name: username, service: "BLOG_COMMENT", - data64: base64, + data64: superLikeToBase64, title: "", description: metadescription, identifier: identifierSuperLike, @@ -107,7 +136,6 @@ export const SuperLike = ({ onSuccess, name, service, identifier, totalAmount, n filename: `superlike_metadata.json`, }; - listOfPublishes.push(requestBodyJson); setPublishes(listOfPublishes); @@ -140,47 +168,63 @@ export const SuperLike = ({ onSuccess, name, service, identifier, totalAmount, n <> { + if (username === name) { + dispatch( + setNotification({ + msg: "You cannot send yourself a Super like", + alertType: "error", + }) + ); + return; + } setIsOpen(true); }} sx={{ - display: 'flex', - alignItems: 'center', - gap: '15px', - cursor: 'pointer', - flexShrink: 0 + display: "flex", + alignItems: "center", + gap: "15px", + cursor: "pointer", + flexShrink: 0, }} - > - {numberOfSuperlikes === 0 ? null : ( -

{totalAmount} QORT from {numberOfSuperlikes} Super Likes

- )} - - - - - -

Super Like

-
+ > + {numberOfSuperlikes === 0 ? null : ( +

+ {totalAmount} QORT from {numberOfSuperlikes} Super Likes +

+ )} + + + + +

+ Super Like +

+
- Super Like @@ -210,7 +254,7 @@ export const SuperLike = ({ onSuccess, name, service, identifier, totalAmount, n > - Amount in QORT + Amount in QORT (min 10 QORT) setComment(e.target.value)} @@ -284,12 +328,12 @@ export const SuperLike = ({ onSuccess, name, service, identifier, totalAmount, n isOpen={isOpenMultiplePublish} onSubmit={() => { onSuccess({ - name: username, - message: comment, - service, - identifier, - amount: +amount, - created: Date.now() + name: username, + message: comment, + service, + identifier, + amount: +amount, + created: Date.now(), }); setIsOpenMultiplePublish(false); onClose(); @@ -300,7 +344,6 @@ export const SuperLike = ({ onSuccess, name, service, identifier, totalAmount, n alertType: "success", }) ); - }} publishes={publishes} /> diff --git a/src/components/common/SuperLikesList/Comment.tsx b/src/components/common/SuperLikesList/Comment.tsx index 78297ca..b244f6d 100644 --- a/src/components/common/SuperLikesList/Comment.tsx +++ b/src/components/common/SuperLikesList/Comment.tsx @@ -37,13 +37,17 @@ interface CommentProps { postName: string; onSubmit: (obj?: any, isEdit?: boolean) => void; amount?: null | number + isSuperLike?: boolean + hasHash?: boolean } export const Comment = ({ comment, postId, postName, onSubmit, - amount + amount, + isSuperLike, + hasHash }: CommentProps) => { const [isReplying, setIsReplying] = useState(false); const [isEditing, setIsEditing] = useState(false); @@ -90,6 +94,9 @@ export const Comment = ({ isEdit commentId={currentEdit?.identifier} commentMessage={currentEdit?.message} + isSuperLike={!!currentEdit?.transactionReference} + comment={comment} + hasHash={hasHash} /> @@ -137,7 +144,7 @@ export const Comment = ({ > reply - {user?.name === comment?.name && ( + {user?.name === comment?.name && hasHash && ( { const [value, setValue] = useState(""); const dispatch = useDispatch(); @@ -149,13 +156,34 @@ export const CommentEditor = ({ } try { + let data64 = null + let description = "" + let tag1 = "" + let superObj = {} + if(isSuperLike){ + if(!comment?.metadata?.description || !comment?.metadata?.tags[0] || !comment?.transactionReference || !comment?.notificationInformation || !comment?.about) throw new Error('unable to edit Super like') + description = comment?.metadata?.description + tag1 = comment?.metadata?.tags[0] + superObj = { + comment: value, + transactionReference: comment.transactionReference, + notificationInformation: comment.notificationInformation, + about: comment.about + } + const superLikeToBase64 = await objectToBase64(superObj); + data64 = superLikeToBase64 + } + if(isSuperLike && !data64) throw new Error('unable to edit Super like') + const base64 = utf8ToBase64(value); const resourceResponse = await qortalRequest({ action: "PUBLISH_QDN_RESOURCE", name: name, service: "BLOG_COMMENT", - data64: base64, + data64: isSuperLike ? data64 : base64, identifier: identifier, + description, + tag1 }); dispatch( setNotification({ @@ -163,6 +191,15 @@ export const CommentEditor = ({ alertType: "success", }) ); + + if(isSuperLike){ + dispatch(addtoHashMapSuperlikes({ + ...superObj, + ...comment, + message: value + })) + + } if (idForNotification) { addItem({ id: idForNotification, @@ -217,13 +254,18 @@ export const CommentEditor = ({ } await publishComment(identifier, idForNotification); - onSubmit({ - created: Date.now(), - identifier, - message: value, - service, - name: user?.name, - }); + if(isSuperLike){ + onSubmit({}) + } else { + onSubmit({ + created: Date.now(), + identifier, + message: value, + service, + name: user?.name, + }); + } + setValue(""); } catch (error) { console.error(error); diff --git a/src/components/common/SuperLikesList/SuperLikesSection.tsx b/src/components/common/SuperLikesList/SuperLikesSection.tsx index ec26bbb..51dac2c 100644 --- a/src/components/common/SuperLikesList/SuperLikesSection.tsx +++ b/src/components/common/SuperLikesList/SuperLikesSection.tsx @@ -57,6 +57,9 @@ export const SuperLikesSection = ({ loadingSuperLikes, superlikes, postId, postN const { user } = useSelector((state: RootState) => state.auth); const [newMessages, setNewMessages] = useState(0); const [loadingComments, setLoadingComments] = useState(null); + const hashMapSuperlikes = useSelector( + (state: RootState) => state.video.hashMapSuperlikes + ) const onSubmit = (obj?: any, isEdit?: boolean) => { if (isEdit) { setListComments((prev: any[]) => { @@ -168,7 +171,7 @@ export const SuperLikesSection = ({ loadingSuperLikes, superlikes, postId, postN ); useEffect(() => { - if(superlikes.length > 0 && postId){ + if(postId){ getComments(superlikes, postId) } }, [getComments, superlikes, postId]); @@ -188,7 +191,6 @@ export const SuperLikesSection = ({ loadingSuperLikes, superlikes, postId, postN }, []); }, [listComments]); - console.log({loadingComments, listComments, superlikes}) return ( <> @@ -212,14 +214,24 @@ export const SuperLikesSection = ({ loadingSuperLikes, superlikes, postId, postN ) : ( {structuredCommentList.map((comment: any) => { + let hasHash = false + let message = {...comment} + let hash = {} + if(hashMapSuperlikes[comment?.identifier]){ + message.message = hashMapSuperlikes[comment?.identifier]?.comment || "" + hasHash = true + hash = hashMapSuperlikes[comment?.identifier] + } return ( ); })} diff --git a/src/constants/index.ts b/src/constants/index.ts index ebf276c..5afe1c8 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,4 +1,4 @@ -const useTestIdentifiers = true; +const useTestIdentifiers = false; export const QTUBE_VIDEO_BASE = useTestIdentifiers ? "MYTEST_vid_" diff --git a/src/hooks/useFetchSuperLikes.tsx b/src/hooks/useFetchSuperLikes.tsx new file mode 100644 index 0000000..cc5fc04 --- /dev/null +++ b/src/hooks/useFetchSuperLikes.tsx @@ -0,0 +1,114 @@ +import React, { useCallback } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { + + addtoHashMapSuperlikes +} from '../state/features/videoSlice' + +import { RootState } from '../state/store' +import { queueSuperlikes } from '../wrappers/GlobalWrapper' + + + +export const useFetchSuperLikes = () => { + const dispatch = useDispatch() + const hashMapSuperlikes = useSelector( + (state: RootState) => state.video.hashMapSuperlikes + ) + const checkAndUpdateSuperlike= React.useCallback( + (superlike: any) => { + const existingVideo = hashMapSuperlikes[superlike.identifier] + if (!existingVideo) { + return true + } else if ( + superlike?.updated && + existingVideo?.updated && + (!existingVideo?.updated || superlike?.updated) > existingVideo?.updated + ) { + return true + } else { + return false + } + }, + [hashMapSuperlikes] + ) + + + const fetchSuperlike = async (data: any) => { + const getsuper = async () => { + const { user, videoId, content } = data + let obj: any = { + ...content, + isValid: false + } + + if (!user || !videoId) return obj + + try { + + const responseData = await qortalRequest({ + action: 'FETCH_QDN_RESOURCE', + name: user, + service: content?.service || 'BLOG_COMMENT', + identifier: videoId + }) + + obj = { + ...content, + ...responseData, + isValid: true + } + + return obj + } catch (error: any) { + throw new Error(error?.message || 'error') + } + } + + const res = await getsuper() + return res + } + + + + const getSuperLikes = async (user: string, videoId: string, content: any, retries: number = 0) => { + try { + const res = await fetchSuperlike({ + user, + videoId, + content + }) + + dispatch(addtoHashMapSuperlikes(res)) + } catch (error) { + retries= retries + 1 + if (retries < 2) { // 3 is the maximum number of retries here, you can adjust it to your needs + queueSuperlikes.push(() => getSuperLikes(user, videoId, content, retries + 1)); + } else { + console.error('Failed to get video after 3 attempts', error); + } + } + + + } + + const addSuperlikeRawDataGetToList = useCallback(({name, identifier, content})=> { + + if (name && identifier) { + const res = checkAndUpdateSuperlike(content) + if (res) { + queueSuperlikes.push(() => getSuperLikes(name, identifier, content)); + + + + } + } + + }, [checkAndUpdateSuperlike]) + + + + return { + addSuperlikeRawDataGetToList + } +} diff --git a/src/pages/PlaylistContent/PlaylistContent.tsx b/src/pages/PlaylistContent/PlaylistContent.tsx index 0b827c4..03b3308 100644 --- a/src/pages/PlaylistContent/PlaylistContent.tsx +++ b/src/pages/PlaylistContent/PlaylistContent.tsx @@ -1,4 +1,10 @@ -import React, { useState, useMemo, useRef, useEffect, useCallback } from "react"; +import React, { + useState, + useMemo, + useRef, + useEffect, + useCallback, +} from "react"; import { useDispatch, useSelector } from "react-redux"; import { useNavigate, useParams } from "react-router-dom"; import { setIsLoadingGlobal } from "../../state/features/globalSlice"; @@ -33,25 +39,70 @@ import { CrowdfundSubTitle, CrowdfundSubTitleRow, } from "../../components/UploadVideo/Upload-styles"; -import { QTUBE_VIDEO_BASE } from "../../constants"; +import { + QTUBE_VIDEO_BASE, + SUPER_LIKE_BASE, + minPriceSuperlike, +} from "../../constants"; import { Playlists } from "../../components/Playlists/Playlists"; import { DisplayHtml } from "../../components/common/TextEditor/DisplayHtml"; import FileElement from "../../components/common/FileElement"; +import { SuperLike } from "../../components/common/SuperLike/SuperLike"; +import { useFetchSuperLikes } from "../../hooks/useFetchSuperLikes"; +import { + extractSigValue, + getPaymentInfo, + isTimestampWithinRange, +} from "../VideoContent/VideoContent"; +import { SuperLikesSection } from "../../components/common/SuperLikesList/SuperLikesSection"; export const PlaylistContent = () => { const { name, id } = useParams(); - const [doAutoPlay, setDoAutoPlay] = useState(false) + const [doAutoPlay, setDoAutoPlay] = useState(false); const [isExpandedDescription, setIsExpandedDescription] = useState(false); - const [descriptionHeight, setDescriptionHeight] = - useState(null); - + const [descriptionHeight, setDescriptionHeight] = useState( + null + ); + const [superlikeList, setSuperlikelist] = useState([]); + const [loadingSuperLikes, setLoadingSuperLikes] = useState(false); + const { addSuperlikeRawDataGetToList } = useFetchSuperLikes(); + + const calculateAmountSuperlike = useMemo(() => { + const totalQort = superlikeList?.reduce((acc, curr) => { + if (curr?.amount && !isNaN(parseFloat(curr.amount))) + return acc + parseFloat(curr.amount); + else return acc; + }, 0); + return totalQort?.toFixed(2); + }, [superlikeList]); + const numberOfSuperlikes = useMemo(() => { + return superlikeList?.length ?? 0; + }, [superlikeList]); + + const [nameAddress, setNameAddress] = useState(""); + + const getAddressName = async (name) => { + const response = await qortalRequest({ + action: "GET_NAME_DATA", + name: name, + }); + + if (response?.owner) { + setNameAddress(response.owner); + } + }; + + useEffect(() => { + if (name) { + getAddressName(name); + } + }, [name]); + const userAvatarHash = useSelector( (state: RootState) => state.global.userAvatarHash ); const contentRef = useRef(null); - - const avatarUrl = useMemo(() => { let url = ""; @@ -144,182 +195,203 @@ export const PlaylistContent = () => { } }, []); - const checkforPlaylist = React.useCallback(async (name, id) => { + const checkforPlaylist = React.useCallback( + async (name, id) => { + try { + dispatch(setIsLoadingGlobal(true)); + + if (!name || !id) return; + + const url = `/arbitrary/resources/search?mode=ALL&service=PLAYLIST&identifier=${id}&limit=1&includemetadata=true&reverse=true&excludeblocked=true&name=${name}&exactmatchnames=true&offset=0`; + const response = await fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + const responseDataSearch = await response.json(); + + if (responseDataSearch?.length > 0) { + let resourceData = responseDataSearch[0]; + resourceData = { + title: resourceData?.metadata?.title, + category: resourceData?.metadata?.category, + categoryName: resourceData?.metadata?.categoryName, + tags: resourceData?.metadata?.tags || [], + description: resourceData?.metadata?.description, + created: resourceData?.created, + updated: resourceData?.updated, + name: resourceData.name, + videoImage: "", + identifier: resourceData.identifier, + service: resourceData.service, + }; + + const responseData = await qortalRequest({ + action: "FETCH_QDN_RESOURCE", + name: resourceData.name, + service: resourceData.service, + identifier: resourceData.identifier, + }); + + if (responseData && !responseData.error) { + const combinedData = { + ...resourceData, + ...responseData, + }; + const videos = []; + if (combinedData?.videos) { + for (const vid of combinedData.videos) { + const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&identifier=${vid.identifier}&limit=1&includemetadata=true&reverse=true&name=${vid.name}&exactmatchnames=true&offset=0`; + const response = await fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + const responseDataSearchVid = await response.json(); + + if (responseDataSearchVid?.length > 0) { + let resourceData2 = responseDataSearchVid[0]; + videos.push(resourceData2); + } + } + } + combinedData.videos = videos; + setPlaylistData(combinedData); + if (combinedData?.videos?.length > 0) { + const vid = combinedData?.videos[0]; + const existingVideo = hashMapVideos[vid?.identifier]; + + if (existingVideo) { + setVideoData(existingVideo); + } else { + getVideoData(vid?.name, vid?.identifier); + } + } + } + } + } catch (error) { + } finally { + dispatch(setIsLoadingGlobal(false)); + } + }, + [hashMapVideos] + ); + + React.useEffect(() => { + if (name && id) { + checkforPlaylist(name, id); + } + }, [id, name]); + + useEffect(() => { + if (contentRef.current) { + const height = contentRef.current.offsetHeight; + if (height > 100) { + // Assuming 100px is your threshold + setDescriptionHeight(100); + } + } + }, [videoData]); + + const nextVideo = useMemo(() => { + const currentVideoIndex = playlistData?.videos?.findIndex( + (item) => item?.identifier === videoData?.id + ); + if (currentVideoIndex !== -1) { + const nextVideoIndex = currentVideoIndex + 1; + const findVideo = playlistData?.videos[nextVideoIndex] || null; + if (findVideo) { + const id = findVideo?.identifier?.replace("_metadata", ""); + return { + ...findVideo, + service: "VIDEO", + identifier: id, + jsonId: findVideo?.identifier, + }; + } + } + + return null; + }, [playlistData, videoData]); + + const onEndVideo = useCallback(() => { + const currentVideoIndex = playlistData?.videos?.findIndex( + (item) => item?.identifier === videoData?.id + ); + if (currentVideoIndex !== -1) { + const nextVideoIndex = currentVideoIndex + 1; + const findVideo = playlistData?.videos[nextVideoIndex] || null; + if (findVideo) { + getVideoData(findVideo?.name, findVideo?.identifier); + setDoAutoPlay(true); + } + } + }, [videoData, playlistData]); + + const getComments = useCallback(async (id, nameAddressParam) => { + if (!id) return; try { - dispatch(setIsLoadingGlobal(true)); + setLoadingSuperLikes(true); - if (!name || !id) return; - - const url = `/arbitrary/resources/search?mode=ALL&service=PLAYLIST&identifier=${id}&limit=1&includemetadata=true&reverse=true&excludeblocked=true&name=${name}&exactmatchnames=true&offset=0`; + const url = `/arbitrary/resources/search?mode=ALL&service=BLOG_COMMENT&query=${SUPER_LIKE_BASE}${id.slice( + 0, + 39 + )}&limit=100&includemetadata=true&reverse=true&excludeblocked=true`; const response = await fetch(url, { method: "GET", headers: { "Content-Type": "application/json", }, }); - const responseDataSearch = await response.json(); - - if (responseDataSearch?.length > 0) { - let resourceData = responseDataSearch[0]; - resourceData = { - title: resourceData?.metadata?.title, - category: resourceData?.metadata?.category, - categoryName: resourceData?.metadata?.categoryName, - tags: resourceData?.metadata?.tags || [], - description: resourceData?.metadata?.description, - created: resourceData?.created, - updated: resourceData?.updated, - name: resourceData.name, - videoImage: "", - identifier: resourceData.identifier, - service: resourceData.service, - }; - - const responseData = await qortalRequest({ - action: "FETCH_QDN_RESOURCE", - name: resourceData.name, - service: resourceData.service, - identifier: resourceData.identifier, - }); - - if (responseData && !responseData.error) { - const combinedData = { - ...resourceData, - ...responseData, - }; - const videos = []; - if (combinedData?.videos) { - for (const vid of combinedData.videos) { - const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&identifier=${vid.identifier}&limit=1&includemetadata=true&reverse=true&name=${vid.name}&exactmatchnames=true&offset=0`; - const response = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, + const responseData = await response.json(); + let comments: any[] = []; + for (const comment of responseData) { + if ( + comment.identifier && + comment.name && + comment?.metadata?.description + ) { + try { + const result = extractSigValue(comment?.metadata?.description); + if (!result) continue; + const res = await getPaymentInfo(result); + if ( + +res?.amount >= minPriceSuperlike && + res.recipient === nameAddressParam && + isTimestampWithinRange(res?.timestamp, comment.created) + ) { + addSuperlikeRawDataGetToList({ + name: comment.name, + identifier: comment.identifier, + content: comment, }); - const responseDataSearchVid = await response.json(); - if (responseDataSearchVid?.length > 0) { - let resourceData2 = responseDataSearchVid[0]; - videos.push(resourceData2); - } + comments = [ + ...comments, + { + ...comment, + message: "", + amount: res.amount, + }, + ]; } - } - combinedData.videos = videos; - setPlaylistData(combinedData); - if(combinedData?.videos?.length > 0){ - const vid = combinedData?.videos[0] - const existingVideo = hashMapVideos[vid?.identifier]; - - if (existingVideo) { - setVideoData(existingVideo); - } else { - getVideoData(vid?.name, vid?.identifier); - } - } + } catch (error) {} } } + setSuperlikelist(comments); } catch (error) { - + console.error(error); } finally { - dispatch(setIsLoadingGlobal(false)); - + setLoadingSuperLikes(false); } - }, [hashMapVideos]); - - // React.useEffect(() => { - // if (name && id) { - // const existingVideo = hashMapVideos[id]; - - // if (existingVideo) { - // setVideoData(existingVideo); - // checkforPlaylist(name, id, existingVideo?.code); - // } else { - // getVideoData(name, id); - // } - // } - // }, [id, name]); - - React.useEffect(() => { - if (name && id) { - checkforPlaylist(name, id); - - } - }, [id, name]); - - // const getAvatar = React.useCallback(async (author: string) => { - // try { - // let url = await qortalRequest({ - // action: 'GET_QDN_RESOURCE_URL', - // name: author, - // service: 'THUMBNAIL', - // identifier: 'qortal_avatar' - // }) - - // setAvatarUrl(url) - // dispatch(setUserAvatarHash({ - // name: author, - // url - // })) - // } catch (error) { } - // }, []) - - // React.useEffect(() => { - // if (name && !avatarUrl) { - // const existingAvatar = userAvatarHash[name] - - // if (existingAvatar) { - // setAvatarUrl(existingAvatar) - // } else { - // getAvatar(name) - // } - - // } - - // }, [name, userAvatarHash]) + }, []); useEffect(() => { - if (contentRef.current) { - const height = contentRef.current.offsetHeight; - if (height > 100) { // Assuming 100px is your threshold - setDescriptionHeight(100) - } - } - }, [videoData]); - - - const nextVideo = useMemo(()=> { - - const currentVideoIndex = playlistData?.videos?.findIndex((item)=> item?.identifier === videoData?.id) - if(currentVideoIndex !== -1){ - const nextVideoIndex = currentVideoIndex + 1 - const findVideo = playlistData?.videos[nextVideoIndex] || null - if(findVideo){ - const id = findVideo?.identifier?.replace("_metadata", ""); - return { - ...findVideo, - service: 'VIDEO', - identifier: id, - jsonId: findVideo?.identifier - } - } - } - - return null - }, [playlistData, videoData]) - - const onEndVideo = useCallback(()=> { - const currentVideoIndex = playlistData?.videos?.findIndex((item)=> item?.identifier === videoData?.id) - if(currentVideoIndex !== -1){ - const nextVideoIndex = currentVideoIndex + 1 - const findVideo = playlistData?.videos[nextVideoIndex] || null - if(findVideo){ - getVideoData(findVideo?.name, findVideo?.identifier) - setDoAutoPlay(true) - } - } - - }, [videoData, playlistData]) + if (!nameAddress || !videoData?.id) return; + getComments(videoData?.id, nameAddress); + }, [getComments, videoData?.id, nameAddress]); return ( { > {videoData && videoData?.videos?.length === 0 ? ( <> - - This playlist is empty - + + This playlist is empty + ) : ( <> {videoReference && ( - - )} - - - - - - - save to disk - - - - - - - - {videoData?.title} - - {videoData?.created && ( - - {formatDate(videoData.created)} - - )} - - - { - navigate(`/channel/${name}`); - }} - > - - - - - - - {name} - - - - - - - {descriptionHeight && !isExpandedDescription && ( + )} + + + + + save to disk + + + + + + + + {videoData?.title} + + {videoData && ( + { + setSuperlikelist((prev) => [val, ...prev]); + }} + /> + )} + + + {videoData?.created && ( + + {formatDate(videoData.created)} + + )} + + { - if (isExpandedDescription) return; - setIsExpandedDescription(true); + navigate(`/channel/${name}`); }} - /> - )} - - {videoData?.htmlDescription ? ( - - ) : ( - - {videoData?.fullDescription} - - )} - - {descriptionHeight && ( - { - setIsExpandedDescription((prev) => !prev); - }} - sx={{ - fontWeight: "bold", - fontSize: "16px", - cursor: "pointer", - paddingLeft: "15px", - paddingTop: "15px", - }} - > - {isExpandedDescription ? "Show less" : "...more"} - - )} - - + > + + + + + + + {name} + + + + + + + {descriptionHeight && !isExpandedDescription && ( + { + if (isExpandedDescription) return; + setIsExpandedDescription(true); + }} + /> + )} + + {videoData?.htmlDescription ? ( + + ) : ( + + {videoData?.fullDescription} + + )} + + {descriptionHeight && ( + { + setIsExpandedDescription((prev) => !prev); + }} + sx={{ + fontWeight: "bold", + fontSize: "16px", + cursor: "pointer", + paddingLeft: "15px", + paddingTop: "15px", + }} + > + {isExpandedDescription ? "Show less" : "...more"} + + )} + )} - - + {}} + loadingSuperLikes={loadingSuperLikes} + superlikes={superlikeList} + postId={videoData?.id || ""} + postName={videoData?.user || ""} + /> { maxWidth: "1200px", }} > - + {playlistData && ( - + )} diff --git a/src/pages/VideoContent/VideoContent.tsx b/src/pages/VideoContent/VideoContent.tsx index a103bc8..85b9a1b 100644 --- a/src/pages/VideoContent/VideoContent.tsx +++ b/src/pages/VideoContent/VideoContent.tsx @@ -41,6 +41,7 @@ import { SuperLike } from "../../components/common/SuperLike/SuperLike"; import { CommentContainer } from "../../components/common/Comments/Comments-styles"; import { Comment } from "../../components/common/Comments/Comment"; import { SuperLikesSection } from "../../components/common/SuperLikesList/SuperLikesSection"; +import { useFetchSuperLikes } from "../../hooks/useFetchSuperLikes"; export function isTimestampWithinRange(resTimestamp, resCreated) { // Calculate the absolute difference in milliseconds @@ -106,7 +107,7 @@ export const VideoContent = () => { useState(false); const [superlikeList, setSuperlikelist] = useState([]) const [loadingSuperLikes, setLoadingSuperLikes] = useState(false) - + const {addSuperlikeRawDataGetToList} = useFetchSuperLikes() const calculateAmountSuperlike = useMemo(()=> { const totalQort = superlikeList?.reduce((acc, curr)=> { @@ -249,36 +250,7 @@ export const VideoContent = () => { } }, [id, name]); - // const getAvatar = React.useCallback(async (author: string) => { - // try { - // let url = await qortalRequest({ - // action: 'GET_QDN_RESOURCE_URL', - // name: author, - // service: 'THUMBNAIL', - // identifier: 'qortal_avatar' - // }) - - // setAvatarUrl(url) - // dispatch(setUserAvatarHash({ - // name: author, - // url - // })) - // } catch (error) { } - // }, []) - - // React.useEffect(() => { - // if (name && !avatarUrl) { - // const existingAvatar = userAvatarHash[name] - - // if (existingAvatar) { - // setAvatarUrl(existingAvatar) - // } else { - // getAvatar(name) - // } - - // } - - // }, [name, userAvatarHash]) + useEffect(() => { if (contentRef.current) { @@ -313,23 +285,16 @@ export const VideoContent = () => { try { + const result = extractSigValue(comment?.metadata?.description) if(!result) continue const res = await getPaymentInfo(result); if(+res?.amount >= minPriceSuperlike && res.recipient === nameAddressParam && isTimestampWithinRange(res?.timestamp, comment.created)){ + addSuperlikeRawDataGetToList({name:comment.name, identifier:comment.identifier, content: comment}) - const url = `/arbitrary/BLOG_COMMENT/${comment.name}/${comment.identifier}`; - const response = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - if(!response.ok) continue - const responseData2 = await response.text(); comments = [...comments, { ...comment, - message: responseData2, + message: "", amount: res.amount }]; diff --git a/src/state/features/videoSlice.ts b/src/state/features/videoSlice.ts index 8598d75..5bbaead 100644 --- a/src/state/features/videoSlice.ts +++ b/src/state/features/videoSlice.ts @@ -6,6 +6,7 @@ interface GlobalState { videos: Video[] filteredVideos: Video[] hashMapVideos: Record + hashMapSuperlikes: Record countNewVideos: number isFiltering: boolean filterValue: string @@ -21,6 +22,7 @@ const initialState: GlobalState = { videos: [], filteredVideos: [], hashMapVideos: {}, + hashMapSuperlikes: {}, countNewVideos: 0, isFiltering: false, filterValue: '', @@ -113,6 +115,10 @@ export const videoSlice = createSlice({ const video = action.payload state.hashMapVideos[video.id] = video }, + addtoHashMapSuperlikes: (state, action) => { + const superlike = action.payload + state.hashMapSuperlikes[superlike.identifier] = superlike + }, updateInHashMap: (state, action) => { const { id } = action.payload const video = action.payload @@ -197,7 +203,8 @@ export const { changeSelectedSubCategoryVideos, blockUser, setEditVideo, - setEditPlaylist + setEditPlaylist, + addtoHashMapSuperlikes } = videoSlice.actions export default videoSlice.reducer diff --git a/src/wrappers/GlobalWrapper.tsx b/src/wrappers/GlobalWrapper.tsx index 3e57bf3..6140ab2 100644 --- a/src/wrappers/GlobalWrapper.tsx +++ b/src/wrappers/GlobalWrapper.tsx @@ -27,6 +27,8 @@ interface Props { let timer: number | null = null; export const queue = new RequestQueue(); +export const queueSuperlikes = new RequestQueue(); + const GlobalWrapper: React.FC = ({ children, setTheme }) => { const dispatch = useDispatch();