mirror of
https://github.com/Qortal/q-tube.git
synced 2025-02-11 17:55:51 +00:00
Follow, Like, and Dislike buttons Added to Video, Playlist, and Channel pages
The Subscribe and Follow buttons no longer appear when the user views their own videos Minimum Superlike amount lowered from 10 to 1 QORT Fixed bug that made searches in the Home page Subscriptions Tab return results from all videos. Fixed bug that prevented filtering by name on Subscriptions Tab Clicking in area around video gives it focus, allowing hotkeys to work, orange border around video when focused is removed Subscription tab doesn't say "You have no subscriptions" while loading videos
This commit is contained in:
parent
10974f68aa
commit
9cf2932d81
@ -8,14 +8,14 @@ import { Provider } from "react-redux";
|
||||
import GlobalWrapper from "./wrappers/GlobalWrapper";
|
||||
import Notification from "./components/common/Notification/Notification";
|
||||
import { Home } from "./pages/Home/Home";
|
||||
import { VideoContent } from "./pages/VideoContent/VideoContent";
|
||||
import { VideoContent } from "./pages/ContentPages/VideoContent/VideoContent";
|
||||
import DownloadWrapper from "./wrappers/DownloadWrapper";
|
||||
import { IndividualProfile } from "./pages/IndividualProfile/IndividualProfile";
|
||||
import { PlaylistContent } from "./pages/PlaylistContent/PlaylistContent";
|
||||
import { IndividualProfile } from "./pages/ContentPages/IndividualProfile/IndividualProfile";
|
||||
import { PlaylistContent } from "./pages/ContentPages/PlaylistContent/PlaylistContent";
|
||||
import { PersistGate } from "redux-persist/integration/react";
|
||||
import { persistStore } from "redux-persist";
|
||||
import { setFilteredSubscriptions } from "./state/features/videoSlice.ts";
|
||||
import { SubscriptionData } from "./components/common/SubscribeButton.tsx";
|
||||
import { SubscriptionData } from "./components/common/ContentButtons/SubscribeButton.tsx";
|
||||
|
||||
export const getUserName = async () => {
|
||||
const account = await qortalRequest({
|
||||
|
@ -3,7 +3,7 @@ import { CardContentContainerComment } from "../common/Comments/Comments-styles"
|
||||
import {
|
||||
CrowdfundSubTitle,
|
||||
CrowdfundSubTitleRow,
|
||||
} from "../PublishVideo/PublishVideo-styles.tsx";
|
||||
} from "../Publish/PublishVideo/PublishVideo-styles.tsx";
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
NewCrowdfundTitle,
|
||||
StyledButton,
|
||||
TimesIcon,
|
||||
} from "./Upload-styles";
|
||||
} from "./Upload-styles.tsx";
|
||||
import {
|
||||
Box,
|
||||
FormControl,
|
||||
@ -30,9 +30,9 @@ import { useDispatch, useSelector } from "react-redux";
|
||||
import AddBoxIcon from "@mui/icons-material/AddBox";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
|
||||
import { setNotification } from "../../state/features/notificationsSlice";
|
||||
import { objectToBase64, uint8ArrayToBase64 } from "../../utils/toBase64";
|
||||
import { RootState } from "../../state/store";
|
||||
import { setNotification } from "../../../state/features/notificationsSlice.ts";
|
||||
import { objectToBase64, uint8ArrayToBase64 } from "../../../utils/toBase64.ts";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import {
|
||||
upsertVideosBeginning,
|
||||
addToHashMap,
|
||||
@ -41,17 +41,17 @@ import {
|
||||
updateVideo,
|
||||
updateInHashMap,
|
||||
setEditPlaylist,
|
||||
} from "../../state/features/videoSlice";
|
||||
import ImageUploader from "../common/ImageUploader";
|
||||
import { categories, subCategories } from "../../constants/Categories.ts";
|
||||
import { Playlists } from "../Playlists/Playlists";
|
||||
import { PlaylistListEdit } from "../PlaylistListEdit/PlaylistListEdit";
|
||||
import { TextEditor } from "../common/TextEditor/TextEditor";
|
||||
import { extractTextFromHTML } from "../common/TextEditor/utils";
|
||||
} from "../../../state/features/videoSlice.ts";
|
||||
import ImageUploader from "../../common/ImageUploader.tsx";
|
||||
import { categories, subCategories } from "../../../constants/Categories.ts";
|
||||
import { Playlists } from "../../Playlists/Playlists.tsx";
|
||||
import { PlaylistListEdit } from "../PlaylistListEdit/PlaylistListEdit.tsx";
|
||||
import { TextEditor } from "../../common/TextEditor/TextEditor.tsx";
|
||||
import { extractTextFromHTML } from "../../common/TextEditor/utils.ts";
|
||||
import {
|
||||
QTUBE_PLAYLIST_BASE,
|
||||
QTUBE_VIDEO_BASE,
|
||||
} from "../../constants/Identifiers.ts";
|
||||
} from "../../../constants/Identifiers.ts";
|
||||
|
||||
const uid = new ShortUniqueId();
|
||||
const shortuid = new ShortUniqueId({ length: 5 });
|
@ -9,10 +9,10 @@ import {
|
||||
Rating,
|
||||
TextField,
|
||||
Typography,
|
||||
Select
|
||||
Select,
|
||||
} from "@mui/material";
|
||||
import AddPhotoAlternateIcon from "@mui/icons-material/AddPhotoAlternate";
|
||||
import { TimesSVG } from "../../assets/svgs/TimesSVG";
|
||||
import { TimesSVG } from "../../../assets/svgs/TimesSVG.tsx";
|
||||
|
||||
export const DoubleLine = styled(Typography)`
|
||||
display: -webkit-box;
|
||||
@ -159,8 +159,6 @@ export const CustomInputField = styled(TextField)(({ theme }) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
|
||||
export const CrowdfundTitle = styled(Typography)(({ theme }) => ({
|
||||
fontFamily: "Copse",
|
||||
letterSpacing: "1px",
|
||||
@ -539,8 +537,8 @@ export const NoReviewsFont = styled(Typography)(({ theme }) => ({
|
||||
|
||||
export const StyledButton = styled(Button)(({ theme }) => ({
|
||||
fontWeight: 600,
|
||||
color: theme.palette.text.primary
|
||||
}))
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const CustomSelect = styled(Select)(({ theme }) => ({
|
||||
fontFamily: "Mulish",
|
||||
@ -549,34 +547,34 @@ export const CustomSelect = styled(Select)(({ theme }) => ({
|
||||
fontWeight: 400,
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
'& .MuiSelect-select': {
|
||||
padding: '12px',
|
||||
"& .MuiSelect-select": {
|
||||
padding: "12px",
|
||||
fontFamily: "Mulish",
|
||||
fontSize: "19px",
|
||||
letterSpacing: "0px",
|
||||
fontWeight: 400,
|
||||
borderRadius: theme.shape.borderRadius, // Match border radius
|
||||
},
|
||||
'&:before': {
|
||||
"&:before": {
|
||||
// Underline style
|
||||
borderBottomColor: theme.palette.mode === "light" ? "#B2BAC2" : "#c9cccf",
|
||||
},
|
||||
'&:after': {
|
||||
"&:after": {
|
||||
// Underline style when focused
|
||||
borderBottomColor: theme.palette.secondary.main,
|
||||
},
|
||||
'& .MuiOutlinedInput-root': {
|
||||
'& fieldset': {
|
||||
"& .MuiOutlinedInput-root": {
|
||||
"& fieldset": {
|
||||
borderColor: "#E0E3E7",
|
||||
},
|
||||
'&:hover fieldset': {
|
||||
"&:hover fieldset": {
|
||||
borderColor: "#B2BAC2",
|
||||
},
|
||||
'&.Mui-focused fieldset': {
|
||||
"&.Mui-focused fieldset": {
|
||||
borderColor: "#6F7E8C",
|
||||
},
|
||||
},
|
||||
'& .MuiInputBase-root': {
|
||||
"& .MuiInputBase-root": {
|
||||
fontFamily: "Mulish",
|
||||
fontSize: "19px",
|
||||
letterSpacing: "0px",
|
@ -9,10 +9,10 @@ import {
|
||||
Rating,
|
||||
TextField,
|
||||
Typography,
|
||||
Select
|
||||
Select,
|
||||
} from "@mui/material";
|
||||
import AddPhotoAlternateIcon from "@mui/icons-material/AddPhotoAlternate";
|
||||
import { TimesSVG } from "../../assets/svgs/TimesSVG";
|
||||
import { TimesSVG } from "../../../assets/svgs/TimesSVG.tsx";
|
||||
|
||||
export const DoubleLine = styled(Typography)`
|
||||
display: -webkit-box;
|
||||
@ -159,8 +159,6 @@ export const CustomInputField = styled(TextField)(({ theme }) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
|
||||
export const CrowdfundTitle = styled(Typography)(({ theme }) => ({
|
||||
fontFamily: "Copse",
|
||||
letterSpacing: "1px",
|
||||
@ -539,8 +537,8 @@ export const NoReviewsFont = styled(Typography)(({ theme }) => ({
|
||||
|
||||
export const StyledButton = styled(Button)(({ theme }) => ({
|
||||
fontWeight: 600,
|
||||
color: theme.palette.text.primary
|
||||
}))
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const CustomSelect = styled(Select)(({ theme }) => ({
|
||||
fontFamily: "Mulish",
|
||||
@ -549,34 +547,34 @@ export const CustomSelect = styled(Select)(({ theme }) => ({
|
||||
fontWeight: 400,
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
'& .MuiSelect-select': {
|
||||
padding: '12px',
|
||||
"& .MuiSelect-select": {
|
||||
padding: "12px",
|
||||
fontFamily: "Mulish",
|
||||
fontSize: "19px",
|
||||
letterSpacing: "0px",
|
||||
fontWeight: 400,
|
||||
borderRadius: theme.shape.borderRadius, // Match border radius
|
||||
},
|
||||
'&:before': {
|
||||
"&:before": {
|
||||
// Underline style
|
||||
borderBottomColor: theme.palette.mode === "light" ? "#B2BAC2" : "#c9cccf",
|
||||
},
|
||||
'&:after': {
|
||||
"&:after": {
|
||||
// Underline style when focused
|
||||
borderBottomColor: theme.palette.secondary.main,
|
||||
},
|
||||
'& .MuiOutlinedInput-root': {
|
||||
'& fieldset': {
|
||||
"& .MuiOutlinedInput-root": {
|
||||
"& fieldset": {
|
||||
borderColor: "#E0E3E7",
|
||||
},
|
||||
'&:hover fieldset': {
|
||||
"&:hover fieldset": {
|
||||
borderColor: "#B2BAC2",
|
||||
},
|
||||
'&.Mui-focused fieldset': {
|
||||
"&.Mui-focused fieldset": {
|
||||
borderColor: "#6F7E8C",
|
||||
},
|
||||
},
|
||||
'& .MuiInputBase-root': {
|
||||
"& .MuiInputBase-root": {
|
||||
fontFamily: "Mulish",
|
||||
fontSize: "19px",
|
||||
letterSpacing: "0px",
|
@ -34,9 +34,9 @@ import { useDispatch, useSelector } from "react-redux";
|
||||
import AddBoxIcon from "@mui/icons-material/AddBox";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
|
||||
import { setNotification } from "../../state/features/notificationsSlice";
|
||||
import { objectToBase64, uint8ArrayToBase64 } from "../../utils/toBase64";
|
||||
import { RootState } from "../../state/store";
|
||||
import { setNotification } from "../../../state/features/notificationsSlice.ts";
|
||||
import { objectToBase64, uint8ArrayToBase64 } from "../../../utils/toBase64.ts";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import {
|
||||
upsertVideosBeginning,
|
||||
addToHashMap,
|
||||
@ -44,16 +44,16 @@ import {
|
||||
setEditVideo,
|
||||
updateVideo,
|
||||
updateInHashMap,
|
||||
} from "../../state/features/videoSlice";
|
||||
import ImageUploader from "../common/ImageUploader";
|
||||
import { categories, subCategories } from "../../constants/Categories.ts";
|
||||
import { MultiplePublish } from "../common/MultiplePublish/MultiplePublishAll";
|
||||
import { TextEditor } from "../common/TextEditor/TextEditor";
|
||||
import { extractTextFromHTML } from "../common/TextEditor/utils";
|
||||
} from "../../../state/features/videoSlice.ts";
|
||||
import ImageUploader from "../../common/ImageUploader.tsx";
|
||||
import { categories, subCategories } from "../../../constants/Categories.ts";
|
||||
import { MultiplePublish } from "../MultiplePublish/MultiplePublishAll.tsx";
|
||||
import { TextEditor } from "../../common/TextEditor/TextEditor.tsx";
|
||||
import { extractTextFromHTML } from "../../common/TextEditor/utils.ts";
|
||||
import { toBase64 } from "../PublishVideo/PublishVideo.tsx";
|
||||
import { FrameExtractor } from "../common/FrameExtractor/FrameExtractor";
|
||||
import { QTUBE_VIDEO_BASE } from "../../constants/Identifiers.ts";
|
||||
import { titleFormatter } from "../../constants/Misc.ts";
|
||||
import { FrameExtractor } from "../../common/FrameExtractor/FrameExtractor.tsx";
|
||||
import { QTUBE_VIDEO_BASE } from "../../../constants/Identifiers.ts";
|
||||
import { titleFormatter } from "../../../constants/Misc.ts";
|
||||
|
||||
const uid = new ShortUniqueId();
|
||||
const shortuid = new ShortUniqueId({ length: 5 });
|
@ -8,8 +8,8 @@ import {
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import React, { useCallback, useEffect, useState, useRef } from "react";
|
||||
import { CircleSVG } from "../../../assets/svgs/CircleSVG";
|
||||
import { EmptyCircleSVG } from "../../../assets/svgs/EmptyCircleSVG";
|
||||
import { CircleSVG } from "../../../assets/svgs/CircleSVG.tsx";
|
||||
import { EmptyCircleSVG } from "../../../assets/svgs/EmptyCircleSVG.tsx";
|
||||
import { styled } from "@mui/system";
|
||||
|
||||
interface Publish {
|
@ -1,5 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { CardContentContainerComment } from "../common/Comments/Comments-styles";
|
||||
import { CardContentContainerComment } from "../../common/Comments/Comments-styles.tsx";
|
||||
import {
|
||||
CrowdfundSubTitle,
|
||||
CrowdfundSubTitleRow,
|
||||
@ -7,11 +7,11 @@ import {
|
||||
import { Box, Button, Input, Typography, useTheme } from "@mui/material";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
|
||||
import { removeVideo } from "../../state/features/videoSlice";
|
||||
import { removeVideo } from "../../../state/features/videoSlice.ts";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../state/store";
|
||||
import { QTUBE_VIDEO_BASE } from "../../constants/Identifiers.ts";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import { QTUBE_VIDEO_BASE } from "../../../constants/Identifiers.ts";
|
||||
export const PlaylistListEdit = ({ playlistData, removeVideo, addVideo }) => {
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
@ -12,7 +12,7 @@ import {
|
||||
Select,
|
||||
} from "@mui/material";
|
||||
import AddPhotoAlternateIcon from "@mui/icons-material/AddPhotoAlternate";
|
||||
import { TimesSVG } from "../../assets/svgs/TimesSVG";
|
||||
import { TimesSVG } from "../../../assets/svgs/TimesSVG.tsx";
|
||||
|
||||
export const DoubleLine = styled(Typography)`
|
||||
display: -webkit-box;
|
@ -37,36 +37,36 @@ import AddBoxIcon from "@mui/icons-material/AddBox";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
|
||||
import { setNotification } from "../../state/features/notificationsSlice";
|
||||
import { objectToBase64, uint8ArrayToBase64 } from "../../utils/toBase64";
|
||||
import { RootState } from "../../state/store";
|
||||
import { setNotification } from "../../../state/features/notificationsSlice.ts";
|
||||
import { objectToBase64, uint8ArrayToBase64 } from "../../../utils/toBase64.ts";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import {
|
||||
upsertVideosBeginning,
|
||||
addToHashMap,
|
||||
upsertVideos,
|
||||
} from "../../state/features/videoSlice";
|
||||
import ImageUploader from "../common/ImageUploader";
|
||||
import { categories, subCategories } from "../../constants/Categories.ts";
|
||||
import { MultiplePublish } from "../common/MultiplePublish/MultiplePublishAll";
|
||||
} from "../../../state/features/videoSlice.ts";
|
||||
import ImageUploader from "../../common/ImageUploader.tsx";
|
||||
import { categories, subCategories } from "../../../constants/Categories.ts";
|
||||
import { MultiplePublish } from "../MultiplePublish/MultiplePublishAll.tsx";
|
||||
import {
|
||||
CrowdfundSubTitle,
|
||||
CrowdfundSubTitleRow,
|
||||
} from "../EditPlaylist/Upload-styles";
|
||||
import { CardContentContainerComment } from "../common/Comments/Comments-styles";
|
||||
import { TextEditor } from "../common/TextEditor/TextEditor";
|
||||
import { extractTextFromHTML } from "../common/TextEditor/utils";
|
||||
} from "../EditPlaylist/Upload-styles.tsx";
|
||||
import { CardContentContainerComment } from "../../common/Comments/Comments-styles.tsx";
|
||||
import { TextEditor } from "../../common/TextEditor/TextEditor.tsx";
|
||||
import { extractTextFromHTML } from "../../common/TextEditor/utils.ts";
|
||||
import {
|
||||
FiltersCheckbox,
|
||||
FiltersRow,
|
||||
FiltersSubContainer,
|
||||
} from "../../pages/Home/VideoList-styles";
|
||||
import { FrameExtractor } from "../common/FrameExtractor/FrameExtractor";
|
||||
} from "../../../pages/Home/VideoList-styles.tsx";
|
||||
import { FrameExtractor } from "../../common/FrameExtractor/FrameExtractor.tsx";
|
||||
import {
|
||||
QTUBE_PLAYLIST_BASE,
|
||||
QTUBE_VIDEO_BASE,
|
||||
} from "../../constants/Identifiers.ts";
|
||||
import { titleFormatter } from "../../constants/Misc.ts";
|
||||
import { getFileName } from "../../utils/stringFunctions.ts";
|
||||
} from "../../../constants/Identifiers.ts";
|
||||
import { titleFormatter } from "../../../constants/Misc.ts";
|
||||
import { getFileName } from "../../../utils/stringFunctions.ts";
|
||||
|
||||
export const toBase64 = (file: File): Promise<string | ArrayBuffer | null> =>
|
||||
new Promise((resolve, reject) => {
|
@ -12,8 +12,6 @@ export const StatsData = () => {
|
||||
width: "100%",
|
||||
padding: "20px 0px",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
borderTop: `1px solid ${theme.palette.background.paper}`,
|
||||
borderRight: `1px solid ${theme.palette.background.paper}`,
|
||||
}));
|
||||
|
||||
const {
|
||||
@ -51,7 +49,10 @@ export const StatsData = () => {
|
||||
</div>
|
||||
<div>
|
||||
Average:{" "}
|
||||
<span style={{ fontWeight: "bold" }}>{videosPerNamePublished}</span>
|
||||
<span style={{ fontWeight: "bold" }}>
|
||||
{videosPerNamePublished > 0 &&
|
||||
Number(videosPerNamePublished).toFixed(0)}
|
||||
</span>
|
||||
</div>
|
||||
</StatsCol>
|
||||
);
|
||||
|
@ -17,7 +17,7 @@ import {
|
||||
import {
|
||||
CrowdfundSubTitle,
|
||||
CrowdfundSubTitleRow,
|
||||
} from "../../PublishVideo/PublishVideo-styles.tsx";
|
||||
} from "../../Publish/PublishVideo/PublishVideo-styles.tsx";
|
||||
import { COMMENT_BASE } from "../../../constants/Identifiers.ts";
|
||||
|
||||
interface CommentSectionProps {
|
||||
|
159
src/components/common/ContentButtons/FollowButton.tsx
Normal file
159
src/components/common/ContentButtons/FollowButton.tsx
Normal file
@ -0,0 +1,159 @@
|
||||
import { Box, Button, ButtonProps } from "@mui/material";
|
||||
import Tooltip, { TooltipProps, tooltipClasses } from "@mui/material/Tooltip";
|
||||
import { MouseEvent, useEffect, useState } from "react";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
interface FollowButtonProps extends ButtonProps {
|
||||
followerName: string;
|
||||
}
|
||||
|
||||
export type FollowData = {
|
||||
userName: string;
|
||||
followerName: string;
|
||||
};
|
||||
|
||||
export const FollowButton = ({ followerName, ...props }: FollowButtonProps) => {
|
||||
const [followingList, setFollowingList] = useState<string[]>([]);
|
||||
const [followingSize, setFollowingSize] = useState<string>("");
|
||||
const [followingItemCount, setFollowingItemCount] = useState<string>("");
|
||||
const isFollowingName = () => {
|
||||
return followingList.includes(followerName);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
qortalRequest({
|
||||
action: "GET_LIST_ITEMS",
|
||||
list_name: "followedNames",
|
||||
}).then(followList => {
|
||||
setFollowingList(followList);
|
||||
});
|
||||
getFollowSize();
|
||||
}, []);
|
||||
|
||||
const followName = () => {
|
||||
if (followingList.includes(followerName) === false) {
|
||||
qortalRequest({
|
||||
action: "ADD_LIST_ITEM",
|
||||
list_name: "followedNames",
|
||||
item: followerName,
|
||||
}).then(response => {
|
||||
if (response === false) console.log("followName failed");
|
||||
else {
|
||||
setFollowingList([...followingList, followerName]);
|
||||
console.log("following Name: ", followerName);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
const unfollowName = () => {
|
||||
if (followingList.includes(followerName)) {
|
||||
qortalRequest({
|
||||
action: "DELETE_LIST_ITEM",
|
||||
list_name: "followedNames",
|
||||
item: followerName,
|
||||
}).then(response => {
|
||||
if (response === false) console.log("unfollowName failed");
|
||||
else {
|
||||
const listWithoutName = followingList.filter(
|
||||
item => followerName !== item
|
||||
);
|
||||
setFollowingList(listWithoutName);
|
||||
console.log("unfollowing Name: ", followerName);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const manageFollow = (e: MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
isFollowingName() ? unfollowName() : followName();
|
||||
};
|
||||
|
||||
const verticalPadding = "3px";
|
||||
const horizontalPadding = "8px";
|
||||
const buttonStyle = {
|
||||
fontSize: "15px",
|
||||
fontWeight: "700",
|
||||
paddingTop: verticalPadding,
|
||||
paddingBottom: verticalPadding,
|
||||
paddingLeft: horizontalPadding,
|
||||
paddingRight: horizontalPadding,
|
||||
borderRadius: 28,
|
||||
color: "white",
|
||||
width: "96px",
|
||||
height: "45px",
|
||||
...props.sx,
|
||||
};
|
||||
|
||||
const getFollowSize = () => {
|
||||
qortalRequest({
|
||||
action: "LIST_QDN_RESOURCES",
|
||||
name: followerName,
|
||||
limit: 0,
|
||||
includeMetadata: false,
|
||||
}).then(publishesList => {
|
||||
let totalSize = 0;
|
||||
let itemsCount = 0;
|
||||
publishesList.map(publish => {
|
||||
totalSize += +publish.size;
|
||||
itemsCount++;
|
||||
});
|
||||
setFollowingSize(formatBytes(totalSize));
|
||||
setFollowingItemCount(itemsCount.toString());
|
||||
});
|
||||
};
|
||||
|
||||
function formatBytes(bytes: number, decimals = 2) {
|
||||
if (!+bytes) return "0 Bytes";
|
||||
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
||||
}
|
||||
|
||||
const TooltipLine = styled("div")(({ theme }) => ({
|
||||
fontSize: "18px",
|
||||
}));
|
||||
|
||||
const tooltipTitle = followingSize && (
|
||||
<>
|
||||
<TooltipLine>
|
||||
Following a name automatically downloads all of its content to your
|
||||
node. The more followers a name has, the faster its content will
|
||||
download for everyone.
|
||||
</TooltipLine>
|
||||
<br />
|
||||
<TooltipLine>{`${followerName}'s Current Download Size: ${followingSize}`}</TooltipLine>
|
||||
<TooltipLine>{`Number of Files: ${followingItemCount}`}</TooltipLine>
|
||||
</>
|
||||
);
|
||||
|
||||
const CustomWidthTooltip = styled(({ className, ...props }: TooltipProps) => (
|
||||
<Tooltip {...props} classes={{ popper: className }} />
|
||||
))({
|
||||
[`& .${tooltipClasses.tooltip}`]: {
|
||||
maxWidth: 600,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<CustomWidthTooltip title={tooltipTitle} placement={"top"} arrow>
|
||||
<Button
|
||||
{...props}
|
||||
variant={"contained"}
|
||||
color="success"
|
||||
sx={buttonStyle}
|
||||
onClick={e => manageFollow(e)}
|
||||
>
|
||||
{isFollowingName() ? "Unfollow" : "Follow"}
|
||||
</Button>
|
||||
</CustomWidthTooltip>
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,66 @@
|
||||
import { fetchResourcesByIdentifier } from "../../../utils/qortalRequestFunctions.ts";
|
||||
import { DISLIKE, LIKE, LikeType, NEUTRAL } from "./LikeAndDislike.tsx";
|
||||
|
||||
export const getCurrentLikeType = async (
|
||||
username: string,
|
||||
likeIdentifier: string
|
||||
): Promise<LikeType> => {
|
||||
try {
|
||||
const response = await qortalRequest({
|
||||
action: "FETCH_QDN_RESOURCE",
|
||||
name: username,
|
||||
service: "CHAIN_COMMENT",
|
||||
identifier: likeIdentifier,
|
||||
});
|
||||
return response?.likeType;
|
||||
} catch (e) {
|
||||
console.log("liketype error: ", e);
|
||||
return NEUTRAL;
|
||||
}
|
||||
};
|
||||
|
||||
type ResourceType = { likeType: LikeType };
|
||||
export type LikesAndDislikes = { likes: number; dislikes: number };
|
||||
const countLikesAndDislikes = (likesAndDislikes: ResourceType[]) => {
|
||||
let totalLikeCount = 0;
|
||||
let totalDislikeCount = 0;
|
||||
likesAndDislikes.map(likeOrDislike => {
|
||||
const likeType = likeOrDislike.likeType;
|
||||
if (likeType === LIKE) totalLikeCount += 1;
|
||||
if (likeType === DISLIKE) totalDislikeCount += 1;
|
||||
});
|
||||
return {
|
||||
likes: totalLikeCount,
|
||||
dislikes: totalDislikeCount,
|
||||
} as LikesAndDislikes;
|
||||
};
|
||||
export const getCurrentLikesAndDislikesCount = async (
|
||||
likeIdentifier: string
|
||||
) => {
|
||||
try {
|
||||
const likesAndDislikes = await fetchResourcesByIdentifier<ResourceType>(
|
||||
"CHAIN_COMMENT",
|
||||
likeIdentifier
|
||||
);
|
||||
return countLikesAndDislikes(likesAndDislikes);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export function formatLikeCount(likeCount: number, decimals = 2) {
|
||||
if (!+likeCount) return "";
|
||||
|
||||
const sigDigits = Math.floor(Math.log10(likeCount) / 3);
|
||||
if (sigDigits < 1) return likeCount.toString();
|
||||
|
||||
const sigDigitSize = 1000;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ["K", "M", "B"];
|
||||
|
||||
const sigDigitsToTheThousands = Math.pow(sigDigitSize, sigDigits);
|
||||
const sigDigitLikeCount = (likeCount / sigDigitsToTheThousands).toFixed(dm);
|
||||
|
||||
return `${sigDigitLikeCount}${sizes[sigDigits - 1] || ""}`;
|
||||
}
|
230
src/components/common/ContentButtons/LikeAndDislike.tsx
Normal file
230
src/components/common/ContentButtons/LikeAndDislike.tsx
Normal file
@ -0,0 +1,230 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import ThumbUpIcon from "@mui/icons-material/ThumbUp";
|
||||
import ThumbDownIcon from "@mui/icons-material/ThumbDown";
|
||||
import ThumbUpOffAltOutlinedIcon from "@mui/icons-material/ThumbUpOffAltOutlined";
|
||||
import ThumbDownOffAltOutlinedIcon from "@mui/icons-material/ThumbDownOffAltOutlined";
|
||||
import { Box, Tooltip } from "@mui/material";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { setNotification } from "../../../state/features/notificationsSlice.ts";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import { objectToBase64 } from "../../../utils/toBase64.ts";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import { FOR, FOR_LIKE, LIKE_BASE } from "../../../constants/Identifiers.ts";
|
||||
import {
|
||||
formatLikeCount,
|
||||
getCurrentLikesAndDislikesCount,
|
||||
getCurrentLikeType,
|
||||
LikesAndDislikes,
|
||||
} from "./LikeAndDislike-functions.ts";
|
||||
|
||||
interface LikeAndDislikeProps {
|
||||
name: string;
|
||||
identifier: string;
|
||||
}
|
||||
export enum LikeType {
|
||||
Like = 1,
|
||||
Neutral = 0,
|
||||
Dislike = -1,
|
||||
}
|
||||
export const LIKE = LikeType.Like;
|
||||
export const DISLIKE = LikeType.Dislike;
|
||||
export const NEUTRAL = LikeType.Neutral;
|
||||
export const LikeAndDislike = ({ name, identifier }: LikeAndDislikeProps) => {
|
||||
const username = useSelector((state: RootState) => state.auth?.user?.name);
|
||||
const dispatch = useDispatch();
|
||||
const [likeCount, setLikeCount] = useState<number>(0);
|
||||
const [dislikeCount, setDislikeCount] = useState<number>(0);
|
||||
const [currentLikeType, setCurrentLikeType] = useState<LikeType>(NEUTRAL);
|
||||
const likeIdentifier = `${LIKE_BASE}${identifier.slice(0, 39)}`;
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
type PromiseReturn = [LikeType, LikesAndDislikes];
|
||||
|
||||
Promise.all([
|
||||
getCurrentLikeType(username, likeIdentifier),
|
||||
getCurrentLikesAndDislikesCount(likeIdentifier),
|
||||
]).then(([likeType, likesAndDislikes]: PromiseReturn) => {
|
||||
setCurrentLikeType(likeType);
|
||||
|
||||
setLikeCount(likesAndDislikes?.likes || 0);
|
||||
setDislikeCount(likesAndDislikes?.dislikes || 0);
|
||||
setIsLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const updateLikeDataState = (newLikeType: LikeType) => {
|
||||
const setSuccessNotification = (msg: string) =>
|
||||
dispatch(
|
||||
setNotification({
|
||||
msg,
|
||||
alertType: "success",
|
||||
})
|
||||
);
|
||||
setCurrentLikeType(newLikeType);
|
||||
switch (newLikeType) {
|
||||
case NEUTRAL:
|
||||
if (currentLikeType === LIKE) {
|
||||
setLikeCount(count => count - 1);
|
||||
setSuccessNotification("Like Removed");
|
||||
} else {
|
||||
setDislikeCount(count => count - 1);
|
||||
setSuccessNotification("Dislike Removed");
|
||||
}
|
||||
|
||||
break;
|
||||
case LIKE:
|
||||
if (currentLikeType === DISLIKE) setDislikeCount(count => count - 1);
|
||||
setLikeCount(count => count + 1);
|
||||
setSuccessNotification("Like Successful");
|
||||
break;
|
||||
case DISLIKE:
|
||||
if (currentLikeType === LIKE) setLikeCount(count => count - 1);
|
||||
setDislikeCount(count => count + 1);
|
||||
setSuccessNotification("Dislike Successful");
|
||||
break;
|
||||
}
|
||||
};
|
||||
function publishLike(chosenLikeType: LikeType) {
|
||||
if (isLoading) {
|
||||
dispatch(
|
||||
setNotification({
|
||||
msg: "Wait for Like Data to load first",
|
||||
alertType: "error",
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
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 (!identifier) throw new Error("Could not retrieve id of video post");
|
||||
|
||||
if (username === name) {
|
||||
dispatch(
|
||||
setNotification({
|
||||
msg: "You cannot send yourself a like",
|
||||
alertType: "error",
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
qortalRequest({
|
||||
action: "GET_NAME_DATA",
|
||||
name: name,
|
||||
}).then(resName => {
|
||||
const address = resName.owner;
|
||||
if (!address)
|
||||
throw new Error("Could not retrieve content creator's address");
|
||||
});
|
||||
|
||||
objectToBase64({
|
||||
likeType: chosenLikeType,
|
||||
}).then(likeToBase64 => {
|
||||
qortalRequest({
|
||||
action: "PUBLISH_QDN_RESOURCE",
|
||||
name: username,
|
||||
service: "CHAIN_COMMENT",
|
||||
data64: likeToBase64,
|
||||
title: "",
|
||||
identifier: likeIdentifier,
|
||||
filename: `like_metadata.json`,
|
||||
}).then(() => {
|
||||
updateLikeDataState(chosenLikeType);
|
||||
});
|
||||
});
|
||||
} catch (error: any) {
|
||||
dispatch(
|
||||
setNotification({
|
||||
msg:
|
||||
error ||
|
||||
error?.error ||
|
||||
error?.message ||
|
||||
"Failed to publish Like or Dislike",
|
||||
alertType: "error",
|
||||
})
|
||||
);
|
||||
throw new Error("Failed to publish Super Like");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "15px",
|
||||
cursor: "pointer",
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<Tooltip title="Like or Dislike Video" placement="top">
|
||||
<Box
|
||||
sx={{
|
||||
padding: "5px",
|
||||
borderRadius: "7px",
|
||||
gap: "5px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
marginRight: "10px",
|
||||
height: "53px",
|
||||
}}
|
||||
>
|
||||
{currentLikeType === LIKE ? (
|
||||
<ThumbUpIcon onClick={() => publishLike(NEUTRAL)} />
|
||||
) : (
|
||||
<ThumbUpOffAltOutlinedIcon onClick={() => publishLike(LIKE)} />
|
||||
)}
|
||||
{likeCount > 0 && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
userSelect: "none",
|
||||
}}
|
||||
>
|
||||
<span style={{ marginRight: "10px", paddingBottom: "4px" }}>
|
||||
{formatLikeCount(likeCount)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{currentLikeType === DISLIKE ? (
|
||||
<ThumbDownIcon
|
||||
onClick={() => publishLike(NEUTRAL)}
|
||||
color={"error"}
|
||||
/>
|
||||
) : (
|
||||
<ThumbDownOffAltOutlinedIcon
|
||||
onClick={() => publishLike(DISLIKE)}
|
||||
color={"error"}
|
||||
/>
|
||||
)}
|
||||
{dislikeCount > 0 && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
userSelect: "none",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
marginRight: "10px",
|
||||
paddingBottom: "4px",
|
||||
color: "red",
|
||||
}}
|
||||
>
|
||||
{formatLikeCount(dislikeCount)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,10 +1,14 @@
|
||||
import { Button, ButtonProps } from "@mui/material";
|
||||
import { Button, ButtonProps, Tooltip } from "@mui/material";
|
||||
import { MouseEvent, useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { RootState } from "../../state/store.ts";
|
||||
import { subscribe, unSubscribe } from "../../state/features/persistSlice.ts";
|
||||
import { setFilteredSubscriptions } from "../../state/features/videoSlice.ts";
|
||||
import { subscriptionListFilter } from "../../App.tsx";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import {
|
||||
subscribe,
|
||||
unSubscribe,
|
||||
} from "../../../state/features/persistSlice.ts";
|
||||
import { setFilteredSubscriptions } from "../../../state/features/videoSlice.ts";
|
||||
import { subscriptionListFilter } from "../../../App.tsx";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
interface SubscribeButtonProps extends ButtonProps {
|
||||
subscriberName: string;
|
||||
@ -89,15 +93,31 @@ export const SubscribeButton = ({
|
||||
height: "45px",
|
||||
...props.sx,
|
||||
};
|
||||
|
||||
const TooltipLine = styled("div")(({ theme }) => ({
|
||||
fontSize: "18px",
|
||||
}));
|
||||
|
||||
const tooltipTitle = (
|
||||
<>
|
||||
<TooltipLine>
|
||||
Subscribing to a name lets you see their content on the Subscriptions
|
||||
tab of the Home Page. This does NOT download any data to your node.
|
||||
</TooltipLine>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<Button
|
||||
{...props}
|
||||
variant={"contained"}
|
||||
color="error"
|
||||
sx={buttonStyle}
|
||||
onClick={e => manageSubscription(e)}
|
||||
>
|
||||
{isSubscribed ? "Unsubscribe" : "Subscribe"}
|
||||
</Button>
|
||||
<Tooltip title={tooltipTitle} placement={"top"} arrow>
|
||||
<Button
|
||||
{...props}
|
||||
variant={"contained"}
|
||||
color="error"
|
||||
sx={buttonStyle}
|
||||
onClick={e => manageSubscription(e)}
|
||||
>
|
||||
{isSubscribed ? "Unsubscribe" : "Subscribe"}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
@ -17,26 +17,25 @@ import {
|
||||
Tooltip,
|
||||
} from "@mui/material";
|
||||
import qortImg from "../../../assets/img/qort.png";
|
||||
import { MultiplePublish } from "../MultiplePublish/MultiplePublishAll";
|
||||
import { MultiplePublish } from "../../Publish/MultiplePublish/MultiplePublishAll.tsx";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { setNotification } from "../../../state/features/notificationsSlice";
|
||||
import { setNotification } from "../../../state/features/notificationsSlice.ts";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import { objectToBase64 } from "../../../utils/toBase64";
|
||||
import { objectToBase64 } from "../../../utils/toBase64.ts";
|
||||
import { minPriceSuperlike } from "../../../constants/Misc.ts";
|
||||
import { CommentInput } from "../Comments/Comments-styles";
|
||||
import { CommentInput } from "../Comments/Comments-styles.tsx";
|
||||
import {
|
||||
CrowdfundActionButton,
|
||||
CrowdfundActionButtonRow,
|
||||
ModalBody,
|
||||
NewCrowdfundTitle,
|
||||
Spacer,
|
||||
} from "../../PublishVideo/PublishVideo-styles.tsx";
|
||||
import { utf8ToBase64 } from "../SuperLikesList/CommentEditor";
|
||||
import { RootState } from "../../../state/store";
|
||||
} from "../../Publish/PublishVideo/PublishVideo-styles.tsx";
|
||||
import { utf8ToBase64 } from "../SuperLikesList/CommentEditor.tsx";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import {
|
||||
FOR,
|
||||
FOR_SUPER_LIKE,
|
||||
QTUBE_VIDEO_BASE,
|
||||
SUPER_LIKE_BASE,
|
||||
} from "../../../constants/Identifiers.ts";
|
||||
import BoundedNumericTextField from "../../../utils/BoundedNumericTextField.tsx";
|
||||
@ -158,7 +157,7 @@ export const SuperLike = ({
|
||||
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 superLikeAmount for a Super like. Each Super like is verified before displaying to make there aren't any non-paid Super likes",
|
||||
"Super likes are a way to support your favorite content creators. Attach a message to the Super like and have your message seen before normal comments. There is a minimum superLikeAmount 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);
|
||||
@ -185,26 +184,16 @@ export const SuperLike = ({
|
||||
|
||||
setIsOpenMultiplePublish(true);
|
||||
} catch (error: any) {
|
||||
let notificationObj: any = null;
|
||||
if (typeof error === "string") {
|
||||
notificationObj = {
|
||||
msg: error || "Failed to publish Super Like",
|
||||
dispatch(
|
||||
setNotification({
|
||||
msg:
|
||||
error ||
|
||||
error?.error ||
|
||||
error?.message ||
|
||||
"Failed to publish Super Like",
|
||||
alertType: "error",
|
||||
};
|
||||
} else if (typeof error?.error === "string") {
|
||||
notificationObj = {
|
||||
msg: error?.error || "Failed to publish Super Like",
|
||||
alertType: "error",
|
||||
};
|
||||
} else {
|
||||
notificationObj = {
|
||||
msg: error?.message || "Failed to publish Super Like",
|
||||
alertType: "error",
|
||||
};
|
||||
}
|
||||
if (!notificationObj) return;
|
||||
dispatch(setNotification(notificationObj));
|
||||
|
||||
})
|
||||
);
|
||||
throw new Error("Failed to publish Super Like");
|
||||
}
|
||||
}
|
||||
@ -239,8 +228,6 @@ export const SuperLike = ({
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
|
||||
|
||||
<Tooltip title="Super Like" placement="top">
|
||||
<Box
|
||||
sx={{
|
||||
@ -250,8 +237,8 @@ export const SuperLike = ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
outline: "1px gold solid",
|
||||
marginRight:'10px',
|
||||
height: '53px',
|
||||
marginRight: "10px",
|
||||
height: "53px",
|
||||
}}
|
||||
>
|
||||
<ThumbUpIcon
|
||||
@ -260,24 +247,30 @@ export const SuperLike = ({
|
||||
}}
|
||||
/>
|
||||
|
||||
{numberOfSuperlikes === 0 ? null : (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center', userSelect: "none"}}>
|
||||
<span style={{marginRight:'10px', paddingBottom:'4px'}}>{numberOfSuperlikes}</span>
|
||||
<img
|
||||
style={{
|
||||
height: "25px",
|
||||
width: "25px",
|
||||
marginRight:'5px',
|
||||
}}
|
||||
src={qortImg}
|
||||
alt={"Qort Icon"}
|
||||
/>
|
||||
{truncateNumber(totalAmount,0)}
|
||||
</div>
|
||||
)}
|
||||
{numberOfSuperlikes === 0 ? null : (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
userSelect: "none",
|
||||
}}
|
||||
>
|
||||
<span style={{ marginRight: "10px", paddingBottom: "4px" }}>
|
||||
{numberOfSuperlikes}
|
||||
</span>
|
||||
<img
|
||||
style={{
|
||||
height: "25px",
|
||||
width: "25px",
|
||||
marginRight: "5px",
|
||||
}}
|
||||
src={qortImg}
|
||||
alt={"Qort Icon"}
|
||||
/>
|
||||
{truncateNumber(totalAmount, 0)}
|
||||
</div>
|
||||
)}
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
@ -301,10 +294,10 @@ export const SuperLike = ({
|
||||
<DialogContent>
|
||||
<Box>
|
||||
<InputLabel htmlFor="standard-adornment-amount">
|
||||
Amount in QORT (min 10 QORT)
|
||||
Amount in QORT (min 1 QORT)
|
||||
</InputLabel>
|
||||
<BoundedNumericTextField
|
||||
minValue={10}
|
||||
minValue={+minPriceSuperlike}
|
||||
initialValue={minPriceSuperlike.toString()}
|
||||
maxValue={numberToInt(+currentBalance)}
|
||||
allowDecimals={false}
|
@ -24,7 +24,7 @@ import {
|
||||
extractSigValue,
|
||||
getPaymentInfo,
|
||||
isTimestampWithinRange,
|
||||
} from "../../../pages/VideoContent/VideoContent";
|
||||
} from "../../../pages/ContentPages/VideoContent/VideoContent";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import localForage from "localforage";
|
||||
import moment from "moment";
|
||||
|
@ -17,7 +17,7 @@ import {
|
||||
import {
|
||||
CrowdfundSubTitle,
|
||||
CrowdfundSubTitleRow,
|
||||
} from "../../PublishVideo/PublishVideo-styles.tsx";
|
||||
} from "../../Publish/PublishVideo/PublishVideo-styles.tsx";
|
||||
import { COMMENT_BASE } from "../../../constants/Identifiers.ts";
|
||||
|
||||
interface CommentSectionProps {
|
||||
|
@ -1,18 +1,19 @@
|
||||
import { styled } from "@mui/system";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
export const VideoContainer = styled(Box)`
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
max-height: 70vh;
|
||||
`;
|
||||
export const VideoContainer = styled(Box)(({ theme }) => ({
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
maxHeight: "70vh",
|
||||
"&:focus": { outline: "none" },
|
||||
}));
|
||||
|
||||
export const VideoElement = styled("video")`
|
||||
width: 100%;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -35,8 +35,8 @@ import {
|
||||
} from "../../../state/features/videoSlice";
|
||||
import { RootState } from "../../../state/store";
|
||||
import { useWindowSize } from "../../../hooks/useWindowSize";
|
||||
import { PublishVideo } from "../../PublishVideo/PublishVideo.tsx";
|
||||
import { StyledButton } from "../../PublishVideo/PublishVideo-styles.tsx";
|
||||
import { PublishVideo } from "../../Publish/PublishVideo/PublishVideo.tsx";
|
||||
import { StyledButton } from "../../Publish/PublishVideo/PublishVideo-styles.tsx";
|
||||
import { Notifications } from "../../common/Notifications/Notifications";
|
||||
interface Props {
|
||||
isAuthenticated: boolean;
|
||||
|
@ -39,6 +39,7 @@ export const categories = [
|
||||
{ id: 24, name: "Anime" },
|
||||
{ id: 25, name: "Cartoons" },
|
||||
{ id: 26, name: "Qortal" },
|
||||
{ id: 99, name: "Other" },
|
||||
].sort(sortCategory);
|
||||
|
||||
export const subCategories: CategoryMap = {
|
||||
@ -59,7 +60,7 @@ export const subCategories: CategoryMap = {
|
||||
{ id: 113, name: "Indie Films" },
|
||||
{ id: 114, name: "International Films" },
|
||||
{ id: 115, name: "Biographies & True Stories" },
|
||||
{ id: 116, name: "Other" },
|
||||
{ id: 199, name: "Other" },
|
||||
].sort(sortCategory),
|
||||
2: [
|
||||
// Series
|
||||
@ -78,14 +79,14 @@ export const subCategories: CategoryMap = {
|
||||
{ id: 213, name: "Anthologies" },
|
||||
{ id: 214, name: "International Series" },
|
||||
{ id: 215, name: "Miniseries" },
|
||||
{ id: 216, name: "Other" },
|
||||
{ id: 299, name: "Other" },
|
||||
].sort(sortCategory),
|
||||
4: [
|
||||
// Education
|
||||
{ id: 400, name: "Tutorial" },
|
||||
{ id: 401, name: "Documentary" },
|
||||
{ id: 401, name: "Qortal" },
|
||||
{ id: 402, name: "Other" },
|
||||
{ id: 402, name: "Documentary" },
|
||||
{ id: 499, name: "Other" },
|
||||
].sort(sortCategory),
|
||||
|
||||
24: [
|
||||
@ -102,6 +103,6 @@ export const subCategories: CategoryMap = {
|
||||
{ id: 2411, name: "Harem" },
|
||||
{ id: 2412, name: "Ecchi" },
|
||||
{ id: 2413, name: "Idol" },
|
||||
{ id: 2414, name: "Other" },
|
||||
{ id: 2499, name: "Other" },
|
||||
].sort(sortCategory),
|
||||
};
|
||||
|
@ -8,8 +8,12 @@ export const QTUBE_PLAYLIST_BASE = useTestIdentifiers
|
||||
export const SUPER_LIKE_BASE = useTestIdentifiers
|
||||
? "MYTEST_superlike_"
|
||||
: "qtube_superlike_";
|
||||
|
||||
export const LIKE_BASE = useTestIdentifiers ? "MYTEST_like_" : "qtube_like_";
|
||||
|
||||
export const COMMENT_BASE = useTestIdentifiers
|
||||
? "qcomment_v1_MYTEST_"
|
||||
: "qcomment_v1_qtube_";
|
||||
export const FOR = useTestIdentifiers ? "FORTEST5" : "FOR0962";
|
||||
export const FOR_SUPER_LIKE = useTestIdentifiers ? "MYTEST_sl" : `qtube_sl`;
|
||||
export const FOR_LIKE = useTestIdentifiers ? "MYTEST_like" : `qtube_like`;
|
||||
|
@ -1,6 +1,3 @@
|
||||
export const minPriceSuperlike = 10;
|
||||
export const minPriceSuperlike = 1;
|
||||
export const titleFormatter = /[^a-zA-Z0-9\s-_!?()&'",.;:|—~@#$%^*+=<>]/g;
|
||||
export const titleFormatterOnSave = /[^a-zA-Z0-9\s-_!()&',.;—~@#$%^+=]/g;
|
||||
|
||||
export const allTabValue = "all";
|
||||
export const subscriptionTabValue = "subscriptions";
|
||||
|
@ -24,9 +24,9 @@ import {
|
||||
QTUBE_PLAYLIST_BASE,
|
||||
QTUBE_VIDEO_BASE,
|
||||
} from "../constants/Identifiers.ts";
|
||||
import { allTabValue, subscriptionTabValue } from "../constants/Misc.ts";
|
||||
import { persistReducer } from "redux-persist";
|
||||
import { subscriptionListFilter } from "../App.tsx";
|
||||
import { ContentType, VideoListType } from "../state/features/persistSlice.ts";
|
||||
|
||||
export const useFetchVideos = () => {
|
||||
const dispatch = useDispatch();
|
||||
@ -194,15 +194,15 @@ export const useFetchVideos = () => {
|
||||
category?: string;
|
||||
subcategory?: string;
|
||||
keywords?: string;
|
||||
type?: string;
|
||||
contentType?: ContentType;
|
||||
};
|
||||
|
||||
const emptyFilters = {
|
||||
const emptyFilters: FilterType = {
|
||||
name: "",
|
||||
category: "",
|
||||
subcategory: "",
|
||||
keywords: "",
|
||||
type: "",
|
||||
contentType: "videos",
|
||||
};
|
||||
const getVideos = React.useCallback(
|
||||
async (
|
||||
@ -210,16 +210,16 @@ export const useFetchVideos = () => {
|
||||
reset?: boolean,
|
||||
resetFilters?: boolean,
|
||||
limit?: number,
|
||||
listType = allTabValue
|
||||
videoListType: VideoListType = "all"
|
||||
) => {
|
||||
emptyFilters.type = filters.type;
|
||||
emptyFilters.contentType = filters.contentType;
|
||||
try {
|
||||
const {
|
||||
name = "",
|
||||
category = "",
|
||||
subcategory = "",
|
||||
keywords = "",
|
||||
type = filters.type,
|
||||
contentType = filters.contentType,
|
||||
}: FilterType = resetFilters ? emptyFilters : filters;
|
||||
let offset = videos.length;
|
||||
if (reset) {
|
||||
@ -231,9 +231,7 @@ export const useFetchVideos = () => {
|
||||
|
||||
if (name) {
|
||||
defaultUrl = defaultUrl + `&name=${name}`;
|
||||
}
|
||||
|
||||
if (listType === subscriptionTabValue) {
|
||||
} else if (videoListType === "subscriptions") {
|
||||
const filteredSubscribeList = await subscriptionListFilter(false);
|
||||
filteredSubscribeList.map(sub => {
|
||||
defaultUrl += `&name=${sub.subscriberName}`;
|
||||
@ -252,7 +250,7 @@ export const useFetchVideos = () => {
|
||||
if (keywords) {
|
||||
defaultUrl = defaultUrl + `&query=${keywords}`;
|
||||
}
|
||||
if (type === "playlists") {
|
||||
if (contentType === "playlists") {
|
||||
defaultUrl = defaultUrl + `&service=PLAYLIST`;
|
||||
defaultUrl = defaultUrl + `&identifier=${QTUBE_PLAYLIST_BASE}`;
|
||||
} else {
|
||||
@ -431,9 +429,7 @@ export const useFetchVideos = () => {
|
||||
const totalVideosPublished = responseData.length;
|
||||
const uniqueNames = new Set(responseData.map(video => video.name));
|
||||
const totalNamesPublished = uniqueNames.size;
|
||||
const videosPerNamePublished = (
|
||||
totalVideosPublished / totalNamesPublished
|
||||
).toFixed(0);
|
||||
const videosPerNamePublished = totalVideosPublished / totalNamesPublished;
|
||||
|
||||
dispatch(setTotalVideosPublished(totalVideosPublished));
|
||||
dispatch(setTotalNamesPublished(totalNamesPublished));
|
||||
|
@ -1,34 +1,37 @@
|
||||
import React, { useMemo } from "react";
|
||||
import { VideoListComponentLevel } from "../Home/VideoListComponentLevel";
|
||||
import { HeaderContainer, ProfileContainer } from "./Profile-styles";
|
||||
import { VideoListComponentLevel } from "../../Home/VideoListComponentLevel.tsx";
|
||||
import { HeaderContainer, ProfileContainer } from "./Profile-styles.tsx";
|
||||
import {
|
||||
AuthorTextComment,
|
||||
StyledCardColComment,
|
||||
StyledCardHeaderComment,
|
||||
} from "../VideoContent/VideoContent-styles";
|
||||
} from "../VideoContent/VideoContent-styles.tsx";
|
||||
import { Avatar, Box, useTheme } from "@mui/material";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useSelector } from "react-redux";
|
||||
import { setUserAvatarHash } from "../../state/features/globalSlice";
|
||||
import { RootState } from "../../state/store";
|
||||
import { SubscribeButton } from "../../components/common/SubscribeButton.tsx";
|
||||
import { setUserAvatarHash } from "../../../state/features/globalSlice.ts";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import { SubscribeButton } from "../../../components/common/ContentButtons/SubscribeButton.tsx";
|
||||
import { FollowButton } from "../../../components/common/ContentButtons/FollowButton.tsx";
|
||||
|
||||
export const IndividualProfile = () => {
|
||||
const { name: paramName } = useParams();
|
||||
const { name: channelName } = useParams();
|
||||
const userName = useSelector((state: RootState) => state.auth.user?.name);
|
||||
|
||||
const userAvatarHash = useSelector(
|
||||
(state: RootState) => state.global.userAvatarHash
|
||||
);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const avatarUrl = useMemo(() => {
|
||||
let url = "";
|
||||
if (paramName && userAvatarHash[paramName]) {
|
||||
url = userAvatarHash[paramName];
|
||||
if (channelName && userAvatarHash[channelName]) {
|
||||
url = userAvatarHash[channelName];
|
||||
}
|
||||
|
||||
return url;
|
||||
}, [userAvatarHash, paramName]);
|
||||
}, [userAvatarHash, channelName]);
|
||||
return (
|
||||
<ProfileContainer>
|
||||
<HeaderContainer>
|
||||
@ -46,8 +49,8 @@ export const IndividualProfile = () => {
|
||||
>
|
||||
<Box>
|
||||
<Avatar
|
||||
src={`/arbitrary/THUMBNAIL/${paramName}/qortal_avatar`}
|
||||
alt={`${paramName}'s avatar`}
|
||||
src={`/arbitrary/THUMBNAIL/${channelName}/qortal_avatar`}
|
||||
alt={`${channelName}'s avatar`}
|
||||
/>
|
||||
</Box>
|
||||
<StyledCardColComment>
|
||||
@ -58,13 +61,21 @@ export const IndividualProfile = () => {
|
||||
: "#d6e8ff"
|
||||
}
|
||||
>
|
||||
{paramName}
|
||||
{channelName}
|
||||
</AuthorTextComment>
|
||||
</StyledCardColComment>
|
||||
<SubscribeButton
|
||||
subscriberName={paramName}
|
||||
sx={{ marginLeft: "10px" }}
|
||||
/>
|
||||
{channelName !== userName && (
|
||||
<>
|
||||
<SubscribeButton
|
||||
subscriberName={channelName}
|
||||
sx={{ marginLeft: "10px" }}
|
||||
/>
|
||||
<FollowButton
|
||||
followerName={channelName}
|
||||
sx={{ marginLeft: "20px" }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</StyledCardHeaderComment>
|
||||
</Box>
|
||||
</HeaderContainer>
|
@ -7,15 +7,18 @@ import React, {
|
||||
} from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { setIsLoadingGlobal } from "../../state/features/globalSlice";
|
||||
import { setIsLoadingGlobal } from "../../../state/features/globalSlice.ts";
|
||||
import { Avatar, Box, Typography, useTheme } from "@mui/material";
|
||||
import { VideoPlayer } from "../../components/common/VideoPlayer/VideoPlayer.tsx";
|
||||
import { RootState } from "../../state/store";
|
||||
import { addToHashMap } from "../../state/features/videoSlice";
|
||||
import {
|
||||
refType,
|
||||
VideoPlayer,
|
||||
} 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 mockImg from "../../../test/mockimg.jpg";
|
||||
import {
|
||||
AuthorTextComment,
|
||||
FileAttachmentContainer,
|
||||
@ -26,39 +29,43 @@ import {
|
||||
VideoDescription,
|
||||
VideoPlayerContainer,
|
||||
VideoTitle,
|
||||
} from "./PlaylistContent-styles";
|
||||
import { setUserAvatarHash } from "../../state/features/globalSlice";
|
||||
} from "./PlaylistContent-styles.tsx";
|
||||
import { setUserAvatarHash } from "../../../state/features/globalSlice.ts";
|
||||
import {
|
||||
formatDate,
|
||||
formatDateSeconds,
|
||||
formatTimestampSeconds,
|
||||
} from "../../utils/time";
|
||||
import { NavbarName } from "../../components/layout/Navbar/Navbar-styles";
|
||||
import { CommentSection } from "../../components/common/Comments/CommentSection";
|
||||
} 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/PublishVideo/PublishVideo-styles.tsx";
|
||||
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";
|
||||
} 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 {
|
||||
extractSigValue,
|
||||
getPaymentInfo,
|
||||
isTimestampWithinRange,
|
||||
} from "../VideoContent/VideoContent";
|
||||
import { SuperLikesSection } from "../../components/common/SuperLikesList/SuperLikesSection";
|
||||
} from "../VideoContent/VideoContent.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/SubscribeButton.tsx";
|
||||
} 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, id } = useParams();
|
||||
const { name: channelName, id } = useParams();
|
||||
const userName = useSelector((state: RootState) => state.auth.user?.name);
|
||||
|
||||
const [doAutoPlay, setDoAutoPlay] = useState(false);
|
||||
const [isExpandedDescription, setIsExpandedDescription] =
|
||||
useState<boolean>(false);
|
||||
@ -68,6 +75,7 @@ export const PlaylistContent = () => {
|
||||
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) => {
|
||||
@ -95,10 +103,10 @@ export const PlaylistContent = () => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (name) {
|
||||
getAddressName(name);
|
||||
if (channelName) {
|
||||
getAddressName(channelName);
|
||||
}
|
||||
}, [name]);
|
||||
}, [channelName]);
|
||||
|
||||
const userAvatarHash = useSelector(
|
||||
(state: RootState) => state.global.userAvatarHash
|
||||
@ -107,12 +115,12 @@ export const PlaylistContent = () => {
|
||||
|
||||
const avatarUrl = useMemo(() => {
|
||||
let url = "";
|
||||
if (name && userAvatarHash[name]) {
|
||||
url = userAvatarHash[name];
|
||||
if (channelName && userAvatarHash[channelName]) {
|
||||
url = userAvatarHash[channelName];
|
||||
}
|
||||
|
||||
return url;
|
||||
}, [userAvatarHash, name]);
|
||||
}, [userAvatarHash, channelName]);
|
||||
const navigate = useNavigate();
|
||||
const theme = useTheme();
|
||||
|
||||
@ -282,10 +290,10 @@ export const PlaylistContent = () => {
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (name && id) {
|
||||
checkforPlaylist(name, id);
|
||||
if (channelName && id) {
|
||||
checkforPlaylist(channelName, id);
|
||||
}
|
||||
}, [id, name]);
|
||||
}, [id, channelName]);
|
||||
|
||||
useEffect(() => {
|
||||
if (contentRef.current) {
|
||||
@ -395,6 +403,14 @@ export const PlaylistContent = () => {
|
||||
getComments(videoData?.id, nameAddress);
|
||||
}, [getComments, videoData?.id, nameAddress]);
|
||||
|
||||
const focusVideo = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const focusRef = containerRef.current?.getContainerRef()?.current;
|
||||
const isCorrectTarget = e.currentTarget == e.target;
|
||||
if (focusRef && isCorrectTarget) {
|
||||
focusRef.focus({ preventScroll: true });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
@ -403,6 +419,7 @@ export const PlaylistContent = () => {
|
||||
flexDirection: "column",
|
||||
padding: "20px 10px",
|
||||
}}
|
||||
onClick={focusVideo}
|
||||
>
|
||||
<VideoPlayerContainer
|
||||
sx={{
|
||||
@ -434,13 +451,14 @@ export const PlaylistContent = () => {
|
||||
name={videoReference?.name}
|
||||
service={videoReference?.service}
|
||||
identifier={videoReference?.identifier}
|
||||
user={name}
|
||||
user={channelName}
|
||||
jsonId={id}
|
||||
poster={videoCover || ""}
|
||||
nextVideo={nextVideo}
|
||||
onEnd={onEndVideo}
|
||||
autoPlay={doAutoPlay}
|
||||
customStyle={{ aspectRatio: "16/9" }}
|
||||
ref={containerRef}
|
||||
/>
|
||||
)}
|
||||
{playlistData && (
|
||||
@ -473,12 +491,12 @@ export const PlaylistContent = () => {
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate(`/channel/${name}`);
|
||||
navigate(`/channel/${channelName}`);
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
src={`/arbitrary/THUMBNAIL/${name}/qortal_avatar`}
|
||||
alt={`${name}'s avatar`}
|
||||
src={`/arbitrary/THUMBNAIL/${channelName}/qortal_avatar`}
|
||||
alt={`${channelName}'s avatar`}
|
||||
/>
|
||||
</Box>
|
||||
<StyledCardColComment>
|
||||
@ -492,14 +510,22 @@ export const PlaylistContent = () => {
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate(`/channel/${name}`);
|
||||
navigate(`/channel/${channelName}`);
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
<SubscribeButton
|
||||
subscriberName={name}
|
||||
sx={{ marginLeft: "20px" }}
|
||||
/>
|
||||
{channelName}
|
||||
{channelName !== userName && (
|
||||
<>
|
||||
<SubscribeButton
|
||||
subscriberName={channelName}
|
||||
sx={{ marginLeft: "20px" }}
|
||||
/>
|
||||
<FollowButton
|
||||
followerName={channelName}
|
||||
sx={{ marginLeft: "20px" }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</AuthorTextComment>
|
||||
</StyledCardColComment>
|
||||
</StyledCardHeaderComment>
|
||||
@ -511,16 +537,22 @@ export const PlaylistContent = () => {
|
||||
}}
|
||||
>
|
||||
{videoData && (
|
||||
<SuperLike
|
||||
numberOfSuperlikes={numberOfSuperlikes}
|
||||
totalAmount={calculateAmountSuperlike}
|
||||
name={videoData?.user}
|
||||
service={videoData?.service}
|
||||
identifier={videoData?.id}
|
||||
onSuccess={val => {
|
||||
setSuperlikelist(prev => [val, ...prev]);
|
||||
}}
|
||||
/>
|
||||
<>
|
||||
<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>
|
||||
@ -677,7 +709,10 @@ export const PlaylistContent = () => {
|
||||
maxWidth: "1200px",
|
||||
}}
|
||||
>
|
||||
<CommentSection postId={videoData?.id || ""} postName={name || ""} />
|
||||
<CommentSection
|
||||
postId={videoData?.id || ""}
|
||||
postName={channelName || ""}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
@ -7,15 +7,18 @@ import React, {
|
||||
} from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { setIsLoadingGlobal } from "../../state/features/globalSlice";
|
||||
import { setIsLoadingGlobal } from "../../../state/features/globalSlice.ts";
|
||||
import { Avatar, Box, Typography, useTheme } from "@mui/material";
|
||||
import { VideoPlayer } from "../../components/common/VideoPlayer/VideoPlayer.tsx";
|
||||
import { RootState } from "../../state/store";
|
||||
import { addToHashMap } from "../../state/features/videoSlice";
|
||||
import {
|
||||
refType,
|
||||
VideoPlayer,
|
||||
} 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 mockImg from "../../../test/mockimg.jpg";
|
||||
import {
|
||||
AuthorTextComment,
|
||||
FileAttachmentContainer,
|
||||
@ -26,37 +29,39 @@ import {
|
||||
VideoDescription,
|
||||
VideoPlayerContainer,
|
||||
VideoTitle,
|
||||
} from "./VideoContent-styles";
|
||||
import { setUserAvatarHash } from "../../state/features/globalSlice";
|
||||
} from "./VideoContent-styles.tsx";
|
||||
import { setUserAvatarHash } from "../../../state/features/globalSlice.ts";
|
||||
import {
|
||||
formatDate,
|
||||
formatDateSeconds,
|
||||
formatTimestampSeconds,
|
||||
} from "../../utils/time";
|
||||
import { NavbarName } from "../../components/layout/Navbar/Navbar-styles";
|
||||
import { CommentSection } from "../../components/common/Comments/CommentSection";
|
||||
} 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/PublishVideo/PublishVideo-styles.tsx";
|
||||
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 { 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";
|
||||
} from "../../../components/Publish/PublishVideo/PublishVideo-styles.tsx";
|
||||
import { Playlists } from "../../../components/Playlists/Playlists.tsx";
|
||||
import { DisplayHtml } from "../../../components/common/TextEditor/DisplayHtml.tsx";
|
||||
import FileElement from "../../../components/common/FileElement.tsx";
|
||||
import { SuperLike } from "../../../components/common/ContentButtons/SuperLike.tsx";
|
||||
import { CommentContainer } from "../../../components/common/Comments/Comments-styles.tsx";
|
||||
import { Comment } from "../../../components/common/Comments/Comment.tsx";
|
||||
import { SuperLikesSection } from "../../../components/common/SuperLikesList/SuperLikesSection.tsx";
|
||||
import { useFetchSuperLikes } from "../../../hooks/useFetchSuperLikes.tsx";
|
||||
import {
|
||||
FOR_SUPER_LIKE,
|
||||
QTUBE_VIDEO_BASE,
|
||||
SUPER_LIKE_BASE,
|
||||
} from "../../constants/Identifiers.ts";
|
||||
} from "../../../constants/Identifiers.ts";
|
||||
import {
|
||||
minPriceSuperlike,
|
||||
titleFormatterOnSave,
|
||||
} from "../../constants/Misc.ts";
|
||||
import { SubscribeButton } from "../../components/common/SubscribeButton.tsx";
|
||||
} from "../../../constants/Misc.ts";
|
||||
import { SubscribeButton } from "../../../components/common/ContentButtons/SubscribeButton.tsx";
|
||||
import { FollowButton } from "../../../components/common/ContentButtons/FollowButton.tsx";
|
||||
import { LikeAndDislike } from "../../../components/common/ContentButtons/LikeAndDislike.tsx";
|
||||
|
||||
export function isTimestampWithinRange(resTimestamp, resCreated) {
|
||||
// Calculate the absolute difference in milliseconds
|
||||
@ -116,12 +121,15 @@ export const getPaymentInfo = async (signature: string) => {
|
||||
};
|
||||
|
||||
export const VideoContent = () => {
|
||||
const { name, id } = useParams();
|
||||
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) => {
|
||||
@ -143,6 +151,7 @@ export const VideoContent = () => {
|
||||
const userAvatarHash = useSelector(
|
||||
(state: RootState) => state.global.userAvatarHash
|
||||
);
|
||||
|
||||
const contentRef = useRef(null);
|
||||
|
||||
const getAddressName = async name => {
|
||||
@ -157,18 +166,18 @@ export const VideoContent = () => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (name) {
|
||||
getAddressName(name);
|
||||
if (channelName) {
|
||||
getAddressName(channelName);
|
||||
}
|
||||
}, [name]);
|
||||
}, [channelName]);
|
||||
const avatarUrl = useMemo(() => {
|
||||
let url = "";
|
||||
if (name && userAvatarHash[name]) {
|
||||
url = userAvatarHash[name];
|
||||
if (channelName && userAvatarHash[channelName]) {
|
||||
url = userAvatarHash[channelName];
|
||||
}
|
||||
|
||||
return url;
|
||||
}, [userAvatarHash, name]);
|
||||
}, [userAvatarHash, channelName]);
|
||||
const navigate = useNavigate();
|
||||
const theme = useTheme();
|
||||
|
||||
@ -279,16 +288,16 @@ export const VideoContent = () => {
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (name && id) {
|
||||
const existingVideo = hashMapVideos[id + "-" + name];
|
||||
if (channelName && id) {
|
||||
const existingVideo = hashMapVideos[id + "-" + channelName];
|
||||
|
||||
if (existingVideo) {
|
||||
setVideoData(existingVideo);
|
||||
} else {
|
||||
getVideoData(name, id);
|
||||
getVideoData(channelName, id);
|
||||
}
|
||||
}
|
||||
}, [id, name]);
|
||||
}, [id, channelName]);
|
||||
|
||||
useEffect(() => {
|
||||
if (contentRef.current) {
|
||||
@ -367,6 +376,14 @@ export const VideoContent = () => {
|
||||
(state: RootState) => state.persist.subscriptionList
|
||||
);
|
||||
|
||||
const focusVideo = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const focusRef = containerRef.current?.getContainerRef()?.current;
|
||||
const isCorrectTarget = e.currentTarget == e.target;
|
||||
if (focusRef && isCorrectTarget) {
|
||||
focusRef.focus({ preventScroll: true });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
@ -375,6 +392,7 @@ export const VideoContent = () => {
|
||||
flexDirection: "column",
|
||||
padding: "20px 10px",
|
||||
}}
|
||||
onClick={focusVideo}
|
||||
>
|
||||
<VideoPlayerContainer
|
||||
sx={{
|
||||
@ -387,10 +405,11 @@ export const VideoContent = () => {
|
||||
name={videoReference?.name}
|
||||
service={videoReference?.service}
|
||||
identifier={videoReference?.identifier}
|
||||
user={name}
|
||||
user={channelName}
|
||||
jsonId={id}
|
||||
poster={videoCover || ""}
|
||||
customStyle={{ aspectRatio: "16/9" }}
|
||||
ref={containerRef}
|
||||
/>
|
||||
)}
|
||||
<Box
|
||||
@ -414,12 +433,12 @@ export const VideoContent = () => {
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate(`/channel/${name}`);
|
||||
navigate(`/channel/${channelName}`);
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
src={`/arbitrary/THUMBNAIL/${name}/qortal_avatar`}
|
||||
alt={`${name}'s avatar`}
|
||||
src={`/arbitrary/THUMBNAIL/${channelName}/qortal_avatar`}
|
||||
alt={`${channelName}'s avatar`}
|
||||
/>
|
||||
</Box>
|
||||
<StyledCardColComment>
|
||||
@ -433,14 +452,22 @@ export const VideoContent = () => {
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate(`/channel/${name}`);
|
||||
navigate(`/channel/${channelName}`);
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
<SubscribeButton
|
||||
subscriberName={name}
|
||||
sx={{ marginLeft: "20px" }}
|
||||
/>
|
||||
{channelName}
|
||||
{channelName !== userName && (
|
||||
<>
|
||||
<SubscribeButton
|
||||
subscriberName={channelName}
|
||||
sx={{ marginLeft: "20px" }}
|
||||
/>
|
||||
<FollowButton
|
||||
followerName={channelName}
|
||||
sx={{ marginLeft: "20px" }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</AuthorTextComment>
|
||||
</StyledCardColComment>
|
||||
</StyledCardHeaderComment>
|
||||
@ -452,16 +479,22 @@ export const VideoContent = () => {
|
||||
}}
|
||||
>
|
||||
{videoData && (
|
||||
<SuperLike
|
||||
numberOfSuperlikes={numberOfSuperlikes}
|
||||
totalAmount={calculateAmountSuperlike}
|
||||
name={videoData?.user}
|
||||
service={videoData?.service}
|
||||
identifier={videoData?.id}
|
||||
onSuccess={val => {
|
||||
setSuperlikelist(prev => [val, ...prev]);
|
||||
}}
|
||||
/>
|
||||
<>
|
||||
<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>
|
||||
@ -598,7 +631,7 @@ export const VideoContent = () => {
|
||||
loadingSuperLikes={loadingSuperLikes}
|
||||
superlikes={superlikeList}
|
||||
postId={id || ""}
|
||||
postName={name || ""}
|
||||
postName={channelName || ""}
|
||||
/>
|
||||
|
||||
<Box
|
||||
@ -609,7 +642,7 @@ export const VideoContent = () => {
|
||||
maxWidth: "1200px",
|
||||
}}
|
||||
>
|
||||
<CommentSection postId={id || ""} postName={name || ""} />
|
||||
<CommentSection postId={id || ""} postName={channelName || ""} />
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
@ -37,12 +37,12 @@ import {
|
||||
import {
|
||||
changeFilterType,
|
||||
resetSubscriptions,
|
||||
VideoListType,
|
||||
} from "../../state/features/persistSlice.ts";
|
||||
import { categories, subCategories } from "../../constants/Categories.ts";
|
||||
import { ListSuperLikeContainer } from "../../components/common/ListSuperLikes/ListSuperLikeContainer.tsx";
|
||||
import { TabContext, TabList, TabPanel } from "@mui/lab";
|
||||
import VideoList from "./VideoList.tsx";
|
||||
import { allTabValue, subscriptionTabValue } from "../../constants/Misc.ts";
|
||||
import { setHomePageSelectedTab } from "../../state/features/persistSlice.ts";
|
||||
import { StatsData } from "../../components/StatsData.tsx";
|
||||
|
||||
@ -74,7 +74,9 @@ export const Home = ({ mode }: HomeProps) => {
|
||||
);
|
||||
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [tabValue, setTabValue] = useState<string>(persistReducer.selectedTab);
|
||||
const [tabValue, setTabValue] = useState<VideoListType>(
|
||||
persistReducer.selectedTab
|
||||
);
|
||||
|
||||
const tabFontSize = "20px";
|
||||
|
||||
@ -123,14 +125,14 @@ export const Home = ({ mode }: HomeProps) => {
|
||||
if (!firstFetch.current || !afterFetch.current) return;
|
||||
if (isFetching.current) return;
|
||||
isFetching.current = true;
|
||||
console.log("in getvideoshandler");
|
||||
|
||||
await getVideos(
|
||||
{
|
||||
name: filterName,
|
||||
category: selectedCategoryVideos?.id,
|
||||
subcategory: selectedSubCategoryVideos?.id,
|
||||
keywords: filterSearch,
|
||||
type: filterType,
|
||||
contentType: filterType,
|
||||
},
|
||||
reset,
|
||||
resetFilters,
|
||||
@ -171,7 +173,7 @@ export const Home = ({ mode }: HomeProps) => {
|
||||
category: "",
|
||||
subcategory: "",
|
||||
keywords: "",
|
||||
type: filterType,
|
||||
contentType: filterType,
|
||||
},
|
||||
null,
|
||||
null,
|
||||
@ -269,11 +271,10 @@ export const Home = ({ mode }: HomeProps) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
console.log("useeffect 5");
|
||||
getVideosHandler(true);
|
||||
}, [tabValue]);
|
||||
|
||||
const changeTab = (e: React.SyntheticEvent, newValue: string) => {
|
||||
const changeTab = (e: React.SyntheticEvent, newValue: VideoListType) => {
|
||||
setTabValue(newValue);
|
||||
dispatch(setHomePageSelectedTab(newValue));
|
||||
};
|
||||
@ -516,23 +517,23 @@ export const Home = ({ mode }: HomeProps) => {
|
||||
>
|
||||
<Tab
|
||||
label="All Videos"
|
||||
value={allTabValue}
|
||||
value={"all"}
|
||||
sx={{ fontSize: tabFontSize }}
|
||||
/>
|
||||
<Tab
|
||||
label="Subscriptions"
|
||||
value={subscriptionTabValue}
|
||||
value={"subscriptions"}
|
||||
sx={{ fontSize: tabFontSize }}
|
||||
/>
|
||||
</TabList>
|
||||
<TabPanel value={allTabValue} sx={{ width: "100%" }}>
|
||||
<TabPanel value={"all"} sx={{ width: "100%" }}>
|
||||
<VideoList videos={videos} />
|
||||
<LazyLoad
|
||||
onLoadMore={getVideosHandler}
|
||||
isLoading={isLoading}
|
||||
></LazyLoad>
|
||||
</TabPanel>
|
||||
<TabPanel value={subscriptionTabValue} sx={{ width: "100%" }}>
|
||||
<TabPanel value={"subscriptions"} sx={{ width: "100%" }}>
|
||||
{filteredSubscriptionList.length > 0 ? (
|
||||
<>
|
||||
<VideoList videos={videos} />
|
||||
@ -541,10 +542,12 @@ export const Home = ({ mode }: HomeProps) => {
|
||||
isLoading={isLoading}
|
||||
></LazyLoad>
|
||||
</>
|
||||
) : (
|
||||
) : !isLoading ? (
|
||||
<div style={{ textAlign: "center" }}>
|
||||
You have no subscriptions
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</TabPanel>
|
||||
</TabContext>
|
||||
|
@ -1,14 +1,19 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { allTabValue, subscriptionTabValue } from "../../constants/Misc.ts";
|
||||
import { SubscriptionData } from "../../components/common/SubscribeButton.tsx";
|
||||
|
||||
type StretchVideoType = "contain" | "fill" | "cover" | "none" | "scale-down";
|
||||
type SubscriptionListFilterType = "ALL" | "currentNameOnly";
|
||||
import { SubscriptionData } from "../../components/common/ContentButtons/SubscribeButton.tsx";
|
||||
|
||||
export type StretchVideoType =
|
||||
| "contain"
|
||||
| "fill"
|
||||
| "cover"
|
||||
| "none"
|
||||
| "scale-down";
|
||||
export type SubscriptionListFilterType = "ALL" | "currentNameOnly";
|
||||
export type ContentType = "videos" | "playlists";
|
||||
export type VideoListType = "all" | "subscriptions";
|
||||
interface settingsState {
|
||||
selectedTab: string;
|
||||
selectedTab: VideoListType;
|
||||
stretchVideoSetting: StretchVideoType;
|
||||
filterType: string;
|
||||
filterType: ContentType;
|
||||
subscriptionList: SubscriptionData[];
|
||||
playbackRate: number;
|
||||
subscriptionListFilter: SubscriptionListFilterType;
|
||||
@ -16,7 +21,7 @@ interface settingsState {
|
||||
}
|
||||
|
||||
const initialState: settingsState = {
|
||||
selectedTab: allTabValue,
|
||||
selectedTab: "all",
|
||||
stretchVideoSetting: "contain",
|
||||
filterType: "videos",
|
||||
subscriptionList: [],
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { SubscriptionData } from "../../components/common/SubscribeButton";
|
||||
import { SubscriptionData } from "../../components/common/ContentButtons/SubscribeButton.tsx";
|
||||
|
||||
interface GlobalState {
|
||||
videos: Video[];
|
||||
|
@ -2,6 +2,7 @@ import {
|
||||
AccountInfo,
|
||||
AccountName,
|
||||
GetRequestData,
|
||||
SearchResourcesResponse,
|
||||
SearchTransactionResponse,
|
||||
TransactionSearchParams,
|
||||
} from "./qortalRequestTypes.ts";
|
||||
@ -46,3 +47,30 @@ export const searchTransactions = async (params: TransactionSearchParams) => {
|
||||
...params,
|
||||
})) as SearchTransactionResponse[];
|
||||
};
|
||||
|
||||
export const fetchResourcesByIdentifier = async <T>(
|
||||
service: string,
|
||||
identifier: string
|
||||
) => {
|
||||
const names: SearchResourcesResponse[] = await qortalRequest({
|
||||
action: "SEARCH_QDN_RESOURCES",
|
||||
service,
|
||||
identifier,
|
||||
includeMetadata: false,
|
||||
});
|
||||
const distinctNames = names.filter(
|
||||
(searchResponse, index) => names.indexOf(searchResponse) === index
|
||||
);
|
||||
|
||||
const promises: Promise<T>[] = [];
|
||||
distinctNames.map(response => {
|
||||
const resource: Promise<T> = qortalRequest({
|
||||
action: "FETCH_QDN_RESOURCE",
|
||||
name: response.name,
|
||||
service,
|
||||
identifier,
|
||||
});
|
||||
promises.push(resource);
|
||||
});
|
||||
return (await Promise.all(promises)) as T[];
|
||||
};
|
||||
|
@ -23,6 +23,23 @@ export interface SearchTransactionResponse {
|
||||
amount: string;
|
||||
}
|
||||
|
||||
export interface MetaData {
|
||||
title: string;
|
||||
description: string;
|
||||
tags: string[];
|
||||
mimeType: string;
|
||||
}
|
||||
|
||||
export interface SearchResourcesResponse {
|
||||
name: string;
|
||||
service: string;
|
||||
identifier: string;
|
||||
metadata?: MetaData;
|
||||
size: number;
|
||||
created: number;
|
||||
updated: number;
|
||||
}
|
||||
|
||||
export type TransactionType =
|
||||
| "GENESIS"
|
||||
| "PAYMENT"
|
||||
|
@ -18,14 +18,14 @@ import {
|
||||
import { VideoPlayerGlobal } from "../components/common/VideoPlayer/VideoPlayerGlobal.tsx";
|
||||
import { Rnd } from "react-rnd";
|
||||
import { RequestQueue } from "../utils/queue";
|
||||
import { EditVideo } from "../components/EditVideo/EditVideo";
|
||||
import { EditPlaylist } from "../components/EditPlaylist/EditPlaylist";
|
||||
import { EditVideo } from "../components/Publish/EditVideo/EditVideo";
|
||||
import { EditPlaylist } from "../components/Publish/EditPlaylist/EditPlaylist";
|
||||
import ConsentModal from "../components/common/ConsentModal";
|
||||
import {
|
||||
extractSigValue,
|
||||
getPaymentInfo,
|
||||
isTimestampWithinRange,
|
||||
} from "../pages/VideoContent/VideoContent";
|
||||
} from "../pages/ContentPages/VideoContent/VideoContent";
|
||||
import { useFetchSuperLikes } from "../hooks/useFetchSuperLikes";
|
||||
import { SUPER_LIKE_BASE } from "../constants/Identifiers.ts";
|
||||
import { minPriceSuperlike } from "../constants/Misc.ts";
|
||||
@ -143,8 +143,8 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
||||
|
||||
const getSuperlikes = useCallback(async () => {
|
||||
try {
|
||||
let totalCount = 0
|
||||
let validCount = 0
|
||||
let totalCount = 0;
|
||||
let validCount = 0;
|
||||
let comments: any[] = [];
|
||||
while (validCount < 20 && totalCount < 100) {
|
||||
const url = `/arbitrary/resources/search?mode=ALL&service=BLOG_COMMENT&query=${SUPER_LIKE_BASE}&limit=1&offset=${totalCount}&includemetadata=true&reverse=true&excludeblocked=true`;
|
||||
@ -177,12 +177,12 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
||||
});
|
||||
comments = [
|
||||
...comments,
|
||||
{
|
||||
...comment,
|
||||
message: "",
|
||||
amount: res.amount,
|
||||
},
|
||||
];
|
||||
{
|
||||
...comment,
|
||||
message: "",
|
||||
amount: res.amount,
|
||||
},
|
||||
];
|
||||
validCount++;
|
||||
}
|
||||
} catch (error) {}
|
||||
|
Loading…
x
Reference in New Issue
Block a user