mirror of
https://github.com/Qortal/q-tube.git
synced 2025-02-11 17:55:51 +00:00
Superlikes list on Home Page shows time since it was made
Refactoring to prepare for SuperDislike button
This commit is contained in:
parent
43663061ee
commit
5d795766c3
425
src/components/common/ContentButtons/SuperDislike.tsx
Normal file
425
src/components/common/ContentButtons/SuperDislike.tsx
Normal file
@ -0,0 +1,425 @@
|
||||
import ThumbDownIcon from "@mui/icons-material/ThumbDown";
|
||||
import {
|
||||
Box,
|
||||
DialogContent,
|
||||
InputAdornment,
|
||||
InputLabel,
|
||||
Modal,
|
||||
Tooltip,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import qortImg from "../../../assets/img/qort.png";
|
||||
import {
|
||||
FOR,
|
||||
FOR_SUPER_LIKE,
|
||||
SUPER_LIKE_BASE,
|
||||
} from "../../../constants/Identifiers.ts";
|
||||
import {
|
||||
fontSizeLarge,
|
||||
fontSizeMedium,
|
||||
minPriceSuperDislike,
|
||||
} from "../../../constants/Misc.ts";
|
||||
import { setNotification } from "../../../state/features/notificationsSlice.ts";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import BoundedNumericTextField from "../../../utils/BoundedNumericTextField.tsx";
|
||||
import { numberToInt, truncateNumber } from "../../../utils/numberFunctions.ts";
|
||||
import { objectToBase64 } from "../../../utils/PublishFormatter.ts";
|
||||
import { getUserBalance } from "../../../utils/qortalRequestFunctions.ts";
|
||||
import { MultiplePublish } from "../../Publish/MultiplePublish/MultiplePublishAll.tsx";
|
||||
import {
|
||||
CrowdfundActionButton,
|
||||
CrowdfundActionButtonRow,
|
||||
ModalBody,
|
||||
NewCrowdfundTitle,
|
||||
Spacer,
|
||||
} from "../../Publish/PublishVideo/PublishVideo-styles.tsx";
|
||||
import { CommentInput } from "../Comments/Comments-styles.tsx";
|
||||
|
||||
const uid = new ShortUniqueId({ length: 4 });
|
||||
|
||||
export const SuperDislike = ({
|
||||
onSuccess,
|
||||
name,
|
||||
service,
|
||||
identifier,
|
||||
totalAmount,
|
||||
numberOfSuperdislikes,
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||
|
||||
const [superDislikeDonationAmount, setSuperdislikeDonationAmount] =
|
||||
useState<number>(minPriceSuperDislike);
|
||||
const [currentBalance, setCurrentBalance] = useState<string>("");
|
||||
|
||||
const [comment, setComment] = useState<string>("");
|
||||
const username = useSelector((state: RootState) => state.auth?.user?.name);
|
||||
const [isOpenMultiplePublish, setIsOpenMultiplePublish] = useState(false);
|
||||
const [publishes, setPublishes] = useState<any>(null);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const resetValues = () => {
|
||||
setSuperdislikeDonationAmount(0);
|
||||
setComment("");
|
||||
setPublishes(null);
|
||||
};
|
||||
const onClose = () => {
|
||||
resetValues();
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
async function publishSuperDislike() {
|
||||
try {
|
||||
if (!username) throw new Error("You need a name to publish");
|
||||
if (!name) throw new Error("Could not retrieve content creator's name");
|
||||
const estimatedTransactionFees = 0.1;
|
||||
const donationExceedsBalance =
|
||||
superDislikeDonationAmount + estimatedTransactionFees >=
|
||||
+currentBalance;
|
||||
if (donationExceedsBalance) {
|
||||
throw new Error("Total donations exceeds current balance");
|
||||
}
|
||||
|
||||
const resName = await qortalRequest({
|
||||
action: "GET_NAME_DATA",
|
||||
name: name,
|
||||
});
|
||||
|
||||
const address = resName.owner;
|
||||
if (!identifier) throw new Error("Could not retrieve id of video post");
|
||||
// if (comment.length > 200) throw new Error("Comment needs to be under 200 characters")
|
||||
|
||||
if (!address)
|
||||
throw new Error("Could not retrieve content creator's address");
|
||||
if (
|
||||
!superDislikeDonationAmount ||
|
||||
superDislikeDonationAmount < minPriceSuperDislike
|
||||
)
|
||||
throw new Error(
|
||||
`The amount is ${superDislikeDonationAmount}, but it needs to be at least ${minPriceSuperDislike} QORT`
|
||||
);
|
||||
|
||||
const listOfPublishes = [];
|
||||
|
||||
const res = await qortalRequest({
|
||||
action: "SEND_COIN",
|
||||
coin: "QORT",
|
||||
destinationAddress: address,
|
||||
amount: superDislikeDonationAmount,
|
||||
});
|
||||
|
||||
const metadescription = `**sig:${
|
||||
res.signature
|
||||
};${FOR}:${name}_${FOR_SUPER_LIKE};nm:${name.slice(
|
||||
0,
|
||||
20
|
||||
)};id:${identifier.slice(-30)}**`;
|
||||
|
||||
const id = uid.rnd();
|
||||
const identifierSuperDislike = `${SUPER_LIKE_BASE}${identifier.slice(
|
||||
0,
|
||||
39
|
||||
)}_${id}`;
|
||||
|
||||
const superLikeToBase64 = await objectToBase64({
|
||||
comment,
|
||||
transactionReference: res.signature,
|
||||
notificationInformation: {
|
||||
name,
|
||||
identifier,
|
||||
for: `${name}_${FOR_SUPER_LIKE}`,
|
||||
},
|
||||
about:
|
||||
"Super likes are a way to 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);
|
||||
|
||||
const requestBodyJson: any = {
|
||||
action: "PUBLISH_QDN_RESOURCE",
|
||||
name: username,
|
||||
service: "BLOG_COMMENT",
|
||||
data64: superLikeToBase64,
|
||||
title: "",
|
||||
description: metadescription,
|
||||
identifier: identifierSuperDislike,
|
||||
tag1: SUPER_LIKE_BASE,
|
||||
filename: `superDislike_metadata.json`,
|
||||
};
|
||||
|
||||
listOfPublishes.push(requestBodyJson);
|
||||
|
||||
const multiplePublish = {
|
||||
action: "PUBLISH_MULTIPLE_QDN_RESOURCES",
|
||||
resources: [...listOfPublishes],
|
||||
};
|
||||
setPublishes(multiplePublish);
|
||||
|
||||
setIsOpenMultiplePublish(true);
|
||||
} catch (error: any) {
|
||||
dispatch(
|
||||
setNotification({
|
||||
msg:
|
||||
error?.error ||
|
||||
error?.message ||
|
||||
error ||
|
||||
"Failed to publish Super Like",
|
||||
alertType: "error",
|
||||
})
|
||||
);
|
||||
throw new Error("Failed to publish Super Like");
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getUserBalance().then(foundBalance => {
|
||||
setCurrentBalance(truncateNumber(foundBalance, 2));
|
||||
});
|
||||
}, []);
|
||||
|
||||
const isScreenSmall = !useMediaQuery(`(min-width:400px)`);
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
onClick={() => {
|
||||
if (username === name) {
|
||||
dispatch(
|
||||
setNotification({
|
||||
msg: "You cannot send yourself a Super like",
|
||||
alertType: "error",
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
setIsOpen(true);
|
||||
}}
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "15px",
|
||||
cursor: "pointer",
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<Tooltip title="Super Like" placement="top">
|
||||
<Box
|
||||
sx={{
|
||||
padding: "5px",
|
||||
borderRadius: "7px",
|
||||
gap: "5px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
outline: "1px red solid",
|
||||
marginRight: "10px",
|
||||
height: "53px",
|
||||
}}
|
||||
>
|
||||
<ThumbDownIcon
|
||||
style={{
|
||||
color: "red",
|
||||
}}
|
||||
/>
|
||||
|
||||
{numberOfSuperdislikes === 0 ? null : (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
userSelect: "none",
|
||||
}}
|
||||
>
|
||||
<span style={{ marginRight: "10px", paddingBottom: "4px" }}>
|
||||
{numberOfSuperdislikes}
|
||||
</span>
|
||||
<img
|
||||
style={{
|
||||
height: "25px",
|
||||
width: "25px",
|
||||
marginRight: "5px",
|
||||
}}
|
||||
src={qortImg}
|
||||
alt={"Qort Icon"}
|
||||
/>
|
||||
{truncateNumber(totalAmount, 0)}
|
||||
</div>
|
||||
)}
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<Modal
|
||||
open={isOpen}
|
||||
onClose={() => setIsOpen(false)}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<ModalBody
|
||||
sx={{
|
||||
width: "90%",
|
||||
backgroundColor: "#A58700",
|
||||
boxShadow: "none",
|
||||
gap: "0px",
|
||||
padding: "0px",
|
||||
border: 0,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<NewCrowdfundTitle>Super Like</NewCrowdfundTitle>
|
||||
</Box>
|
||||
<DialogContent sx={{ padding: "10px 12px" }}>
|
||||
<Box>
|
||||
<InputLabel
|
||||
sx={{ color: "white" }}
|
||||
htmlFor="standard-adornment-amount"
|
||||
>
|
||||
Amount
|
||||
</InputLabel>
|
||||
<BoundedNumericTextField
|
||||
addIconButtons={!isScreenSmall}
|
||||
minValue={+minPriceSuperDislike}
|
||||
initialValue={minPriceSuperDislike.toString()}
|
||||
maxValue={numberToInt(+currentBalance)}
|
||||
allowDecimals={false}
|
||||
allowNegatives={false}
|
||||
id="standard-adornment-amount"
|
||||
value={superDislikeDonationAmount}
|
||||
afterChange={(e: string) => setSuperdislikeDonationAmount(+e)}
|
||||
InputProps={{
|
||||
style: {
|
||||
fontSize: fontSizeMedium,
|
||||
width: "80%",
|
||||
border: `1px solid ${theme.palette.primary.main}`,
|
||||
},
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<img
|
||||
style={{
|
||||
height: "40px",
|
||||
width: "40px",
|
||||
}}
|
||||
src={qortImg}
|
||||
alt={"Qort Icon"}
|
||||
/>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
<Box sx={{ display: "flex", gap: "5px", alignItems: "center" }}>
|
||||
<img
|
||||
style={{
|
||||
height: "25px",
|
||||
width: "25px",
|
||||
}}
|
||||
src={qortImg}
|
||||
alt={"Qort Icon"}
|
||||
/>
|
||||
Balance: {currentBalance}
|
||||
</Box>
|
||||
<Spacer height="25px" />
|
||||
|
||||
<CommentInput
|
||||
id="standard-multiline-flexible"
|
||||
label="Comment Here"
|
||||
multiline
|
||||
minRows={8}
|
||||
maxRows={8}
|
||||
variant="filled"
|
||||
value={comment}
|
||||
InputLabelProps={{
|
||||
style: { fontSize: fontSizeMedium, color: "white" },
|
||||
}}
|
||||
inputProps={{
|
||||
maxLength: 500,
|
||||
style: { fontSize: fontSizeLarge },
|
||||
}}
|
||||
onChange={e => setComment(e.target.value)}
|
||||
sx={{ border: `1px solid ${theme.palette.primary.main}` }}
|
||||
/>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<CrowdfundActionButtonRow>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
margin: "10px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<CrowdfundActionButton
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
resetValues();
|
||||
onClose();
|
||||
}}
|
||||
variant="contained"
|
||||
color="error"
|
||||
>
|
||||
Cancel
|
||||
</CrowdfundActionButton>
|
||||
|
||||
<CrowdfundActionButton
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
publishSuperDislike();
|
||||
}}
|
||||
>
|
||||
Publish
|
||||
</CrowdfundActionButton>
|
||||
</Box>
|
||||
</CrowdfundActionButtonRow>
|
||||
</ModalBody>
|
||||
</Modal>
|
||||
{isOpenMultiplePublish && (
|
||||
<MultiplePublish
|
||||
isOpen={isOpenMultiplePublish}
|
||||
onError={messageNotification => {
|
||||
setIsOpenMultiplePublish(false);
|
||||
setPublishes(null);
|
||||
if (messageNotification) {
|
||||
dispatch(
|
||||
setNotification({
|
||||
msg: messageNotification,
|
||||
alertType: "error",
|
||||
})
|
||||
);
|
||||
}
|
||||
}}
|
||||
onSubmit={() => {
|
||||
onSuccess({
|
||||
name: username,
|
||||
message: comment,
|
||||
service,
|
||||
identifier,
|
||||
amount: +superDislikeDonationAmount,
|
||||
created: Date.now(),
|
||||
});
|
||||
setIsOpenMultiplePublish(false);
|
||||
onClose();
|
||||
|
||||
dispatch(
|
||||
setNotification({
|
||||
msg: "Super like published",
|
||||
alertType: "success",
|
||||
})
|
||||
);
|
||||
}}
|
||||
publishes={publishes}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@ -21,7 +21,7 @@ import {
|
||||
import {
|
||||
fontSizeLarge,
|
||||
fontSizeMedium,
|
||||
minPriceSuperlike,
|
||||
minPriceSuperLike,
|
||||
} from "../../../constants/Misc.ts";
|
||||
import { setNotification } from "../../../state/features/notificationsSlice.ts";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
@ -52,7 +52,7 @@ export const SuperLike = ({
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||
|
||||
const [superlikeDonationAmount, setSuperlikeDonationAmount] =
|
||||
useState<number>(minPriceSuperlike);
|
||||
useState<number>(minPriceSuperLike);
|
||||
const [currentBalance, setCurrentBalance] = useState<string>("");
|
||||
|
||||
const [comment, setComment] = useState<string>("");
|
||||
@ -95,10 +95,10 @@ export const SuperLike = ({
|
||||
throw new Error("Could not retrieve content creator's address");
|
||||
if (
|
||||
!superlikeDonationAmount ||
|
||||
superlikeDonationAmount < minPriceSuperlike
|
||||
superlikeDonationAmount < minPriceSuperLike
|
||||
)
|
||||
throw new Error(
|
||||
`The amount is ${superlikeDonationAmount}, but it needs to be at least ${minPriceSuperlike} QORT`
|
||||
`The amount is ${superlikeDonationAmount}, but it needs to be at least ${minPriceSuperLike} QORT`
|
||||
);
|
||||
|
||||
const listOfPublishes = [];
|
||||
@ -286,8 +286,8 @@ export const SuperLike = ({
|
||||
</InputLabel>
|
||||
<BoundedNumericTextField
|
||||
addIconButtons={!isScreenSmall}
|
||||
minValue={+minPriceSuperlike}
|
||||
initialValue={minPriceSuperlike.toString()}
|
||||
minValue={+minPriceSuperLike}
|
||||
initialValue={minPriceSuperLike.toString()}
|
||||
maxValue={numberToInt(+currentBalance)}
|
||||
allowDecimals={false}
|
||||
allowNegatives={false}
|
||||
|
@ -1,17 +1,20 @@
|
||||
import * as React from "react";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import Divider from "@mui/material/Divider";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import ListItemAvatar from "@mui/material/ListItemAvatar";
|
||||
import Avatar from "@mui/material/Avatar";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import EmojiEventsIcon from "@mui/icons-material/EmojiEvents";
|
||||
import ThumbUpIcon from "@mui/icons-material/ThumbUp";
|
||||
import { Box } from "@mui/material";
|
||||
import Avatar from "@mui/material/Avatar";
|
||||
import Divider from "@mui/material/Divider";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemAvatar from "@mui/material/ListItemAvatar";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import * as React from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../../state/store";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import EmojiEventsIcon from "@mui/icons-material/EmojiEvents";
|
||||
import { fontSizeSmall } from "../../../constants/Misc.ts";
|
||||
import { RootState } from "../../../state/store";
|
||||
import { formatDate } from "../../../utils/time.ts";
|
||||
|
||||
const truncateMessage = message => {
|
||||
return message.length > 40 ? message.slice(0, 40) + "..." : message;
|
||||
};
|
||||
@ -139,6 +142,9 @@ export default function ListSuperLikes({ superlikes }) {
|
||||
{forName}
|
||||
</Box>
|
||||
)}
|
||||
<span style={{ fontSize: fontSizeSmall }}>
|
||||
{formatDate(superlike.created)}
|
||||
</span>
|
||||
</Box>
|
||||
</ListItem>
|
||||
<Box
|
||||
|
@ -33,7 +33,7 @@ import {
|
||||
FOR_SUPER_LIKE,
|
||||
SUPER_LIKE_BASE,
|
||||
} from "../../../constants/Identifiers.ts";
|
||||
import { minPriceSuperlike } from "../../../constants/Misc.ts";
|
||||
import { minPriceSuperLike } from "../../../constants/Misc.ts";
|
||||
|
||||
const generalLocal = localForage.createInstance({
|
||||
name: "q-tube-general",
|
||||
@ -141,7 +141,7 @@ export const Notifications = () => {
|
||||
if (!result) continue;
|
||||
const res = await getPaymentInfo(result);
|
||||
if (
|
||||
+res?.amount >= minPriceSuperlike &&
|
||||
+res?.amount >= minPriceSuperLike &&
|
||||
res.recipient === usernameAddress &&
|
||||
isTimestampWithinRange(res?.timestamp, comment.created)
|
||||
) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
const useTestIdentifiers = false;
|
||||
export const useTestIdentifiers = false;
|
||||
export const QTUBE_VIDEO_BASE = useTestIdentifiers
|
||||
? "MYTEST_vid_"
|
||||
: "qtube_vid_";
|
||||
|
@ -1,4 +1,7 @@
|
||||
export const minPriceSuperlike = 1;
|
||||
import { useTestIdentifiers } from "./Identifiers.ts";
|
||||
|
||||
export const minPriceSuperLike = 1;
|
||||
export const minPriceSuperDislike = 1;
|
||||
|
||||
export const titleFormatter = /[\r\n]+/g;
|
||||
export const titleFormatterOnSave = /[\r\n/<>:"'\\*|?]+/g;
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
SUPER_LIKE_BASE,
|
||||
} from "../../../constants/Identifiers.ts";
|
||||
import {
|
||||
minPriceSuperlike,
|
||||
minPriceSuperLike,
|
||||
titleFormatterOnSave,
|
||||
} from "../../../constants/Misc.ts";
|
||||
import { useFetchSuperLikes } from "../../../hooks/useFetchSuperLikes.tsx";
|
||||
@ -199,7 +199,7 @@ export const useVideoContentState = () => {
|
||||
if (!result) continue;
|
||||
const res = await getPaymentInfo(result);
|
||||
if (
|
||||
+res?.amount >= minPriceSuperlike &&
|
||||
+res?.amount >= minPriceSuperLike &&
|
||||
res.recipient === nameAddressParam &&
|
||||
isTimestampWithinRange(res?.timestamp, comment.created)
|
||||
) {
|
||||
|
@ -28,7 +28,7 @@ import { EditPlaylist } from "../components/Publish/EditPlaylist/EditPlaylist";
|
||||
import ConsentModal from "../components/common/ConsentModal";
|
||||
import { useFetchSuperLikes } from "../hooks/useFetchSuperLikes";
|
||||
import { SUPER_LIKE_BASE } from "../constants/Identifiers.ts";
|
||||
import { minPriceSuperlike } from "../constants/Misc.ts";
|
||||
import { minPriceSuperLike } from "../constants/Misc.ts";
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
@ -167,7 +167,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
||||
if (!result) continue;
|
||||
const res = await getPaymentInfo(result);
|
||||
if (
|
||||
+res?.amount >= minPriceSuperlike &&
|
||||
+res?.amount >= minPriceSuperLike &&
|
||||
isTimestampWithinRange(res?.timestamp, comment.created)
|
||||
) {
|
||||
addSuperlikeRawDataGetToList({
|
||||
|
Loading…
x
Reference in New Issue
Block a user