mirror of
https://github.com/Qortal/q-tube.git
synced 2025-02-11 17:55:51 +00:00
added preview images on hover
This commit is contained in:
parent
5e211737b0
commit
469688b4e0
@ -1,4 +1,6 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Compressor from 'compressorjs'
|
||||
|
||||
import {
|
||||
AddCoverImageButton,
|
||||
AddLogoIcon,
|
||||
@ -13,6 +15,8 @@ import {
|
||||
StyledButton,
|
||||
TimesIcon,
|
||||
} from "./Upload-styles";
|
||||
import { CircularProgress } from "@mui/material";
|
||||
|
||||
import {
|
||||
Box,
|
||||
FormControl,
|
||||
@ -46,6 +50,8 @@ import { QTUBE_VIDEO_BASE, categories, subCategories } from "../../constants";
|
||||
import { MultiplePublish } from "../common/MultiplePublish/MultiplePublish";
|
||||
import { TextEditor } from "../common/TextEditor/TextEditor";
|
||||
import { extractTextFromHTML } from "../common/TextEditor/utils";
|
||||
import { toBase64 } from "../UploadVideo/UploadVideo";
|
||||
import { FrameExtractor } from "../common/FrameExtractor/FrameExtractor";
|
||||
|
||||
const uid = new ShortUniqueId();
|
||||
const shortuid = new ShortUniqueId({ length: 5 });
|
||||
@ -88,6 +94,8 @@ export const EditVideo = () => {
|
||||
useState<any>(null);
|
||||
const [selectedSubCategoryVideos, setSelectedSubCategoryVideos] =
|
||||
useState<any>(null);
|
||||
const [imageExtracts, setImageExtracts] = useState<any>([])
|
||||
|
||||
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
accept: {
|
||||
@ -205,6 +213,7 @@ export const EditVideo = () => {
|
||||
setVideoPropertiesToSetToRedux(null);
|
||||
setFile(null);
|
||||
setTitle("");
|
||||
setImageExtracts([])
|
||||
setDescription("");
|
||||
setCoverImage("");
|
||||
};
|
||||
@ -270,6 +279,7 @@ export const EditVideo = () => {
|
||||
fullDescription,
|
||||
videoImage: coverImage,
|
||||
videoReference: editVideoProperties.videoReference,
|
||||
extracts: file ? imageExtracts : editVideoProperties?.extracts,
|
||||
commentsId: editVideoProperties.commentsId,
|
||||
category,
|
||||
subcategory,
|
||||
@ -346,21 +356,7 @@ export const EditVideo = () => {
|
||||
}
|
||||
}
|
||||
|
||||
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<string>
|
||||
@ -380,6 +376,44 @@ export const EditVideo = () => {
|
||||
setSelectedSubCategoryVideos(selectedOption || null);
|
||||
};
|
||||
|
||||
const onFramesExtracted = async (imgs)=> {
|
||||
try {
|
||||
let imagesExtracts = []
|
||||
|
||||
for (const img of imgs){
|
||||
try {
|
||||
let compressedFile
|
||||
const image = img
|
||||
await new Promise<void>((resolve) => {
|
||||
new Compressor(image, {
|
||||
quality: .8,
|
||||
maxWidth: 750,
|
||||
mimeType: 'image/webp',
|
||||
success(result) {
|
||||
const file = new File([result], 'name', {
|
||||
type: 'image/webp'
|
||||
})
|
||||
compressedFile = file
|
||||
resolve()
|
||||
},
|
||||
error(err) {}
|
||||
})
|
||||
})
|
||||
if (!compressedFile) continue
|
||||
const base64Img = await toBase64(compressedFile)
|
||||
imagesExtracts.push(base64Img)
|
||||
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
setImageExtracts(imagesExtracts)
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
@ -466,6 +500,9 @@ export const EditVideo = () => {
|
||||
</FormControl>
|
||||
)}
|
||||
</Box>
|
||||
{file && (
|
||||
<FrameExtractor videoFile={file} onFramesExtracted={(imgs)=> onFramesExtracted(imgs)}/>
|
||||
)}
|
||||
<React.Fragment>
|
||||
{!coverImage ? (
|
||||
<ImageUploader onPick={(img: string) => setCoverImage(img)}>
|
||||
@ -548,7 +585,11 @@ export const EditVideo = () => {
|
||||
onClick={() => {
|
||||
publishQDNResource();
|
||||
}}
|
||||
disabled={file && imageExtracts.length === 0}
|
||||
>
|
||||
{file && imageExtracts.length === 0 && (
|
||||
<CircularProgress color="secondary" size={14} />
|
||||
)}
|
||||
Publish
|
||||
</CrowdfundActionButton>
|
||||
</Box>
|
||||
|
@ -44,7 +44,7 @@ const ResponsiveImage: React.FC<ResponsiveImageProps> = ({
|
||||
<Box
|
||||
sx={{
|
||||
padding: '2px',
|
||||
maxHeight: '50%'
|
||||
height: '100%'
|
||||
}}
|
||||
>
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Compressor from 'compressorjs'
|
||||
import {
|
||||
AddCoverImageButton,
|
||||
AddLogoIcon,
|
||||
@ -13,6 +14,8 @@ import {
|
||||
StyledButton,
|
||||
TimesIcon,
|
||||
} from "./Upload-styles";
|
||||
import { CircularProgress } from "@mui/material";
|
||||
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
@ -57,6 +60,17 @@ import { CardContentContainerComment } from "../common/Comments/Comments-styles"
|
||||
import { TextEditor } from "../common/TextEditor/TextEditor";
|
||||
import { extractTextFromHTML } from "../common/TextEditor/utils";
|
||||
import { FiltersCheckbox, FiltersRow, FiltersSubContainer } from "../../pages/Home/VideoList-styles";
|
||||
import { FrameExtractor } from "../common/FrameExtractor/FrameExtractor";
|
||||
|
||||
export const toBase64 = (file: File): Promise<string | ArrayBuffer | null> =>
|
||||
new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
reader.readAsDataURL(file)
|
||||
reader.onload = () => resolve(reader.result)
|
||||
reader.onerror = (error) => {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
|
||||
const uid = new ShortUniqueId();
|
||||
const shortuid = new ShortUniqueId({ length: 5 });
|
||||
@ -114,7 +128,7 @@ export const UploadVideo = ({ editId, editContent }: NewCrowdfundProps) => {
|
||||
const [isCheckTitleByFile, setIsCheckTitleByFile] = useState(false)
|
||||
const [isCheckSameCoverImage, setIsCheckSameCoverImage] = useState(false)
|
||||
const [isCheckDescriptionIsTitle, setIsCheckDescriptionIsTitle] = useState(false)
|
||||
|
||||
const [imageExtracts, setImageExtracts] = useState<any>({})
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
accept: {
|
||||
"video/*": [],
|
||||
@ -219,7 +233,8 @@ export const UploadVideo = ({ editId, editContent }: NewCrowdfundProps) => {
|
||||
|
||||
let listOfPublishes = [];
|
||||
|
||||
for (const publish of files) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const publish = files[i]
|
||||
const title = publish.title;
|
||||
const description = isCheckDescriptionIsTitle ? publish.title : publish.description;
|
||||
const category = selectedCategoryVideos.id;
|
||||
@ -271,6 +286,7 @@ export const UploadVideo = ({ editId, editContent }: NewCrowdfundProps) => {
|
||||
identifier: identifier,
|
||||
service: "VIDEO",
|
||||
},
|
||||
extracts: imageExtracts[i],
|
||||
commentsId: `${QTUBE_VIDEO_BASE}_cm_${id}`,
|
||||
category,
|
||||
subcategory,
|
||||
@ -539,6 +555,49 @@ export const UploadVideo = ({ editId, editContent }: NewCrowdfundProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
const onFramesExtracted = async (imgs, index)=> {
|
||||
try {
|
||||
let imagesExtracts = []
|
||||
|
||||
for (const img of imgs){
|
||||
try {
|
||||
let compressedFile
|
||||
const image = img
|
||||
await new Promise<void>((resolve) => {
|
||||
new Compressor(image, {
|
||||
quality: .8,
|
||||
maxWidth: 750,
|
||||
mimeType: 'image/webp',
|
||||
success(result) {
|
||||
const file = new File([result], 'name', {
|
||||
type: 'image/webp'
|
||||
})
|
||||
compressedFile = file
|
||||
resolve()
|
||||
},
|
||||
error(err) {}
|
||||
})
|
||||
})
|
||||
if (!compressedFile) continue
|
||||
const base64Img = await toBase64(compressedFile)
|
||||
imagesExtracts.push(base64Img)
|
||||
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
setImageExtracts((prev)=> {
|
||||
return {
|
||||
...prev,
|
||||
[index]: imagesExtracts
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{username && (
|
||||
@ -719,6 +778,7 @@ export const UploadVideo = ({ editId, editContent }: NewCrowdfundProps) => {
|
||||
{files.map((file, index) => {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
<FrameExtractor videoFile={file.file} onFramesExtracted={(imgs)=> onFramesExtracted(imgs, index)}/>
|
||||
<Typography>{file?.file?.name}</Typography>
|
||||
{!isCheckSameCoverImage && (
|
||||
<>
|
||||
@ -1126,24 +1186,30 @@ export const UploadVideo = ({ editId, editContent }: NewCrowdfundProps) => {
|
||||
) : (
|
||||
<CrowdfundActionButton
|
||||
variant="contained"
|
||||
disabled={files?.length !== Object.keys(imageExtracts)?.length}
|
||||
onClick={() => {
|
||||
next();
|
||||
}}
|
||||
>
|
||||
Next
|
||||
{files?.length !== Object.keys(imageExtracts)?.length ? 'Generating image extracts' : ''}
|
||||
{files?.length !== Object.keys(imageExtracts)?.length && (
|
||||
<CircularProgress color="secondary" size={14} />
|
||||
)}
|
||||
Next
|
||||
</CrowdfundActionButton>
|
||||
)}
|
||||
</Box>
|
||||
</CrowdfundActionButtonRow>
|
||||
</ModalBody>
|
||||
</Modal>
|
||||
|
||||
|
||||
{isOpenMultiplePublish && (
|
||||
<MultiplePublish
|
||||
isOpen={isOpenMultiplePublish}
|
||||
onSubmit={() => {
|
||||
setIsOpenMultiplePublish(false);
|
||||
setIsOpen(false);
|
||||
setImageExtracts({})
|
||||
setFiles([]);
|
||||
setStep("videos");
|
||||
setPlaylistCoverImage(null);
|
||||
|
77
src/components/common/FrameExtractor/FrameExtractor.tsx
Normal file
77
src/components/common/FrameExtractor/FrameExtractor.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
import React, { useEffect, useRef, useState, useMemo } from 'react';
|
||||
|
||||
export const FrameExtractor = ({ videoFile, onFramesExtracted }) => {
|
||||
const videoRef = useRef(null);
|
||||
const [durations, setDurations] = useState([]);
|
||||
const canvasRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const video = videoRef.current;
|
||||
video.addEventListener('loadedmetadata', () => {
|
||||
const duration = video.duration;
|
||||
if (isFinite(duration)) {
|
||||
// Proceed with your logic
|
||||
|
||||
console.log('duration', duration)
|
||||
const section = duration / 4;
|
||||
let timestamps = [];
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const randomTime = Math.random() * section + i * section;
|
||||
timestamps.push(randomTime);
|
||||
}
|
||||
|
||||
setDurations(timestamps);
|
||||
} else {
|
||||
onFramesExtracted([])
|
||||
}
|
||||
});
|
||||
}, [videoFile]);
|
||||
|
||||
console.log({durations})
|
||||
|
||||
useEffect(() => {
|
||||
if (durations.length === 4) {
|
||||
extractFrames();
|
||||
}
|
||||
}, [durations]);
|
||||
|
||||
const fileUrl = useMemo(() => {
|
||||
return URL.createObjectURL(videoFile);
|
||||
}, [videoFile]);
|
||||
|
||||
const extractFrames = async () => {
|
||||
const video = videoRef.current;
|
||||
const canvas = canvasRef.current;
|
||||
canvas.width = video.videoWidth;
|
||||
canvas.height = video.videoHeight;
|
||||
const context = canvas.getContext('2d');
|
||||
|
||||
let frameData = [];
|
||||
|
||||
for (const time of durations) {
|
||||
await new Promise((resolve) => {
|
||||
video.currentTime = time;
|
||||
const onSeeked = () => {
|
||||
context.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||
canvas.toBlob(blob => {
|
||||
frameData.push(blob);
|
||||
resolve();
|
||||
}, 'image/png');
|
||||
video.removeEventListener('seeked', onSeeked);
|
||||
};
|
||||
video.addEventListener('seeked', onSeeked, { once: true });
|
||||
});
|
||||
}
|
||||
|
||||
onFramesExtracted(frameData);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<video ref={videoRef} style={{ display: 'none' }} src={fileUrl}></video>
|
||||
<canvas ref={canvasRef} style={{ display: 'none' }}></canvas>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
const useTestIdentifiers = false;
|
||||
const useTestIdentifiers = true;
|
||||
|
||||
export const QTUBE_VIDEO_BASE = useTestIdentifiers
|
||||
? "MYTEST_vid_"
|
||||
|
42
src/pages/Home/VideoCardImageContainer.tsx
Normal file
42
src/pages/Home/VideoCardImageContainer.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import React, { useState } from "react";
|
||||
import ResponsiveImage from "../../components/ResponsiveImage";
|
||||
|
||||
export const VideoCardImageContainer = ({
|
||||
videoImage,
|
||||
frameImages,
|
||||
height,
|
||||
width,
|
||||
}) => {
|
||||
const [previewImage, setPreviewImage] = useState(null);
|
||||
const intervalRef = React.useRef(null);
|
||||
|
||||
const startPreview = () => {
|
||||
let frameIndex = 0;
|
||||
intervalRef.current = setInterval(() => {
|
||||
setPreviewImage(frameImages[frameIndex]);
|
||||
frameIndex = (frameIndex + 1) % frameImages.length;
|
||||
}, 500); // Change frame every 500 ms
|
||||
};
|
||||
|
||||
const stopPreview = () => {
|
||||
clearInterval(intervalRef.current);
|
||||
setPreviewImage(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
height,
|
||||
maxWidth: "100%",
|
||||
}}
|
||||
onMouseEnter={startPreview}
|
||||
onMouseLeave={stopPreview}
|
||||
>
|
||||
<ResponsiveImage
|
||||
src={previewImage || videoImage}
|
||||
width={266}
|
||||
height={150}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -64,6 +64,7 @@ import { PlaylistSVG } from "../../assets/svgs/PlaylistSVG";
|
||||
import BlockIcon from "@mui/icons-material/Block";
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
import { LiskSuperLikeContainer } from "../../components/common/ListSuperLikes/LiskSuperLikeContainer";
|
||||
import { VideoCardImageContainer } from "./VideoCardImageContainer";
|
||||
|
||||
interface VideoListProps {
|
||||
mode?: string;
|
||||
@ -696,11 +697,13 @@ export const VideoList = ({ mode }: VideoListProps) => {
|
||||
navigate(`/video/${videoObj?.user}/${videoObj?.id}`);
|
||||
}}
|
||||
>
|
||||
<ResponsiveImage
|
||||
<VideoCardImageContainer width={266}
|
||||
height={150} videoImage={videoObj.videoImage} frameImages={videoObj?.extracts || []} />
|
||||
{/* <ResponsiveImage
|
||||
src={videoObj.videoImage}
|
||||
width={266}
|
||||
height={150}
|
||||
/>
|
||||
/> */}
|
||||
<VideoCardTitle>{videoObj.title}</VideoCardTitle>
|
||||
<BottomParent>
|
||||
<NameContainer
|
||||
|
@ -1,66 +1,68 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { RootState } from '../../state/store'
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../state/store";
|
||||
import { Avatar, Box, Button, Typography, useTheme } from "@mui/material";
|
||||
import { useFetchVideos } from "../../hooks/useFetchVideos";
|
||||
import LazyLoad from "../../components/common/LazyLoad";
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
Button,
|
||||
Typography,
|
||||
useTheme
|
||||
} from '@mui/material'
|
||||
import { useFetchVideos } from '../../hooks/useFetchVideos'
|
||||
import LazyLoad from '../../components/common/LazyLoad'
|
||||
import { BottomParent, NameContainer, ProductManagerRow, VideoCard, VideoCardCol, VideoCardContainer, VideoCardName, VideoCardTitle, VideoContainer, VideoUploadDate } from './VideoList-styles'
|
||||
import ResponsiveImage from '../../components/ResponsiveImage'
|
||||
import { formatDate, formatTimestampSeconds } from '../../utils/time'
|
||||
import { Video } from '../../state/features/videoSlice'
|
||||
import { queue } from '../../wrappers/GlobalWrapper'
|
||||
import { QTUBE_VIDEO_BASE } from '../../constants'
|
||||
BottomParent,
|
||||
NameContainer,
|
||||
ProductManagerRow,
|
||||
VideoCard,
|
||||
VideoCardCol,
|
||||
VideoCardContainer,
|
||||
VideoCardName,
|
||||
VideoCardTitle,
|
||||
VideoContainer,
|
||||
VideoUploadDate,
|
||||
} from "./VideoList-styles";
|
||||
import ResponsiveImage from "../../components/ResponsiveImage";
|
||||
import { formatDate, formatTimestampSeconds } from "../../utils/time";
|
||||
import { Video } from "../../state/features/videoSlice";
|
||||
import { queue } from "../../wrappers/GlobalWrapper";
|
||||
import { QTUBE_VIDEO_BASE } from "../../constants";
|
||||
import { VideoCardImageContainer } from "./VideoCardImageContainer";
|
||||
|
||||
interface VideoListProps {
|
||||
mode?: string
|
||||
mode?: string;
|
||||
}
|
||||
export const VideoListComponentLevel = ({ mode }: VideoListProps) => {
|
||||
const { name: paramName } = useParams()
|
||||
const theme = useTheme()
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true)
|
||||
const { name: paramName } = useParams();
|
||||
const theme = useTheme();
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
|
||||
const firstFetch = useRef(false)
|
||||
const afterFetch = useRef(false)
|
||||
const firstFetch = useRef(false);
|
||||
const afterFetch = useRef(false);
|
||||
const hashMapVideos = useSelector(
|
||||
(state: RootState) => state.video.hashMapVideos
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
const countNewVideos = useSelector(
|
||||
(state: RootState) => state.video.countNewVideos
|
||||
)
|
||||
);
|
||||
const userAvatarHash = useSelector(
|
||||
(state: RootState) => state.global.userAvatarHash
|
||||
)
|
||||
|
||||
const [videos, setVideos] = React.useState<Video[]>([])
|
||||
|
||||
const navigate = useNavigate()
|
||||
const {
|
||||
getVideo,
|
||||
getNewVideos,
|
||||
checkNewVideos,
|
||||
checkAndUpdateVideo
|
||||
} = useFetchVideos()
|
||||
);
|
||||
|
||||
const [videos, setVideos] = React.useState<Video[]>([]);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { getVideo, getNewVideos, checkNewVideos, checkAndUpdateVideo } =
|
||||
useFetchVideos();
|
||||
|
||||
const getVideos = React.useCallback(async () => {
|
||||
try {
|
||||
const offset = videos.length
|
||||
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QTUBE_VIDEO_BASE}_&limit=20&includemetadata=false&reverse=true&excludeblocked=true&name=${paramName}&exactmatchnames=true&offset=${offset}`
|
||||
const offset = videos.length;
|
||||
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QTUBE_VIDEO_BASE}_&limit=20&includemetadata=false&reverse=true&excludeblocked=true&name=${paramName}&exactmatchnames=true&offset=${offset}`;
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
method: "GET",
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
const responseData = await response.json()
|
||||
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const responseData = await response.json();
|
||||
|
||||
const structureData = responseData.map((video: any): Video => {
|
||||
return {
|
||||
title: video?.metadata?.title,
|
||||
@ -71,128 +73,126 @@ export const VideoListComponentLevel = ({ mode }: VideoListProps) => {
|
||||
created: video?.created,
|
||||
updated: video?.updated,
|
||||
user: video.name,
|
||||
videoImage: '',
|
||||
id: video.identifier
|
||||
}
|
||||
})
|
||||
|
||||
const copiedVideos: Video[] = [...videos]
|
||||
videoImage: "",
|
||||
id: video.identifier,
|
||||
};
|
||||
});
|
||||
|
||||
const copiedVideos: Video[] = [...videos];
|
||||
structureData.forEach((video: Video) => {
|
||||
const index = videos.findIndex((p) => p.id === video.id)
|
||||
const index = videos.findIndex((p) => p.id === video.id);
|
||||
if (index !== -1) {
|
||||
copiedVideos[index] = video
|
||||
copiedVideos[index] = video;
|
||||
} else {
|
||||
copiedVideos.push(video)
|
||||
copiedVideos.push(video);
|
||||
}
|
||||
})
|
||||
setVideos(copiedVideos)
|
||||
});
|
||||
setVideos(copiedVideos);
|
||||
|
||||
for (const content of structureData) {
|
||||
if (content.user && content.id) {
|
||||
const res = checkAndUpdateVideo(content)
|
||||
const res = checkAndUpdateVideo(content);
|
||||
if (res) {
|
||||
queue.push(() => getVideo(content.user, content.id, content));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
} finally {
|
||||
|
||||
}
|
||||
}, [videos, hashMapVideos])
|
||||
}, [videos, hashMapVideos]);
|
||||
|
||||
|
||||
const getVideosHandler = React.useCallback(async () => {
|
||||
if(!firstFetch.current || !afterFetch.current) return
|
||||
await getVideos()
|
||||
}, [getVideos])
|
||||
|
||||
if (!firstFetch.current || !afterFetch.current) return;
|
||||
await getVideos();
|
||||
}, [getVideos]);
|
||||
|
||||
const getVideosHandlerMount = React.useCallback(async () => {
|
||||
if(firstFetch.current) return
|
||||
firstFetch.current = true
|
||||
await getVideos()
|
||||
afterFetch.current = true
|
||||
setIsLoading(false)
|
||||
}, [getVideos])
|
||||
if (firstFetch.current) return;
|
||||
firstFetch.current = true;
|
||||
await getVideos();
|
||||
afterFetch.current = true;
|
||||
setIsLoading(false);
|
||||
}, [getVideos]);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
useEffect(()=> {
|
||||
if(!firstFetch.current){
|
||||
getVideosHandlerMount()
|
||||
useEffect(() => {
|
||||
if (!firstFetch.current) {
|
||||
getVideosHandlerMount();
|
||||
}
|
||||
}, [getVideosHandlerMount]);
|
||||
|
||||
}, [getVideosHandlerMount ])
|
||||
|
||||
|
||||
return (
|
||||
<ProductManagerRow>
|
||||
<Box sx={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
|
||||
<VideoCardContainer>
|
||||
{videos.map((video: any, index: number) => {
|
||||
const existingVideo = hashMapVideos[video.id]
|
||||
let hasHash = false
|
||||
let videoObj = video
|
||||
if (existingVideo) {
|
||||
videoObj = existingVideo
|
||||
hasHash = true
|
||||
}
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<VideoCardContainer>
|
||||
{videos.map((video: any, index: number) => {
|
||||
const existingVideo = hashMapVideos[video.id];
|
||||
let hasHash = false;
|
||||
let videoObj = video;
|
||||
if (existingVideo) {
|
||||
videoObj = existingVideo;
|
||||
hasHash = true;
|
||||
}
|
||||
|
||||
let avatarUrl = ''
|
||||
if(userAvatarHash[videoObj?.user]){
|
||||
avatarUrl = userAvatarHash[videoObj?.user]
|
||||
}
|
||||
let avatarUrl = "";
|
||||
if (userAvatarHash[videoObj?.user]) {
|
||||
avatarUrl = userAvatarHash[videoObj?.user];
|
||||
}
|
||||
|
||||
if(hasHash && (!videoObj?.videoImage || videoObj?.videoImage?.length < 50)){
|
||||
return null
|
||||
}
|
||||
if (
|
||||
hasHash &&
|
||||
(!videoObj?.videoImage || videoObj?.videoImage?.length < 50)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<VideoCardCol
|
||||
|
||||
key={videoObj.id}
|
||||
>
|
||||
|
||||
return (
|
||||
<VideoCardCol key={videoObj.id}>
|
||||
<VideoCard
|
||||
onClick={() => {
|
||||
navigate(`/video/${videoObj.user}/${videoObj.id}`)
|
||||
navigate(`/video/${videoObj.user}/${videoObj.id}`);
|
||||
}}
|
||||
>
|
||||
<ResponsiveImage src={videoObj.videoImage} width={266} height={150}/>
|
||||
>
|
||||
<VideoCardImageContainer
|
||||
width={266}
|
||||
height={150}
|
||||
videoImage={videoObj.videoImage}
|
||||
frameImages={videoObj?.extracts || []}
|
||||
/>
|
||||
<VideoCardTitle>{videoObj.title}</VideoCardTitle>
|
||||
<BottomParent>
|
||||
<NameContainer>
|
||||
<Avatar sx={{height: 24, width: 24}} src={`/arbitrary/THUMBNAIL/${videoObj?.user}/qortal_avatar`} alt={`${videoObj.user}'s avatar`} />
|
||||
<VideoCardName>{videoObj.user}</VideoCardName>
|
||||
</NameContainer>
|
||||
|
||||
{videoObj?.created && (
|
||||
<VideoUploadDate>{formatDate(videoObj.created)}</VideoUploadDate>
|
||||
)}
|
||||
|
||||
<NameContainer>
|
||||
<Avatar
|
||||
sx={{ height: 24, width: 24 }}
|
||||
src={`/arbitrary/THUMBNAIL/${videoObj?.user}/qortal_avatar`}
|
||||
alt={`${videoObj.user}'s avatar`}
|
||||
/>
|
||||
<VideoCardName>{videoObj.user}</VideoCardName>
|
||||
</NameContainer>
|
||||
|
||||
{videoObj?.created && (
|
||||
<VideoUploadDate>
|
||||
{formatDate(videoObj.created)}
|
||||
</VideoUploadDate>
|
||||
)}
|
||||
</BottomParent>
|
||||
</VideoCard>
|
||||
|
||||
|
||||
</VideoCardCol>
|
||||
)
|
||||
})}
|
||||
</VideoCardCol>
|
||||
);
|
||||
})}
|
||||
</VideoCardContainer>
|
||||
<LazyLoad onLoadMore={getVideosHandler} isLoading={isLoading}></LazyLoad>
|
||||
</Box>
|
||||
<LazyLoad
|
||||
onLoadMore={getVideosHandler}
|
||||
isLoading={isLoading}
|
||||
></LazyLoad>
|
||||
</Box>
|
||||
</ProductManagerRow>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
);
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user