forked from Qortal/q-share
Added FollowButton.tsx
This commit is contained in:
@@ -221,10 +221,10 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
action: "PUBLISH_QDN_RESOURCE",
|
action: "PUBLISH_QDN_RESOURCE",
|
||||||
name: name,
|
name: name,
|
||||||
service: "DOCUMENT",
|
service: "DOCUMENT",
|
||||||
|
identifier: identifier + "_metadata",
|
||||||
data64: crowdfundObjectToBase64,
|
data64: crowdfundObjectToBase64,
|
||||||
title: title.slice(0, 50),
|
title: title.slice(0, 50),
|
||||||
description: metadescription,
|
description: metadescription,
|
||||||
identifier: identifier + "_metadata",
|
|
||||||
tag1: QSHARE_FILE_BASE,
|
tag1: QSHARE_FILE_BASE,
|
||||||
filename: `video_metadata.json`,
|
filename: `video_metadata.json`,
|
||||||
};
|
};
|
||||||
|
|||||||
163
src/components/common/FollowButton.tsx
Normal file
163
src/components/common/FollowButton.tsx
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TooltipLine = styled("div")(({ theme }) => ({
|
||||||
|
fontSize: "18px",
|
||||||
|
}));
|
||||||
|
|
||||||
|
const CustomWidthTooltipStyles = styled(
|
||||||
|
({ className, ...props }: TooltipProps) => (
|
||||||
|
<Tooltip {...props} classes={{ popper: className }} />
|
||||||
|
)
|
||||||
|
)({
|
||||||
|
[`& .${tooltipClasses.tooltip}`]: {
|
||||||
|
maxWidth: 600,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const CustomTooltip = ({ title, ...props }: TooltipProps) => {
|
||||||
|
if (typeof title === "string") title = <TooltipLine>{title}</TooltipLine>;
|
||||||
|
|
||||||
|
return <CustomWidthTooltipStyles title={title} {...props} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
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_ITEMS",
|
||||||
|
list_name: "followedNames",
|
||||||
|
items: [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",
|
||||||
|
items: [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 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CustomTooltip title={tooltipTitle} placement={"top"} arrow>
|
||||||
|
<Button
|
||||||
|
{...props}
|
||||||
|
variant={"contained"}
|
||||||
|
color="success"
|
||||||
|
sx={buttonStyle}
|
||||||
|
onClick={e => manageFollow(e)}
|
||||||
|
>
|
||||||
|
{isFollowingName() ? "Unfollow" : "Follow"}
|
||||||
|
</Button>
|
||||||
|
</CustomTooltip>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
|
|||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { CopyLinkButton } from "../../components/common/CopyLinkButton.tsx";
|
import { CopyLinkButton } from "../../components/common/CopyLinkButton.tsx";
|
||||||
|
import { FollowButton } from "../../components/common/FollowButton.tsx";
|
||||||
import { fontSizeMedium } from "../../constants/Misc.ts";
|
import { fontSizeMedium } from "../../constants/Misc.ts";
|
||||||
import { setIsLoadingGlobal } from "../../state/features/globalSlice";
|
import { setIsLoadingGlobal } from "../../state/features/globalSlice";
|
||||||
import { Avatar, Box, Typography, useTheme } from "@mui/material";
|
import { Avatar, Box, Typography, useTheme } from "@mui/material";
|
||||||
@@ -40,7 +41,8 @@ import {
|
|||||||
getIconsFromObject,
|
getIconsFromObject,
|
||||||
} from "../../constants/Categories/CategoryFunctions.ts";
|
} from "../../constants/Categories/CategoryFunctions.ts";
|
||||||
|
|
||||||
export function formatBytes(bytes, decimals = 2) {
|
export const formatBytes = (bytes: number | string, decimals = 2) => {
|
||||||
|
bytes = Number(bytes);
|
||||||
if (bytes === 0) return "0 Bytes";
|
if (bytes === 0) return "0 Bytes";
|
||||||
|
|
||||||
const k = 1024;
|
const k = 1024;
|
||||||
@@ -50,7 +52,7 @@ export function formatBytes(bytes, decimals = 2) {
|
|||||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
|
||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
|
||||||
}
|
};
|
||||||
|
|
||||||
export const FileContent = () => {
|
export const FileContent = () => {
|
||||||
const { name, id } = useParams();
|
const { name, id } = useParams();
|
||||||
@@ -381,6 +383,8 @@ export const FileContent = () => {
|
|||||||
>
|
>
|
||||||
<StyledCardHeaderComment
|
<StyledCardHeaderComment
|
||||||
sx={{
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
gap: "20px",
|
||||||
"& .MuiCardHeader-content": {
|
"& .MuiCardHeader-content": {
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
},
|
},
|
||||||
@@ -403,6 +407,14 @@ export const FileContent = () => {
|
|||||||
{name}
|
{name}
|
||||||
</AuthorTextComment>
|
</AuthorTextComment>
|
||||||
</StyledCardColComment>
|
</StyledCardColComment>
|
||||||
|
<FollowButton
|
||||||
|
sx={{ minWidth: "96px" }}
|
||||||
|
followerName={fileData?.user}
|
||||||
|
/>
|
||||||
|
<CopyLinkButton
|
||||||
|
link={`qortal://APP/Q-Share/share/${encodeURIComponent(fileData?.user)}/${encodeURIComponent(fileData?.id)}`}
|
||||||
|
tooltipTitle={`Copy page link`}
|
||||||
|
/>
|
||||||
</StyledCardHeaderComment>
|
</StyledCardHeaderComment>
|
||||||
</Box>
|
</Box>
|
||||||
<Spacer height="15px" />
|
<Spacer height="15px" />
|
||||||
@@ -417,10 +429,6 @@ export const FileContent = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{categoriesDisplay}
|
{categoriesDisplay}
|
||||||
<CopyLinkButton
|
|
||||||
link={`qortal://APP/Q-Share/share/${encodeURIComponent(fileData?.user)}/${encodeURIComponent(fileData?.id)}`}
|
|
||||||
tooltipTitle={`Copy page link`}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Spacer height="15px" />
|
<Spacer height="15px" />
|
||||||
<Box
|
<Box
|
||||||
|
|||||||
@@ -11,12 +11,13 @@ import {
|
|||||||
VideoUploadDate,
|
VideoUploadDate,
|
||||||
} from "./FileList-styles.tsx";
|
} from "./FileList-styles.tsx";
|
||||||
import EditIcon from "@mui/icons-material/Edit";
|
import EditIcon from "@mui/icons-material/Edit";
|
||||||
|
import BlockIcon from "@mui/icons-material/Block";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
blockUser,
|
blockUser,
|
||||||
setEditFile,
|
setEditFile,
|
||||||
Video,
|
Video,
|
||||||
} from "../../state/features/fileSlice.ts";
|
} from "../../state/features/fileSlice.ts";
|
||||||
import BlockIcon from "@mui/icons-material/Block";
|
|
||||||
import AttachFileIcon from "@mui/icons-material/AttachFile";
|
import AttachFileIcon from "@mui/icons-material/AttachFile";
|
||||||
import { formatBytes } from "../FileContent/FileContent.tsx";
|
import { formatBytes } from "../FileContent/FileContent.tsx";
|
||||||
import { formatDate } from "../../utils/time.ts";
|
import { formatDate } from "../../utils/time.ts";
|
||||||
|
|||||||
Reference in New Issue
Block a user