mirror of
https://github.com/Qortal/q-tube.git
synced 2025-02-11 17:55:51 +00:00
Refactor to VideoContent.tsx, PlaylistContent.tsx, and VideoListComponentLevel.tsx to use customhooks. This reuses a lot of code between them.
ChannelActions.tsx and VideoActionsBar.tsx components created to reduce code reuse between VideoContent.tsx, PlaylistContent.tsx, and VideoListComponentLevel.tsx. If a playlist is empty, comments and buttons no longer appear, the page just shows text explaining the situation.
This commit is contained in:
parent
d42b512caa
commit
d3e35ddbdc
@ -20,7 +20,7 @@ import {
|
||||
extractSigValue,
|
||||
getPaymentInfo,
|
||||
isTimestampWithinRange,
|
||||
} from "../../../pages/ContentPages/VideoContent/VideoContent-State.tsx";
|
||||
} from "../../../pages/ContentPages/VideoContent/VideoContent-State.ts";
|
||||
import { RootState } from "../../../state/store";
|
||||
import NotificationsIcon from "@mui/icons-material/Notifications";
|
||||
import { formatDate } from "../../../utils/time";
|
||||
|
@ -35,24 +35,11 @@ export const useFetchVideos = () => {
|
||||
(state: RootState) => state.video.hashMapVideos
|
||||
);
|
||||
const videos = useSelector((state: RootState) => state.video.videos);
|
||||
const userAvatarHash = useSelector(
|
||||
(state: RootState) => state.global.userAvatarHash
|
||||
);
|
||||
const totalVideosPublished = useSelector(
|
||||
(state: RootState) => state.global.totalVideosPublished
|
||||
);
|
||||
const totalNamesPublished = useSelector(
|
||||
(state: RootState) => state.global.totalNamesPublished
|
||||
);
|
||||
const videosPerNamePublished = useSelector(
|
||||
(state: RootState) => state.global.videosPerNamePublished
|
||||
);
|
||||
|
||||
const filteredVideos = useSelector(
|
||||
(state: RootState) => state.video.filteredVideos
|
||||
);
|
||||
|
||||
const videoReducer = useSelector((state: RootState) => state.video);
|
||||
|
||||
const checkAndUpdateVideo = React.useCallback(
|
||||
(video: Video) => {
|
||||
const existingVideo = hashMapVideos[video.id + "-" + video.user];
|
||||
@ -71,26 +58,6 @@ export const useFetchVideos = () => {
|
||||
[hashMapVideos]
|
||||
);
|
||||
|
||||
const getAvatar = React.useCallback(async (author: string) => {
|
||||
try {
|
||||
const url = await qortalRequest({
|
||||
action: "GET_QDN_RESOURCE_URL",
|
||||
name: author,
|
||||
service: "THUMBNAIL",
|
||||
identifier: "qortal_avatar",
|
||||
});
|
||||
|
||||
dispatch(
|
||||
setUserAvatarHash({
|
||||
name: author,
|
||||
url,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const getVideo = async (
|
||||
user: string,
|
||||
videoId: string,
|
||||
@ -123,17 +90,6 @@ export const useFetchVideos = () => {
|
||||
try {
|
||||
dispatch(setIsLoadingGlobal(true));
|
||||
|
||||
// const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QTUBE_VIDEO_BASE}&limit=20&includemetadata=false&reverse=true&exc
|
||||
// ludeblocked=true&exactmatchnames=true`;
|
||||
//
|
||||
// const response = await fetch(url, {
|
||||
// method: "GET",
|
||||
// headers: {
|
||||
// "Content-Type": "application/json",
|
||||
// },
|
||||
// });
|
||||
// const responseData = await response.json();
|
||||
|
||||
const responseData = await qortalRequest({
|
||||
action: "SEARCH_QDN_RESOURCES",
|
||||
mode: "ALL",
|
||||
@ -235,7 +191,7 @@ export const useFetchVideos = () => {
|
||||
const videoLimit = limit || 20;
|
||||
|
||||
let defaultUrl = `/arbitrary/resources/search?mode=ALL&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true&offset=${offset}&limit=${videoLimit}`;
|
||||
|
||||
|
||||
if (name) {
|
||||
defaultUrl = defaultUrl + `&name=${name}`;
|
||||
} else if (videoListType === "subscriptions") {
|
||||
|
207
src/pages/ContentPages/PlaylistContent/PlaylistContent-State.ts
Normal file
207
src/pages/ContentPages/PlaylistContent/PlaylistContent-State.ts
Normal file
@ -0,0 +1,207 @@
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { setIsLoadingGlobal } from "../../../state/features/globalSlice.ts";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import { useVideoContentState } from "../VideoContent/VideoContent-State.ts";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
export const usePlaylistContentState = () => {
|
||||
const {
|
||||
channelName,
|
||||
id,
|
||||
videoData,
|
||||
superLikeList,
|
||||
getVideoData,
|
||||
setVideoData,
|
||||
videoReference,
|
||||
focusVideo,
|
||||
videoCover,
|
||||
containerRef,
|
||||
theme,
|
||||
descriptionHeight,
|
||||
setSuperLikeList,
|
||||
isExpandedDescription,
|
||||
setIsExpandedDescription,
|
||||
contentRef,
|
||||
descriptionThreshold,
|
||||
loadingSuperLikes,
|
||||
} = useVideoContentState();
|
||||
|
||||
const userName = useSelector((state: RootState) => state.auth.user?.name);
|
||||
|
||||
const [doAutoPlay, setDoAutoPlay] = useState(false);
|
||||
|
||||
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 navigate = useNavigate();
|
||||
const [playlistData, setPlaylistData] = useState<any>(null);
|
||||
|
||||
const hashMapVideos = useSelector(
|
||||
(state: RootState) => state.video.hashMapVideos
|
||||
);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const checkforPlaylist = 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) {
|
||||
const resourceData2 = responseDataSearchVid[0];
|
||||
videos.push(resourceData2);
|
||||
}
|
||||
}
|
||||
}
|
||||
combinedData.videos = videos;
|
||||
setPlaylistData(combinedData);
|
||||
if (combinedData?.videos?.length > 0) {
|
||||
const vid = combinedData?.videos[0];
|
||||
const fullId = vid ? `${vid.identifier}-${vid.name}` : undefined;
|
||||
const existingVideo = hashMapVideos[fullId];
|
||||
|
||||
if (existingVideo) setVideoData(existingVideo);
|
||||
else getVideoData(vid?.name, vid?.identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
dispatch(setIsLoadingGlobal(false));
|
||||
}
|
||||
},
|
||||
[hashMapVideos]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (channelName && id) {
|
||||
checkforPlaylist(channelName, id);
|
||||
}
|
||||
}, [id, channelName]);
|
||||
|
||||
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]);
|
||||
|
||||
return {
|
||||
channelName,
|
||||
id,
|
||||
videoData,
|
||||
superLikeList,
|
||||
getVideoData,
|
||||
setVideoData,
|
||||
videoReference,
|
||||
focusVideo,
|
||||
videoCover,
|
||||
containerRef,
|
||||
theme,
|
||||
descriptionHeight,
|
||||
nextVideo,
|
||||
onEndVideo,
|
||||
doAutoPlay,
|
||||
playlistData,
|
||||
navigate,
|
||||
userName,
|
||||
numberOfSuperlikes,
|
||||
calculateAmountSuperlike,
|
||||
setSuperLikeList,
|
||||
isExpandedDescription,
|
||||
setIsExpandedDescription,
|
||||
contentRef,
|
||||
descriptionThreshold,
|
||||
loadingSuperLikes,
|
||||
};
|
||||
};
|
@ -1,433 +1,55 @@
|
||||
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.ts";
|
||||
import { Avatar, Box, Typography, useTheme } from "@mui/material";
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import React from "react";
|
||||
import { CommentSection } from "../../../components/common/Comments/CommentSection.tsx";
|
||||
import { SuperLikesSection } from "../../../components/common/SuperLikesList/SuperLikesSection.tsx";
|
||||
import { DisplayHtml } from "../../../components/common/TextEditor/DisplayHtml.tsx";
|
||||
import { VideoPlayer } from "../../../components/common/VideoPlayer/VideoPlayer.tsx";
|
||||
import { Playlists } from "../../../components/Playlists/Playlists.tsx";
|
||||
import { formatDate } from "../../../utils/time.ts";
|
||||
import { VideoActionsBar } from "../VideoContent/VideoActionsBar.tsx";
|
||||
import { usePlaylistContentState } from "./PlaylistContent-State.ts";
|
||||
import {
|
||||
refType,
|
||||
VideoPlayer,
|
||||
VideoStyles,
|
||||
} from "../../../components/common/VideoPlayer/VideoPlayer.tsx";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import { addToHashMap } from "../../../state/features/videoSlice.ts";
|
||||
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-State.tsx";
|
||||
import {
|
||||
AuthorTextComment,
|
||||
FileAttachmentContainer,
|
||||
FileAttachmentFont,
|
||||
Spacer,
|
||||
StyledCardColComment,
|
||||
StyledCardHeaderComment,
|
||||
VideoDescription,
|
||||
VideoPlayerContainer,
|
||||
VideoTitle,
|
||||
} from "./PlaylistContent-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 { useFetchSuperLikes } from "../../../hooks/useFetchSuperLikes.tsx";
|
||||
import { SuperLikesSection } from "../../../components/common/SuperLikesList/SuperLikesSection.tsx";
|
||||
import {
|
||||
QTUBE_VIDEO_BASE,
|
||||
SUPER_LIKE_BASE,
|
||||
} from "../../../constants/Identifiers.ts";
|
||||
import { minPriceSuperlike } 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 const PlaylistContent = () => {
|
||||
const { name: channelName, id } = useParams();
|
||||
const userName = useSelector((state: RootState) => state.auth.user?.name);
|
||||
const {
|
||||
channelName,
|
||||
id,
|
||||
videoData,
|
||||
superLikeList,
|
||||
getVideoData,
|
||||
videoReference,
|
||||
focusVideo,
|
||||
videoCover,
|
||||
containerRef,
|
||||
theme,
|
||||
descriptionHeight,
|
||||
nextVideo,
|
||||
onEndVideo,
|
||||
doAutoPlay,
|
||||
playlistData,
|
||||
setSuperLikeList,
|
||||
isExpandedDescription,
|
||||
setIsExpandedDescription,
|
||||
contentRef,
|
||||
descriptionThreshold,
|
||||
loadingSuperLikes,
|
||||
} = usePlaylistContentState();
|
||||
|
||||
const [doAutoPlay, setDoAutoPlay] = useState(false);
|
||||
const [isExpandedDescription, setIsExpandedDescription] =
|
||||
useState<boolean>(false);
|
||||
const [descriptionHeight, setDescriptionHeight] = useState<null | number>(
|
||||
null
|
||||
);
|
||||
const [superlikeList, setSuperlikelist] = useState<any[]>([]);
|
||||
const [loadingSuperLikes, setLoadingSuperLikes] = useState<boolean>(false);
|
||||
const { addSuperlikeRawDataGetToList } = useFetchSuperLikes();
|
||||
const containerRef = useRef<refType>(null);
|
||||
|
||||
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<string>("");
|
||||
|
||||
const getAddressName = async name => {
|
||||
const response = await qortalRequest({
|
||||
action: "GET_NAME_DATA",
|
||||
name: name,
|
||||
});
|
||||
|
||||
if (response?.owner) {
|
||||
setNameAddress(response.owner);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (channelName) {
|
||||
getAddressName(channelName);
|
||||
}
|
||||
}, [channelName]);
|
||||
|
||||
const userAvatarHash = useSelector(
|
||||
(state: RootState) => state.global.userAvatarHash
|
||||
);
|
||||
const contentRef = useRef(null);
|
||||
|
||||
const avatarUrl = useMemo(() => {
|
||||
let url = "";
|
||||
if (channelName && userAvatarHash[channelName]) {
|
||||
url = userAvatarHash[channelName];
|
||||
}
|
||||
|
||||
return url;
|
||||
}, [userAvatarHash, channelName]);
|
||||
const navigate = useNavigate();
|
||||
const theme = useTheme();
|
||||
|
||||
const [videoData, setVideoData] = useState<any>(null);
|
||||
const [playlistData, setPlaylistData] = useState<any>(null);
|
||||
|
||||
const hashMapVideos = useSelector(
|
||||
(state: RootState) => state.video.hashMapVideos
|
||||
);
|
||||
const videoReference = useMemo(() => {
|
||||
if (!videoData) return null;
|
||||
const { videoReference } = videoData;
|
||||
if (
|
||||
videoReference?.identifier &&
|
||||
videoReference?.name &&
|
||||
videoReference?.service
|
||||
) {
|
||||
return videoReference;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}, [videoData]);
|
||||
|
||||
const videoCover = useMemo(() => {
|
||||
if (!videoData) return null;
|
||||
const { videoImage } = videoData;
|
||||
return videoImage || null;
|
||||
}, [videoData]);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const getVideoData = React.useCallback(async (name: string, id: string) => {
|
||||
try {
|
||||
if (!name || !id) return;
|
||||
dispatch(setIsLoadingGlobal(true));
|
||||
|
||||
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QTUBE_VIDEO_BASE}&limit=1&includemetadata=true&reverse=true&excludeblocked=true&name=${name}&exactmatchnames=true&offset=0&identifier=${id}`;
|
||||
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,
|
||||
user: resourceData.name,
|
||||
videoImage: "",
|
||||
id: resourceData.identifier,
|
||||
};
|
||||
|
||||
const responseData = await qortalRequest({
|
||||
action: "FETCH_QDN_RESOURCE",
|
||||
name: name,
|
||||
service: "DOCUMENT",
|
||||
identifier: id,
|
||||
});
|
||||
|
||||
if (responseData && !responseData.error) {
|
||||
const combinedData = {
|
||||
...resourceData,
|
||||
...responseData,
|
||||
};
|
||||
|
||||
setVideoData(combinedData);
|
||||
dispatch(addToHashMap(combinedData));
|
||||
|
||||
checkforPlaylist(name, id);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
dispatch(setIsLoadingGlobal(false));
|
||||
}
|
||||
}, []);
|
||||
|
||||
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) {
|
||||
const resourceData2 = responseDataSearchVid[0];
|
||||
videos.push(resourceData2);
|
||||
}
|
||||
}
|
||||
}
|
||||
combinedData.videos = videos;
|
||||
setPlaylistData(combinedData);
|
||||
if (combinedData?.videos?.length > 0) {
|
||||
const vid = combinedData?.videos[0];
|
||||
const fullId = vid ? `${vid.identifier}-${vid.name}` : undefined;
|
||||
const existingVideo = hashMapVideos[fullId];
|
||||
|
||||
if (existingVideo) setVideoData(existingVideo);
|
||||
else getVideoData(vid?.name, vid?.identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
dispatch(setIsLoadingGlobal(false));
|
||||
}
|
||||
},
|
||||
[hashMapVideos]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (channelName && id) {
|
||||
checkforPlaylist(channelName, id);
|
||||
}
|
||||
}, [id, channelName]);
|
||||
|
||||
const descriptionThreshold = 200;
|
||||
useEffect(() => {
|
||||
if (contentRef.current) {
|
||||
const height = contentRef.current.offsetHeight;
|
||||
if (height > descriptionThreshold)
|
||||
setDescriptionHeight(descriptionThreshold);
|
||||
}
|
||||
}, [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 {
|
||||
setLoadingSuperLikes(true);
|
||||
|
||||
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 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,
|
||||
});
|
||||
|
||||
comments = [
|
||||
...comments,
|
||||
{
|
||||
...comment,
|
||||
message: "",
|
||||
amount: res.amount,
|
||||
},
|
||||
];
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
setSuperlikelist(comments);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
setLoadingSuperLikes(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!nameAddress || !videoData?.id) return;
|
||||
getComments(videoData?.id, nameAddress);
|
||||
}, [getComments, videoData?.id, nameAddress]);
|
||||
|
||||
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);
|
||||
|
||||
if (target == e.currentTarget || clickOnEmptySpace) {
|
||||
console.log("in correctTarget");
|
||||
const focusRef = containerRef.current?.getContainerRef()?.current;
|
||||
focusRef.focus({ preventScroll: true });
|
||||
}
|
||||
};
|
||||
return (
|
||||
return videoData && videoData?.videos?.length === 0 ? (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
<Typography>This playlist doesn't exist</Typography>
|
||||
</Box>
|
||||
) : (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
@ -443,288 +65,176 @@ export const PlaylistContent = () => {
|
||||
marginBottom: "30px",
|
||||
}}
|
||||
>
|
||||
{videoData && videoData?.videos?.length === 0 ? (
|
||||
<>
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "55vw 35vw",
|
||||
width: "100vw",
|
||||
gap: "3vw",
|
||||
}}
|
||||
>
|
||||
{videoReference && (
|
||||
<Box
|
||||
sx={{
|
||||
aspectRatio: "16/9",
|
||||
}}
|
||||
>
|
||||
<VideoPlayer
|
||||
name={videoReference?.name}
|
||||
service={videoReference?.service}
|
||||
identifier={videoReference?.identifier}
|
||||
user={channelName}
|
||||
jsonId={id}
|
||||
poster={videoCover || ""}
|
||||
nextVideo={nextVideo}
|
||||
onEnd={onEndVideo}
|
||||
autoPlay={doAutoPlay}
|
||||
ref={containerRef}
|
||||
videoStyles={{
|
||||
videoContainer: { aspectRatio: "16 / 9" },
|
||||
video: { aspectRatio: "16 / 9" },
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{playlistData && (
|
||||
<Playlists
|
||||
playlistData={playlistData}
|
||||
currentVideoIdentifier={videoData?.id}
|
||||
onClick={getVideoData}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<VideoActionsBar
|
||||
channelName={channelName}
|
||||
videoData={videoData}
|
||||
videoReference={videoReference}
|
||||
superLikeList={superLikeList}
|
||||
setSuperLikeList={setSuperLikeList}
|
||||
sx={{ width: "100%" }}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
marginTop: "10px",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<VideoTitle
|
||||
variant="h1"
|
||||
color="textPrimary"
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{videoData?.title}
|
||||
</VideoTitle>
|
||||
</Box>
|
||||
|
||||
{videoData?.created && (
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
}}
|
||||
color={theme.palette.text.primary}
|
||||
>
|
||||
{formatDate(videoData.created)}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
<Spacer height="30px" />
|
||||
{videoData?.fullDescription && (
|
||||
<Box
|
||||
sx={{
|
||||
background: "#333333",
|
||||
borderRadius: "5px",
|
||||
padding: "5px",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
cursor: !descriptionHeight
|
||||
? "default"
|
||||
: isExpandedDescription
|
||||
? "default"
|
||||
: "pointer",
|
||||
position: "relative",
|
||||
}}
|
||||
className={
|
||||
!descriptionHeight
|
||||
? ""
|
||||
: isExpandedDescription
|
||||
? ""
|
||||
: "hover-click"
|
||||
}
|
||||
>
|
||||
<Typography>This playlist is empty</Typography>
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "55vw 35vw",
|
||||
width: "100vw",
|
||||
gap: "3vw",
|
||||
}}
|
||||
>
|
||||
{videoReference && (
|
||||
{descriptionHeight && !isExpandedDescription && (
|
||||
<Box
|
||||
sx={{
|
||||
aspectRatio: "16/9",
|
||||
position: "absolute",
|
||||
top: "0px",
|
||||
right: "0px",
|
||||
left: "0px",
|
||||
bottom: "0px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
if (isExpandedDescription) return;
|
||||
setIsExpandedDescription(true);
|
||||
}}
|
||||
>
|
||||
<VideoPlayer
|
||||
name={videoReference?.name}
|
||||
service={videoReference?.service}
|
||||
identifier={videoReference?.identifier}
|
||||
user={channelName}
|
||||
jsonId={id}
|
||||
poster={videoCover || ""}
|
||||
nextVideo={nextVideo}
|
||||
onEnd={onEndVideo}
|
||||
autoPlay={doAutoPlay}
|
||||
ref={containerRef}
|
||||
videoStyles={{
|
||||
videoContainer: { aspectRatio: "16 / 9" },
|
||||
video: { aspectRatio: "16 / 9" },
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{playlistData && (
|
||||
<Playlists
|
||||
playlistData={playlistData}
|
||||
currentVideoIdentifier={videoData?.id}
|
||||
onClick={getVideoData}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<Spacer height="15px" />
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr 1fr",
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<StyledCardHeaderComment
|
||||
<Box
|
||||
ref={contentRef}
|
||||
sx={{
|
||||
height: !descriptionHeight
|
||||
? "auto"
|
||||
: isExpandedDescription
|
||||
? "auto"
|
||||
: `${descriptionHeight}px`,
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
{videoData?.htmlDescription ? (
|
||||
<DisplayHtml html={videoData?.htmlDescription} />
|
||||
) : (
|
||||
<VideoDescription
|
||||
variant="body1"
|
||||
color="textPrimary"
|
||||
sx={{
|
||||
cursor: "default",
|
||||
}}
|
||||
>
|
||||
{videoData?.fullDescription}
|
||||
</VideoDescription>
|
||||
)}
|
||||
</Box>
|
||||
{descriptionHeight >= descriptionThreshold && (
|
||||
<Typography
|
||||
onClick={() => {
|
||||
setIsExpandedDescription(prev => !prev);
|
||||
}}
|
||||
sx={{
|
||||
"& .MuiCardHeader-content": {
|
||||
overflow: "hidden",
|
||||
},
|
||||
fontWeight: "bold",
|
||||
fontSize: "16px",
|
||||
cursor: "pointer",
|
||||
paddingLeft: "15px",
|
||||
paddingTop: "15px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate(`/channel/${channelName}`);
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
src={`/arbitrary/THUMBNAIL/${channelName}/qortal_avatar`}
|
||||
alt={`${channelName}'s avatar`}
|
||||
/>
|
||||
</Box>
|
||||
<StyledCardColComment>
|
||||
<AuthorTextComment
|
||||
color={
|
||||
theme.palette.mode === "light"
|
||||
? theme.palette.text.secondary
|
||||
: "#d6e8ff"
|
||||
}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate(`/channel/${channelName}`);
|
||||
}}
|
||||
>
|
||||
{channelName}
|
||||
{channelName !== userName && (
|
||||
<>
|
||||
<SubscribeButton
|
||||
subscriberName={channelName}
|
||||
sx={{ marginLeft: "20px" }}
|
||||
/>
|
||||
<FollowButton
|
||||
followerName={channelName}
|
||||
sx={{ marginLeft: "20px" }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</AuthorTextComment>
|
||||
</StyledCardColComment>
|
||||
</StyledCardHeaderComment>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
}}
|
||||
>
|
||||
{videoData && (
|
||||
<>
|
||||
<LikeAndDislike
|
||||
name={videoData?.user}
|
||||
identifier={videoData?.id}
|
||||
/>
|
||||
<SuperLike
|
||||
numberOfSuperlikes={numberOfSuperlikes}
|
||||
totalAmount={calculateAmountSuperlike}
|
||||
name={videoData?.user}
|
||||
service={videoData?.service}
|
||||
identifier={videoData?.id}
|
||||
onSuccess={val => {
|
||||
setSuperlikelist(prev => [val, ...prev]);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<FileAttachmentContainer>
|
||||
<FileAttachmentFont>Save to Disk</FileAttachmentFont>
|
||||
<FileElement
|
||||
fileInfo={{
|
||||
...videoReference,
|
||||
filename:
|
||||
videoData?.filename ||
|
||||
videoData?.title?.slice(0, 20) + ".mp4",
|
||||
mimeType: videoData?.videoType || '"video/mp4',
|
||||
}}
|
||||
title={
|
||||
videoData?.filename || videoData?.title?.slice(0, 20)
|
||||
}
|
||||
customStyles={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
<DownloadIcon />
|
||||
</FileElement>
|
||||
</FileAttachmentContainer>
|
||||
</Box>
|
||||
{isExpandedDescription ? "Show less" : "...more"}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
marginTop: "10px",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<VideoTitle
|
||||
variant="h1"
|
||||
color="textPrimary"
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{videoData?.title}
|
||||
</VideoTitle>
|
||||
</Box>
|
||||
|
||||
{videoData?.created && (
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
}}
|
||||
color={theme.palette.text.primary}
|
||||
>
|
||||
{formatDate(videoData.created)}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
<Spacer height="30px" />
|
||||
{videoData?.fullDescription && (
|
||||
<Box
|
||||
sx={{
|
||||
background: "#333333",
|
||||
borderRadius: "5px",
|
||||
padding: "5px",
|
||||
width: "100%",
|
||||
cursor: !descriptionHeight
|
||||
? "default"
|
||||
: isExpandedDescription
|
||||
? "default"
|
||||
: "pointer",
|
||||
position: "relative",
|
||||
}}
|
||||
className={
|
||||
!descriptionHeight
|
||||
? ""
|
||||
: isExpandedDescription
|
||||
? ""
|
||||
: "hover-click"
|
||||
}
|
||||
>
|
||||
{descriptionHeight && !isExpandedDescription && (
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "0px",
|
||||
right: "0px",
|
||||
left: "0px",
|
||||
bottom: "0px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
if (isExpandedDescription) return;
|
||||
setIsExpandedDescription(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Box
|
||||
ref={contentRef}
|
||||
sx={{
|
||||
height: !descriptionHeight
|
||||
? "auto"
|
||||
: isExpandedDescription
|
||||
? "auto"
|
||||
: `${descriptionHeight}px`,
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
{videoData?.htmlDescription ? (
|
||||
<DisplayHtml html={videoData?.htmlDescription} />
|
||||
) : (
|
||||
<VideoDescription
|
||||
variant="body1"
|
||||
color="textPrimary"
|
||||
sx={{
|
||||
cursor: "default",
|
||||
}}
|
||||
>
|
||||
{videoData?.fullDescription}
|
||||
</VideoDescription>
|
||||
)}
|
||||
</Box>
|
||||
{descriptionHeight >= descriptionThreshold && (
|
||||
<Typography
|
||||
onClick={() => {
|
||||
setIsExpandedDescription(prev => !prev);
|
||||
}}
|
||||
sx={{
|
||||
fontWeight: "bold",
|
||||
fontSize: "16px",
|
||||
cursor: "pointer",
|
||||
paddingLeft: "15px",
|
||||
paddingTop: "15px",
|
||||
}}
|
||||
>
|
||||
{isExpandedDescription ? "Show less" : "...more"}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
)}
|
||||
</>
|
||||
{videoData?.id && videoData?.user && (
|
||||
<SuperLikesSection
|
||||
loadingSuperLikes={loadingSuperLikes}
|
||||
superlikes={superlikeList}
|
||||
superlikes={superLikeList}
|
||||
postId={videoData?.id || ""}
|
||||
postName={videoData?.user || ""}
|
||||
/>
|
||||
|
75
src/pages/ContentPages/VideoContent/ChannelActions.tsx
Normal file
75
src/pages/ContentPages/VideoContent/ChannelActions.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import { Avatar, Box, useTheme } from "@mui/material";
|
||||
import { FollowButton } from "../../../components/common/ContentButtons/FollowButton.tsx";
|
||||
import { SubscribeButton } from "../../../components/common/ContentButtons/SubscribeButton.tsx";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import {
|
||||
AuthorTextComment,
|
||||
StyledCardColComment,
|
||||
StyledCardHeaderComment,
|
||||
} from "./VideoContent-styles.tsx";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
export interface ChannelActionsParams {
|
||||
channelName: string;
|
||||
}
|
||||
export const ChannelActions = ({ channelName }: ChannelActionsParams) => {
|
||||
const navigate = useNavigate();
|
||||
const theme = useTheme();
|
||||
const userName = useSelector((state: RootState) => state.auth.user?.name);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<StyledCardHeaderComment
|
||||
sx={{
|
||||
"& .MuiCardHeader-content": {
|
||||
overflow: "hidden",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate(`/channel/${channelName}`);
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
src={`/arbitrary/THUMBNAIL/${channelName}/qortal_avatar`}
|
||||
alt={`${channelName}'s avatar`}
|
||||
/>
|
||||
</Box>
|
||||
<StyledCardColComment>
|
||||
<AuthorTextComment
|
||||
color={
|
||||
theme.palette.mode === "light"
|
||||
? theme.palette.text.secondary
|
||||
: "#d6e8ff"
|
||||
}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate(`/channel/${channelName}`);
|
||||
}}
|
||||
>
|
||||
{channelName}
|
||||
{channelName !== userName && (
|
||||
<>
|
||||
<SubscribeButton
|
||||
subscriberName={channelName}
|
||||
sx={{ marginLeft: "20px" }}
|
||||
/>
|
||||
<FollowButton
|
||||
followerName={channelName}
|
||||
sx={{ marginLeft: "20px" }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</AuthorTextComment>
|
||||
</StyledCardColComment>
|
||||
</StyledCardHeaderComment>
|
||||
</Box>
|
||||
);
|
||||
};
|
136
src/pages/ContentPages/VideoContent/VideoActionsBar.tsx
Normal file
136
src/pages/ContentPages/VideoContent/VideoActionsBar.tsx
Normal file
@ -0,0 +1,136 @@
|
||||
import { Avatar, Box, SxProps, Theme, useTheme } from "@mui/material";
|
||||
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 { refType } from "../../../components/common/VideoPlayer/VideoPlayer.tsx";
|
||||
import { titleFormatterOnSave } from "../../../constants/Misc.ts";
|
||||
import { useFetchSuperLikes } from "../../../hooks/useFetchSuperLikes.tsx";
|
||||
import DownloadIcon from "@mui/icons-material/Download";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import { ChannelActions } from "./ChannelActions.tsx";
|
||||
import {
|
||||
AuthorTextComment,
|
||||
FileAttachmentContainer,
|
||||
FileAttachmentFont,
|
||||
StyledCardColComment,
|
||||
StyledCardHeaderComment,
|
||||
} from "./VideoContent-styles.tsx";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
export interface VideoActionsBarProps {
|
||||
channelName: string;
|
||||
videoData: any;
|
||||
videoReference: any;
|
||||
superLikeList: any[];
|
||||
setSuperLikeList: React.Dispatch<React.SetStateAction<any[]>>;
|
||||
sx?: SxProps<Theme>;
|
||||
}
|
||||
|
||||
export const VideoActionsBar = ({
|
||||
channelName,
|
||||
videoData,
|
||||
videoReference,
|
||||
superLikeList,
|
||||
setSuperLikeList,
|
||||
sx,
|
||||
}: VideoActionsBarProps) => {
|
||||
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 saveAsFilename = useMemo(() => {
|
||||
// nb. we prefer to construct the local filename to use for
|
||||
// saving, from the video "title" when possible
|
||||
if (videoData?.title) {
|
||||
// figure out filename extension
|
||||
let ext = ".mp4";
|
||||
if (videoData?.filename) {
|
||||
// nb. this regex copied from https://stackoverflow.com/a/680982
|
||||
const re = /(?:\.([^.]+))?$/;
|
||||
const match = re.exec(videoData.filename);
|
||||
if (match[1]) {
|
||||
ext = "." + match[1];
|
||||
}
|
||||
}
|
||||
|
||||
return (videoData.title + ext).replace(titleFormatterOnSave, "");
|
||||
}
|
||||
|
||||
// otherwise use QDN filename if applicable
|
||||
if (videoData?.filename) {
|
||||
return videoData.filename.replace(titleFormatterOnSave, "");
|
||||
}
|
||||
|
||||
// TODO: this was the previous value, leaving here as the
|
||||
// fallback for now even though it probably is not needed..?
|
||||
return videoData?.filename || videoData?.title?.slice(0, 20) + ".mp4";
|
||||
}, [videoData]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "80%",
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr 1fr",
|
||||
marginTop: "15px",
|
||||
...sx,
|
||||
}}
|
||||
>
|
||||
<ChannelActions channelName={channelName} />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
}}
|
||||
>
|
||||
{videoData && (
|
||||
<>
|
||||
<LikeAndDislike name={videoData?.user} identifier={videoData?.id} />
|
||||
<SuperLike
|
||||
numberOfSuperlikes={numberOfSuperlikes}
|
||||
totalAmount={calculateAmountSuperlike}
|
||||
name={videoData?.user}
|
||||
service={videoData?.service}
|
||||
identifier={videoData?.id}
|
||||
onSuccess={val => {
|
||||
setSuperLikeList(prev => [val, ...prev]);
|
||||
}}
|
||||
/>
|
||||
|
||||
<FileAttachmentContainer>
|
||||
<FileAttachmentFont>Save to Disk</FileAttachmentFont>
|
||||
<FileElement
|
||||
fileInfo={{
|
||||
...videoReference,
|
||||
filename: saveAsFilename,
|
||||
mimeType: videoData?.videoType || '"video/mp4',
|
||||
}}
|
||||
title={videoData?.filename || videoData?.title?.slice(0, 20)}
|
||||
customStyles={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
<DownloadIcon />
|
||||
</FileElement>
|
||||
</FileAttachmentContainer>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -25,28 +25,10 @@ import { useNavigate, useParams } from "react-router-dom";
|
||||
export const useVideoContentState = () => {
|
||||
const { name: channelName, id } = useParams();
|
||||
|
||||
const userName = useSelector((state: RootState) => state.auth.user?.name);
|
||||
|
||||
const [isExpandedDescription, setIsExpandedDescription] =
|
||||
useState<boolean>(false);
|
||||
const [superlikeList, setSuperlikelist] = useState<any[]>([]);
|
||||
const [loadingSuperLikes, setLoadingSuperLikes] = useState<boolean>(false);
|
||||
|
||||
const { addSuperlikeRawDataGetToList } = useFetchSuperLikes();
|
||||
const containerRef = useRef<refType>(null);
|
||||
|
||||
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<string>("");
|
||||
const [descriptionHeight, setDescriptionHeight] = useState<null | number>(
|
||||
null
|
||||
@ -57,6 +39,9 @@ export const useVideoContentState = () => {
|
||||
const userAvatarHash = useSelector(
|
||||
(state: RootState) => state.global.userAvatarHash
|
||||
);
|
||||
const [loadingSuperLikes, setLoadingSuperLikes] = useState<boolean>(false);
|
||||
const [superLikeList, setSuperLikeList] = useState<any[]>([]);
|
||||
const { addSuperlikeRawDataGetToList } = useFetchSuperLikes();
|
||||
|
||||
const contentRef = useRef(null);
|
||||
|
||||
@ -87,34 +72,6 @@ export const useVideoContentState = () => {
|
||||
const navigate = useNavigate();
|
||||
const theme = useTheme();
|
||||
|
||||
const saveAsFilename = useMemo(() => {
|
||||
// nb. we prefer to construct the local filename to use for
|
||||
// saving, from the video "title" when possible
|
||||
if (videoData?.title) {
|
||||
// figure out filename extension
|
||||
let ext = ".mp4";
|
||||
if (videoData?.filename) {
|
||||
// nb. this regex copied from https://stackoverflow.com/a/680982
|
||||
const re = /(?:\.([^.]+))?$/;
|
||||
const match = re.exec(videoData.filename);
|
||||
if (match[1]) {
|
||||
ext = "." + match[1];
|
||||
}
|
||||
}
|
||||
|
||||
return (videoData.title + ext).replace(titleFormatterOnSave, "");
|
||||
}
|
||||
|
||||
// otherwise use QDN filename if applicable
|
||||
if (videoData?.filename) {
|
||||
return videoData.filename.replace(titleFormatterOnSave, "");
|
||||
}
|
||||
|
||||
// TODO: this was the previous value, leaving here as the
|
||||
// fallback for now even though it probably is not needed..?
|
||||
return videoData?.filename || videoData?.title?.slice(0, 20) + ".mp4";
|
||||
}, [videoData]);
|
||||
|
||||
const hashMapVideos = useSelector(
|
||||
(state: RootState) => state.video.hashMapVideos
|
||||
);
|
||||
@ -140,7 +97,7 @@ export const useVideoContentState = () => {
|
||||
}, [videoData]);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const getVideoData = React.useCallback(async (name: string, id: string) => {
|
||||
const getVideoData = useCallback(async (name: string, id: string) => {
|
||||
try {
|
||||
if (!name || !id) return;
|
||||
dispatch(setIsLoadingGlobal(true));
|
||||
@ -193,7 +150,7 @@ export const useVideoContentState = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (channelName && id) {
|
||||
const existingVideo = hashMapVideos[id + "-" + channelName];
|
||||
|
||||
@ -267,7 +224,7 @@ export const useVideoContentState = () => {
|
||||
}
|
||||
}
|
||||
|
||||
setSuperlikelist(comments);
|
||||
setSuperLikeList(comments);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
@ -316,19 +273,18 @@ export const useVideoContentState = () => {
|
||||
isVideoLoaded,
|
||||
navigate,
|
||||
theme,
|
||||
userName,
|
||||
videoData,
|
||||
numberOfSuperlikes,
|
||||
calculateAmountSuperlike,
|
||||
setSuperlikelist,
|
||||
saveAsFilename,
|
||||
descriptionHeight,
|
||||
isExpandedDescription,
|
||||
setIsExpandedDescription,
|
||||
contentRef,
|
||||
descriptionThreshold,
|
||||
loadingSuperLikes,
|
||||
superlikeList,
|
||||
superLikeList,
|
||||
setSuperLikeList,
|
||||
getComments,
|
||||
getVideoData,
|
||||
setVideoData,
|
||||
};
|
||||
};
|
||||
|
@ -1,14 +1,8 @@
|
||||
import DownloadIcon from "@mui/icons-material/Download";
|
||||
import { Avatar, Box, Typography, useTheme } from "@mui/material";
|
||||
import React from "react";
|
||||
|
||||
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 {
|
||||
@ -28,12 +22,13 @@ import { setIsLoadingGlobal } from "../../../state/features/globalSlice.ts";
|
||||
import { addToHashMap } from "../../../state/features/videoSlice.ts";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import { formatDate } from "../../../utils/time.ts";
|
||||
import { VideoActionsBar } from "./VideoActionsBar.tsx";
|
||||
import {
|
||||
extractSigValue,
|
||||
getPaymentInfo,
|
||||
isTimestampWithinRange,
|
||||
useVideoContentState,
|
||||
} from "./VideoContent-State.tsx";
|
||||
} from "./VideoContent-State.ts";
|
||||
import {
|
||||
AuthorTextComment,
|
||||
FileAttachmentContainer,
|
||||
@ -56,21 +51,16 @@ export const VideoContent = () => {
|
||||
videoCover,
|
||||
containerRef,
|
||||
isVideoLoaded,
|
||||
navigate,
|
||||
theme,
|
||||
userName,
|
||||
videoData,
|
||||
numberOfSuperlikes,
|
||||
calculateAmountSuperlike,
|
||||
setSuperlikelist,
|
||||
saveAsFilename,
|
||||
descriptionHeight,
|
||||
isExpandedDescription,
|
||||
setIsExpandedDescription,
|
||||
contentRef,
|
||||
descriptionThreshold,
|
||||
loadingSuperLikes,
|
||||
superlikeList,
|
||||
superLikeList,
|
||||
setSuperLikeList,
|
||||
} = useVideoContentState();
|
||||
|
||||
return (
|
||||
@ -111,114 +101,14 @@ export const VideoContent = () => {
|
||||
<Box sx={{ width: "55vw", aspectRatio: "16/9" }}></Box>
|
||||
)}
|
||||
<VideoContentContainer>
|
||||
<Box
|
||||
sx={{
|
||||
width: "80%",
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr 1fr",
|
||||
marginTop: "15px",
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<StyledCardHeaderComment
|
||||
sx={{
|
||||
"& .MuiCardHeader-content": {
|
||||
overflow: "hidden",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate(`/channel/${channelName}`);
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
src={`/arbitrary/THUMBNAIL/${channelName}/qortal_avatar`}
|
||||
alt={`${channelName}'s avatar`}
|
||||
/>
|
||||
</Box>
|
||||
<StyledCardColComment>
|
||||
<AuthorTextComment
|
||||
color={
|
||||
theme.palette.mode === "light"
|
||||
? theme.palette.text.secondary
|
||||
: "#d6e8ff"
|
||||
}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate(`/channel/${channelName}`);
|
||||
}}
|
||||
>
|
||||
{channelName}
|
||||
{channelName !== userName && (
|
||||
<>
|
||||
<SubscribeButton
|
||||
subscriberName={channelName}
|
||||
sx={{ marginLeft: "20px" }}
|
||||
/>
|
||||
<FollowButton
|
||||
followerName={channelName}
|
||||
sx={{ marginLeft: "20px" }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</AuthorTextComment>
|
||||
</StyledCardColComment>
|
||||
</StyledCardHeaderComment>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
}}
|
||||
>
|
||||
{videoData && (
|
||||
<>
|
||||
<LikeAndDislike
|
||||
name={videoData?.user}
|
||||
identifier={videoData?.id}
|
||||
/>
|
||||
<SuperLike
|
||||
numberOfSuperlikes={numberOfSuperlikes}
|
||||
totalAmount={calculateAmountSuperlike}
|
||||
name={videoData?.user}
|
||||
service={videoData?.service}
|
||||
identifier={videoData?.id}
|
||||
onSuccess={val => {
|
||||
setSuperlikelist(prev => [val, ...prev]);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{videoData?.filename && (
|
||||
<FileAttachmentContainer>
|
||||
<FileAttachmentFont>Save to Disk</FileAttachmentFont>
|
||||
<FileElement
|
||||
fileInfo={{
|
||||
...videoReference,
|
||||
filename: saveAsFilename,
|
||||
mimeType: videoData?.videoType || '"video/mp4',
|
||||
}}
|
||||
title={
|
||||
videoData?.filename || videoData?.title?.slice(0, 20)
|
||||
}
|
||||
customStyles={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
<DownloadIcon />
|
||||
</FileElement>
|
||||
</FileAttachmentContainer>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
<VideoActionsBar
|
||||
channelName={channelName}
|
||||
videoData={videoData}
|
||||
setSuperLikeList={setSuperLikeList}
|
||||
superLikeList={superLikeList}
|
||||
videoReference={videoReference}
|
||||
/>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
@ -343,7 +233,7 @@ export const VideoContent = () => {
|
||||
/* eslint-disable-next-line @typescript-eslint/no-empty-function */
|
||||
getMore={() => {}}
|
||||
loadingSuperLikes={loadingSuperLikes}
|
||||
superlikes={superlikeList}
|
||||
superlikes={superLikeList}
|
||||
postId={id || ""}
|
||||
postName={channelName || ""}
|
||||
/>
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
} from "@mui/material";
|
||||
import { StatsData } from "../../../components/StatsData.tsx";
|
||||
import { categories, subCategories } from "../../../constants/Categories.ts";
|
||||
import { useSidebarState } from "./SearchSidebar-State.tsx";
|
||||
import { useSidebarState } from "./SearchSidebar-State.ts";
|
||||
import {
|
||||
FiltersCol,
|
||||
FiltersContainer,
|
||||
|
@ -111,7 +111,7 @@ export const NameContainer = styled(Box)(({ theme }) => ({
|
||||
marginBottom: "2px",
|
||||
}));
|
||||
|
||||
export const ProductManagerRow = styled(Box)(({ theme }) => ({
|
||||
export const VideoManagerRow = styled(Box)(({ theme }) => ({
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr auto",
|
||||
alignItems: "center",
|
||||
|
@ -38,9 +38,6 @@ export const VideoList = ({ videos }: VideoListProps) => {
|
||||
(state: RootState) => state.video.hashMapVideos
|
||||
);
|
||||
|
||||
const userAvatarHash = useSelector(
|
||||
(state: RootState) => state.global.userAvatarHash
|
||||
);
|
||||
const username = useSelector((state: RootState) => state.auth?.user?.name);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
@ -1,36 +1,21 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { Box, useTheme } from "@mui/material";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import { Avatar, Box, Button, Typography, useTheme } from "@mui/material";
|
||||
import { useFetchVideos } from "../../../hooks/useFetchVideos.tsx";
|
||||
import LazyLoad from "../../../components/common/LazyLoad.tsx";
|
||||
import {
|
||||
BottomParent,
|
||||
NameContainer,
|
||||
ProductManagerRow,
|
||||
VideoCard,
|
||||
VideoCardCol,
|
||||
VideoCardContainer,
|
||||
VideoCardName,
|
||||
VideoCardTitle,
|
||||
VideoContainer,
|
||||
VideoUploadDate,
|
||||
} from "./VideoList-styles.tsx";
|
||||
import ResponsiveImage from "../../../components/ResponsiveImage.tsx";
|
||||
import { formatDate, formatTimestampSeconds } from "../../../utils/time.ts";
|
||||
import { Video } from "../../../state/features/videoSlice.ts";
|
||||
import { queue } from "../../../wrappers/GlobalWrapper.tsx";
|
||||
import { VideoCardImageContainer } from "./VideoCardImageContainer.tsx";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { QTUBE_VIDEO_BASE } from "../../../constants/Identifiers.ts";
|
||||
import { useFetchVideos } from "../../../hooks/useFetchVideos.tsx";
|
||||
import { Video } from "../../../state/features/videoSlice.ts";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import { queue } from "../../../wrappers/GlobalWrapper.tsx";
|
||||
import { VideoManagerRow } from "./VideoList-styles.tsx";
|
||||
import VideoList from "./VideoList.tsx";
|
||||
|
||||
interface VideoListProps {
|
||||
mode?: string;
|
||||
}
|
||||
|
||||
export const VideoListComponentLevel = ({ mode }: VideoListProps) => {
|
||||
const { name: paramName } = useParams();
|
||||
const theme = useTheme();
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
|
||||
const firstFetch = useRef(false);
|
||||
const afterFetch = useRef(false);
|
||||
@ -38,18 +23,9 @@ export const VideoListComponentLevel = ({ mode }: VideoListProps) => {
|
||||
(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 { getVideo, checkAndUpdateVideo } = useFetchVideos();
|
||||
|
||||
const getVideos = React.useCallback(async () => {
|
||||
try {
|
||||
@ -98,21 +74,15 @@ export const VideoListComponentLevel = ({ mode }: VideoListProps) => {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
} finally {
|
||||
console.log(error);
|
||||
}
|
||||
}, [videos, hashMapVideos]);
|
||||
|
||||
const getVideosHandler = React.useCallback(async () => {
|
||||
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]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -122,7 +92,7 @@ export const VideoListComponentLevel = ({ mode }: VideoListProps) => {
|
||||
}, [getVideosHandlerMount]);
|
||||
|
||||
return (
|
||||
<ProductManagerRow>
|
||||
<VideoManagerRow>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
@ -131,68 +101,8 @@ export const VideoListComponentLevel = ({ mode }: VideoListProps) => {
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<VideoCardContainer>
|
||||
{videos.map((video: any, index: number) => {
|
||||
const existingVideo = hashMapVideos[video.id + "-" + video.user];
|
||||
let hasHash = false;
|
||||
let videoObj = video;
|
||||
if (existingVideo) {
|
||||
videoObj = existingVideo;
|
||||
hasHash = true;
|
||||
}
|
||||
|
||||
let avatarUrl = "";
|
||||
if (userAvatarHash[videoObj?.user]) {
|
||||
avatarUrl = userAvatarHash[videoObj?.user];
|
||||
}
|
||||
|
||||
if (
|
||||
hasHash &&
|
||||
(!videoObj?.videoImage || videoObj?.videoImage?.length < 50)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<VideoCardCol key={videoObj.id}>
|
||||
<VideoCard
|
||||
onClick={() => {
|
||||
navigate(`/video/${videoObj.user}/${videoObj.id}`);
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
)}
|
||||
</BottomParent>
|
||||
</VideoCard>
|
||||
</VideoCardCol>
|
||||
);
|
||||
})}
|
||||
</VideoCardContainer>
|
||||
<LazyLoad
|
||||
onLoadMore={getVideosHandler}
|
||||
isLoading={isLoading}
|
||||
></LazyLoad>
|
||||
<VideoList videos={videos} />
|
||||
</Box>
|
||||
</ProductManagerRow>
|
||||
</VideoManagerRow>
|
||||
);
|
||||
};
|
||||
|
@ -5,12 +5,9 @@ import React from "react";
|
||||
import LazyLoad from "../../components/common/LazyLoad";
|
||||
import { ListSuperLikeContainer } from "../../components/common/ListSuperLikes/ListSuperLikeContainer.tsx";
|
||||
import { SearchSidebar } from "./Components/SearchSidebar.tsx";
|
||||
import {
|
||||
FiltersCol,
|
||||
ProductManagerRow,
|
||||
} from "./Components/VideoList-styles.tsx";
|
||||
import { FiltersCol, VideoManagerRow } from "./Components/VideoList-styles.tsx";
|
||||
import VideoList from "./Components/VideoList.tsx";
|
||||
import { useHomeState } from "./Home-State.tsx";
|
||||
import { useHomeState } from "./Home-State.ts";
|
||||
import { SubtitleContainer } from "./Home-styles";
|
||||
|
||||
interface HomeProps {
|
||||
@ -32,7 +29,7 @@ export const Home = ({ mode }: HomeProps) => {
|
||||
<Grid container sx={{ width: "100%" }}>
|
||||
<SearchSidebar onSearch={getVideosHandler} />
|
||||
<Grid item xs={12} md={10} lg={7} xl={8} sm={9}>
|
||||
<ProductManagerRow>
|
||||
<VideoManagerRow>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
@ -93,7 +90,7 @@ export const Home = ({ mode }: HomeProps) => {
|
||||
</TabPanel>
|
||||
</TabContext>
|
||||
</Box>
|
||||
</ProductManagerRow>
|
||||
</VideoManagerRow>
|
||||
</Grid>
|
||||
<FiltersCol item xs={0} lg={3} xl={2}>
|
||||
<ListSuperLikeContainer />
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
extractSigValue,
|
||||
getPaymentInfo,
|
||||
isTimestampWithinRange,
|
||||
} from "../pages/ContentPages/VideoContent/VideoContent-State.tsx";
|
||||
} from "../pages/ContentPages/VideoContent/VideoContent-State.ts";
|
||||
|
||||
import { addUser } from "../state/features/authSlice";
|
||||
import NavBar from "../components/layout/Navbar/Navbar";
|
||||
|
Loading…
x
Reference in New Issue
Block a user