Add comment TODO, format code and remove unused import

This commit is contained in:
Nicola Benaglia 2025-04-23 20:49:30 +02:00
parent 1a0ccbc08d
commit d796a3f7c0
32 changed files with 1689 additions and 1460 deletions

View File

@ -91,7 +91,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
if (!name) throw new Error('Please provide a name');
if (!description) throw new Error('Please provide a description');
const fee = await getFee('CREATE_GROUP');
const fee = await getFee('CREATE_GROUP'); // TODO translate
await show({
message: 'Would you like to perform an CREATE_GROUP transaction?',
publishFee: fee.fee + ' QORT',

View File

@ -101,7 +101,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
const handleJoinGroup = async (group, isOpen) => {
try {
const groupId = group.groupId;
const fee = await getFee('JOIN_GROUP');
const fee = await getFee('JOIN_GROUP'); // TODO translate
await show({
message: 'Would you like to perform an JOIN_GROUP transaction?',
publishFee: fee.fee + ' QORT',

View File

@ -129,7 +129,7 @@ export const BlockedUsersModal = () => {
executeEvent('updateChatMessagesWithBlocks', true);
}
} catch (error) {
setOpenSnackGlobal(true);
setOpenSnackGlobal(true); // TODO translate
setInfoSnackCustom({
type: 'error',
message: error?.message || 'Unable to block user',

View File

@ -1,21 +1,20 @@
import { useMemo } from "react";
import DOMPurify from "dompurify";
import "react-quill/dist/quill.snow.css";
import "react-quill/dist/quill.core.css";
import "react-quill/dist/quill.bubble.css";
import { Box, styled } from "@mui/material";
import { convertQortalLinks } from "../../../utils/qortalLink";
import { useMemo } from 'react';
import DOMPurify from 'dompurify';
import 'react-quill/dist/quill.snow.css';
import 'react-quill/dist/quill.core.css';
import 'react-quill/dist/quill.bubble.css';
import { Box, styled } from '@mui/material';
import { convertQortalLinks } from '../../../utils/qortalLink';
const CrowdfundInlineContent = styled(Box)(({ theme }) => ({
display: "flex",
fontFamily: "Mulish",
fontSize: "19px",
fontWeight: 400,
letterSpacing: 0,
color: theme.palette.text.primary,
width: '100%'
}));
display: 'flex',
fontFamily: 'Mulish',
fontSize: '19px',
fontWeight: 400,
letterSpacing: 0,
color: theme.palette.text.primary,
width: '100%',
}));
export const DisplayHtml = ({ html, textColor }: any) => {
const cleanContent = useMemo(() => {
@ -29,6 +28,7 @@ export const DisplayHtml = ({ html, textColor }: any) => {
}, [html]);
if (!cleanContent) return null;
return (
<CrowdfundInlineContent>
<div
@ -36,7 +36,7 @@ export const DisplayHtml = ({ html, textColor }: any) => {
style={{
color: textColor || 'white',
fontWeight: 400,
fontSize: '16px'
fontSize: '16px',
}}
dangerouslySetInnerHTML={{ __html: cleanContent }}
/>

View File

@ -45,7 +45,6 @@ import { formatDate, formatTimestamp } from '../../../utils/time';
import LazyLoad from '../../../common/LazyLoad';
import { delay } from '../../../utils/helpers';
import { NewThread } from './NewThread';
import { getBaseApi } from '../../../background';
import {
decryptPublishes,
getTempPublish,
@ -55,15 +54,11 @@ import CheckSVG from '../../../assets/svgs/Check.svg';
import SortSVG from '../../../assets/svgs/Sort.svg';
import ArrowDownSVG from '../../../assets/svgs/ArrowDown.svg';
import { LoadingSnackbar } from '../../Snackbar/LoadingSnackbar';
import {
executeEvent,
subscribeToEvent,
unsubscribeFromEvent,
} from '../../../utils/events';
import { executeEvent } from '../../../utils/events';
import RefreshIcon from '@mui/icons-material/Refresh';
import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App';
import { WrapperUserAction } from '../../WrapperUserAction';
import { addDataPublishesFunc, getDataPublishesFunc } from '../Group';
const filterOptions = ['Recently active', 'Newest', 'Oldest'];
export const threadIdentifier = 'DOCUMENT';
@ -183,7 +178,7 @@ export const GroupMail = ({
});
});
} catch (error) {
} finally {
console.log(error);
}
};
@ -266,7 +261,6 @@ export const GroupMail = ({
} else {
sorted = fullArrayMsg.sort((a: any, b: any) => a.created - b.created);
}
setAllThreads(sorted);
} catch (error) {
console.log({ error });
@ -376,6 +370,7 @@ export const GroupMail = ({
);
setRecentThreads(sorted);
} catch (error) {
console.log(error);
} finally {
setIsLoading(false);
// dispatch(setIsLoadingCustom(null));
@ -444,7 +439,6 @@ export const GroupMail = ({
}
}
}
setMembers(members);
} catch (error) {
console.log({ error });
@ -754,7 +748,9 @@ export const GroupMail = ({
>
{thread?.threadData?.title}
</ThreadSingleTitle>
<Spacer height="10px" />
{filterMode === 'Recently active' && (
<div
style={{
@ -799,7 +795,7 @@ export const GroupMail = ({
sx={{
color: 'white',
fontSize: '12px',
}}
}} // TODO translate
>
Last page
</Typography>

View File

@ -1,52 +1,47 @@
import {
AppBar,
Button,
Toolbar,
Typography,
Box,
TextField,
} from "@mui/material";
import { styled } from "@mui/system";
import { Typography, Box, TextField } from '@mui/material';
import { styled } from '@mui/system';
export const InstanceContainer = styled(Box)(({ theme }) => ({
display: "flex",
alignItems: "center",
width: "100%",
backgroundColor: "var(--color-instance)",
height: "59px",
display: 'flex',
alignItems: 'center',
width: '100%',
backgroundColor: 'var(--color-instance)',
height: '59px',
flexShrink: 0,
justifyContent: "space-between",
justifyContent: 'space-between',
}));
export const MailContainer = styled(Box)(({ theme }) => ({
display: "flex",
flexDirection: "column",
width: "100%",
height: "calc(100vh - 78px)",
overflow: "hidden",
display: 'flex',
flexDirection: 'column',
width: '100%',
height: 'calc(100vh - 78px)',
overflow: 'hidden',
}));
export const MailBody = styled(Box)(({ theme }) => ({
display: "flex",
flexDirection: "row",
width: "100%",
height: "calc(100% - 59px)",
// overflow: 'auto !important'
display: 'flex',
flexDirection: 'row',
width: '100%',
height: 'calc(100% - 59px)',
}));
export const MailBodyInner = styled(Box)(({ theme }) => ({
display: "flex",
flexDirection: "column",
width: "50%",
height: "100%",
display: 'flex',
flexDirection: 'column',
width: '50%',
height: '100%',
}));
export const MailBodyInnerHeader = styled(Box)(({ theme }) => ({
display: "flex",
width: "100%",
height: "25px",
marginTop: "50px",
marginBottom: "35px",
justifyContent: "center",
alignItems: "center",
gap: "11px",
display: 'flex',
width: '100%',
height: '25px',
marginTop: '50px',
marginBottom: '35px',
justifyContent: 'center',
alignItems: 'center',
gap: '11px',
}));
export const MailBodyInnerScroll = styled(Box)`
@ -84,163 +79,174 @@ export const MailBodyInnerScroll = styled(Box)`
`;
export const ComposeContainer = styled(Box)(({ theme }) => ({
display: "flex",
width: "150px",
alignItems: "center",
gap: "7px",
height: "100%",
cursor: "pointer",
transition: "0.2s background-color",
justifyContent: "center",
"&:hover": {
backgroundColor: "rgba(67, 68, 72, 1)",
display: 'flex',
width: '150px',
alignItems: 'center',
gap: '7px',
height: '100%',
cursor: 'pointer',
transition: '0.2s background-color',
justifyContent: 'center',
'&:hover': {
backgroundColor: 'rgba(67, 68, 72, 1)',
},
}));
export const ComposeContainerBlank = styled(Box)(({ theme }) => ({
display: "flex",
width: "150px",
alignItems: "center",
gap: "7px",
height: "100%",
display: 'flex',
width: '150px',
alignItems: 'center',
gap: '7px',
height: '100%',
}));
export const ComposeP = styled(Typography)(({ theme }) => ({
fontSize: "15px",
fontSize: '15px',
fontWeight: 500,
}));
export const ComposeIcon = styled("img")({
width: "auto",
height: "auto",
userSelect: "none",
objectFit: "contain",
cursor: "pointer",
});
export const ArrowDownIcon = styled("img")({
width: "auto",
height: "auto",
userSelect: "none",
objectFit: "contain",
cursor: "pointer",
});
export const MailIconImg = styled("img")({
width: "auto",
height: "auto",
userSelect: "none",
objectFit: "contain",
export const ComposeIcon = styled('img')({
width: 'auto',
height: 'auto',
userSelect: 'none',
objectFit: 'contain',
cursor: 'pointer',
});
export const MailMessageRowInfoImg = styled("img")({
width: "auto",
height: "auto",
userSelect: "none",
objectFit: "contain",
export const ArrowDownIcon = styled('img')({
width: 'auto',
height: 'auto',
userSelect: 'none',
objectFit: 'contain',
cursor: 'pointer',
});
export const MailIconImg = styled('img')({
width: 'auto',
height: 'auto',
userSelect: 'none',
objectFit: 'contain',
});
export const MailMessageRowInfoImg = styled('img')({
width: 'auto',
height: 'auto',
userSelect: 'none',
objectFit: 'contain',
});
export const SelectInstanceContainer = styled(Box)(({ theme }) => ({
display: "flex",
alignItems: "center",
gap: "17px",
display: 'flex',
alignItems: 'center',
gap: '17px',
}));
export const SelectInstanceContainerInner = styled(Box)(({ theme }) => ({
display: "flex",
alignItems: "center",
gap: "3px",
cursor: "pointer",
padding: "8px",
transition: "all 0.2s",
"&:hover": {
borderRadius: "8px",
background: "#434448",
display: 'flex',
alignItems: 'center',
gap: '3px',
cursor: 'pointer',
padding: '8px',
transition: 'all 0.2s',
'&:hover': {
borderRadius: '8px',
background: '#434448',
},
}));
export const SelectInstanceContainerFilterInner = styled(Box)(({ theme }) => ({
display: "flex",
alignItems: "center",
gap: "3px",
cursor: "pointer",
padding: "8px",
transition: "all 0.2s"
display: 'flex',
alignItems: 'center',
gap: '3px',
cursor: 'pointer',
padding: '8px',
transition: 'all 0.2s',
}));
export const InstanceLabel = styled(Typography)(({ theme }) => ({
fontSize: "16px",
fontSize: '16px',
fontWeight: 500,
color: "#FFFFFF33",
color: '#FFFFFF33',
}));
export const InstanceP = styled(Typography)(({ theme }) => ({
fontSize: "16px",
fontSize: '16px',
fontWeight: 500,
}));
export const MailMessageRowContainer = styled(Box)(({ theme }) => ({
display: "flex",
alignItems: "center",
cursor: "pointer",
justifyContent: "space-between",
borderRadius: "56px 5px 10px 56px",
paddingRight: "15px",
transition: "background 0.2s",
gap: "10px",
"&:hover": {
background: "#434448",
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
justifyContent: 'space-between',
borderRadius: '56px 5px 10px 56px',
paddingRight: '15px',
transition: 'background 0.2s',
gap: '10px',
'&:hover': {
background: '#434448',
},
}));
export const MailMessageRowProfile = styled(Box)(({ theme }) => ({
display: "flex",
alignItems: "center",
cursor: "pointer",
justifyContent: "flex-start",
gap: "10px",
width: "50%",
overflow: "hidden",
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
justifyContent: 'flex-start',
gap: '10px',
width: '50%',
overflow: 'hidden',
}));
export const MailMessageRowInfo = styled(Box)(({ theme }) => ({
display: "flex",
alignItems: "center",
cursor: "pointer",
justifyContent: "flex-start",
gap: "7px",
width: "50%",
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
justifyContent: 'flex-start',
gap: '7px',
width: '50%',
}));
export const MailMessageRowInfoStatusNotDecrypted = styled(Typography)(
({ theme }) => ({
fontSize: "16px",
fontSize: '16px',
fontWeight: 900,
textTransform: "uppercase",
paddingTop: "2px",
textTransform: 'uppercase',
paddingTop: '2px',
})
);
export const MailMessageRowInfoStatusRead = styled(Typography)(({ theme }) => ({
fontSize: "16px",
fontSize: '16px',
fontWeight: 300,
}));
export const MessageExtraInfo = styled(Box)(({ theme }) => ({
display: "flex",
flexDirection: "column",
gap: "2px",
overflow: "hidden",
display: 'flex',
flexDirection: 'column',
gap: '2px',
overflow: 'hidden',
}));
export const MessageExtraName = styled(Typography)(({ theme }) => ({
fontSize: "16px",
fontSize: '16px',
fontWeight: 900,
whiteSpace: "nowrap",
textOverflow: "ellipsis",
overflow: "hidden",
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
}));
export const MessageExtraDate = styled(Typography)(({ theme }) => ({
fontSize: "15px",
fontSize: '15px',
fontWeight: 500,
}));
export const MessagesContainer = styled(Box)(({ theme }) => ({
width: "460px",
maxWidth: "90%",
display: "flex",
flexDirection: "column",
gap: "12px",
width: '460px',
maxWidth: '90%',
display: 'flex',
flexDirection: 'column',
gap: '12px',
}));
export const InstanceListParent = styled(Box)`
@ -254,18 +260,21 @@ export const InstanceListParent = styled(Box)`
background-color: var(--color-instance-popover-bg);
border: 1px solid rgba(0, 0, 0, 0.1);
`;
export const InstanceListHeader = styled(Box)`
display: flex;
flex-direction: column;
width: 100%;
background-color: var(--color-instance-popover-bg);
`;
export const InstanceFooter = styled(Box)`
display: flex;
flex-direction: column;
width: 100%;
flex-shrink: 0;
`;
export const InstanceListContainer = styled(Box)`
width: 100%;
display: flex;
@ -301,126 +310,132 @@ export const InstanceListContainer = styled(Box)`
}
}
`;
export const InstanceListContainerRowLabelContainer = styled(Box)(
({ theme }) => ({
width: "100%",
display: "flex",
alignItems: "center",
gap: "10px",
height: "50px",
width: '100%',
display: 'flex',
alignItems: 'center',
gap: '10px',
height: '50px',
})
);
export const InstanceListContainerRow = styled(Box)(({ theme }) => ({
width: "100%",
display: "flex",
alignItems: "center",
gap: "10px",
height: "50px",
cursor: "pointer",
transition: "0.2s background",
"&:hover": {
background: "rgba(67, 68, 72, 1)",
width: '100%',
display: 'flex',
alignItems: 'center',
gap: '10px',
height: '50px',
cursor: 'pointer',
transition: '0.2s background',
'&:hover': {
background: 'rgba(67, 68, 72, 1)',
},
flexShrink: 0,
}));
export const InstanceListContainerRowCheck = styled(Box)(({ theme }) => ({
width: "47px",
display: "flex",
alignItems: "center",
justifyContent: "center",
width: '47px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}));
export const InstanceListContainerRowMain = styled(Box)(({ theme }) => ({
display: "flex",
justifyContent: "space-between",
width: "100%",
alignItems: "center",
paddingRight: "30px",
overflow: "hidden",
display: 'flex',
justifyContent: 'space-between',
width: '100%',
alignItems: 'center',
paddingRight: '30px',
overflow: 'hidden',
}));
export const CloseParent = styled(Box)(({ theme }) => ({
display: "flex",
alignItems: "center",
gap: "20px",
display: 'flex',
alignItems: 'center',
gap: '20px',
}));
export const InstanceListContainerRowMainP = styled(Typography)(
({ theme }) => ({
fontWeight: 500,
fontSize: "16px",
textOverflow: "ellipsis",
overflow: "hidden",
fontSize: '16px',
textOverflow: 'ellipsis',
overflow: 'hidden',
})
);
export const InstanceListContainerRowCheckIcon = styled("img")({
width: "auto",
height: "auto",
userSelect: "none",
objectFit: "contain",
export const InstanceListContainerRowCheckIcon = styled('img')({
width: 'auto',
height: 'auto',
userSelect: 'none',
objectFit: 'contain',
});
export const InstanceListContainerRowGroupIcon = styled("img")({
width: "auto",
height: "auto",
userSelect: "none",
objectFit: "contain",
export const InstanceListContainerRowGroupIcon = styled('img')({
width: 'auto',
height: 'auto',
userSelect: 'none',
objectFit: 'contain',
});
export const TypeInAliasTextfield = styled(TextField)({
width: "340px", // Adjust the width as needed
borderRadius: "5px",
backgroundColor: "rgba(30, 30, 32, 1)",
border: "none",
outline: "none",
width: '340px', // Adjust the width as needed
borderRadius: '5px',
backgroundColor: 'rgba(30, 30, 32, 1)',
border: 'none',
outline: 'none',
input: {
fontSize: 16,
color: "white",
"&::placeholder": {
color: 'white',
'&::placeholder': {
fontSize: 16,
color: "rgba(255, 255, 255, 0.2)",
color: 'rgba(255, 255, 255, 0.2)',
},
border: "none",
outline: "none",
padding: "10px",
border: 'none',
outline: 'none',
padding: '10px',
},
"& .MuiOutlinedInput-root": {
"& fieldset": {
border: "none",
'& .MuiOutlinedInput-root': {
'& fieldset': {
border: 'none',
},
"&:hover fieldset": {
border: "none",
'&:hover fieldset': {
border: 'none',
},
"&.Mui-focused fieldset": {
border: "none",
'&.Mui-focused fieldset': {
border: 'none',
},
},
"& .MuiInput-underline:before": {
borderBottom: "none",
'& .MuiInput-underline:before': {
borderBottom: 'none',
},
"& .MuiInput-underline:hover:not(.Mui-disabled):before": {
borderBottom: "none",
'& .MuiInput-underline:hover:not(.Mui-disabled):before': {
borderBottom: 'none',
},
"& .MuiInput-underline:after": {
borderBottom: "none",
'& .MuiInput-underline:after': {
borderBottom: 'none',
},
});
export const NewMessageCloseImg = styled("img")({
width: "auto",
height: "auto",
userSelect: "none",
objectFit: "contain",
cursor: "pointer",
export const NewMessageCloseImg = styled('img')({
width: 'auto',
height: 'auto',
userSelect: 'none',
objectFit: 'contain',
cursor: 'pointer',
});
export const NewMessageHeaderP = styled(Typography)(({ theme }) => ({
fontSize: "18px",
fontSize: '18px',
fontWeight: 600,
}));
export const NewMessageInputRow = styled(Box)(({ theme }) => ({
display: "flex",
alignItems: "center",
justifyContent: "space-between",
borderBottom: "3px solid rgba(237, 239, 241, 1)",
width: "100%",
paddingBottom: "6px",
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
borderBottom: '3px solid rgba(237, 239, 241, 1)',
width: '100%',
paddingBottom: '6px',
}));
export const NewMessageInputLabelP = styled(Typography)`
color: rgba(84, 84, 84, 0.7);
@ -444,25 +459,25 @@ export const AliasLabelP = styled(Typography)`
}
`;
export const NewMessageAliasContainer = styled(Box)(({ theme }) => ({
display: "flex",
alignItems: "center",
gap: "12px",
display: 'flex',
alignItems: 'center',
gap: '12px',
}));
export const AttachmentContainer = styled(Box)(({ theme }) => ({
height: "36px",
width: "100%",
display: "flex",
alignItems: "center",
height: '36px',
width: '100%',
display: 'flex',
alignItems: 'center',
}));
export const NewMessageAttachmentImg = styled("img")({
width: "auto",
height: "auto",
userSelect: "none",
objectFit: "contain",
cursor: "pointer",
padding: "10px",
border: "1px dashed #646464",
export const NewMessageAttachmentImg = styled('img')({
width: 'auto',
height: 'auto',
userSelect: 'none',
objectFit: 'contain',
cursor: 'pointer',
padding: '10px',
border: '1px dashed #646464',
});
export const NewMessageSendButton = styled(Box)`
@ -582,34 +597,36 @@ export const ShowMessageButtonP = styled(Typography)`
color: white;
`;
export const ShowMessageButtonImg = styled("img")({
width: "auto",
height: "auto",
userSelect: "none",
objectFit: "contain",
cursor: "pointer",
export const ShowMessageButtonImg = styled('img')({
width: 'auto',
height: 'auto',
userSelect: 'none',
objectFit: 'contain',
cursor: 'pointer',
});
export const MailAttachmentImg = styled("img")({
width: "auto",
height: "auto",
userSelect: "none",
objectFit: "contain",
export const MailAttachmentImg = styled('img')({
width: 'auto',
height: 'auto',
userSelect: 'none',
objectFit: 'contain',
});
export const AliasAvatarImg = styled("img")({
width: "auto",
height: "auto",
userSelect: "none",
objectFit: "contain",
export const AliasAvatarImg = styled('img')({
width: 'auto',
height: 'auto',
userSelect: 'none',
objectFit: 'contain',
});
export const MoreImg = styled("img")({
width: "auto",
height: "auto",
userSelect: "none",
objectFit: "contain",
transition: "0.2s all",
"&:hover": {
transform: "scale(1.3)",
export const MoreImg = styled('img')({
width: 'auto',
height: 'auto',
userSelect: 'none',
objectFit: 'contain',
transition: '0.2s all',
'&:hover': {
transform: 'scale(1.3)',
},
});
@ -625,17 +642,19 @@ export const MoreP = styled(Typography)`
letter-spacing: -0.16px;
white-space: nowrap;
`;
export const ThreadContainerFullWidth = styled(Box)(({ theme }) => ({
display: "flex",
flexDirection: "column",
width: "100%",
alignItems: "center",
display: 'flex',
flexDirection: 'column',
width: '100%',
alignItems: 'center',
}));
export const ThreadContainer = styled(Box)(({ theme }) => ({
display: "flex",
flexDirection: "column",
width: "100%",
maxWidth: "95%",
display: 'flex',
flexDirection: 'column',
width: '100%',
maxWidth: '95%',
}));
export const GroupNameP = styled(Typography)`
@ -648,130 +667,131 @@ export const GroupNameP = styled(Typography)`
`;
export const AllThreadP = styled(Typography)`
color: #FFF;
font-size: 20px;
font-style: normal;
font-weight: 400;
line-height: 120%; /* 24px */
letter-spacing: 0.15px;
color: #fff;
font-size: 20px;
font-style: normal;
font-weight: 400;
line-height: 120%; /* 24px */
letter-spacing: 0.15px;
`;
export const SingleThreadParent = styled(Box)`
border-radius: 35px 4px 4px 35px;
position: relative;
background: #434448;
display: flex;
padding: 13px;
cursor: pointer;
margin-bottom: 5px;
height: 76px;
align-items:center;
transition: 0.2s all;
&:hover {
background: rgba(255, 255, 255, 0.20)
}
border-radius: 35px 4px 4px 35px;
position: relative;
background: #434448;
display: flex;
padding: 13px;
cursor: pointer;
margin-bottom: 5px;
height: 76px;
align-items: center;
transition: 0.2s all;
&:hover {
background: rgba(255, 255, 255, 0.2);
}
`;
export const SingleTheadMessageParent = styled(Box)`
border-radius: 35px 4px 4px 35px;
background: #434448;
display: flex;
padding: 13px;
cursor: pointer;
margin-bottom: 5px;
height: 76px;
align-items:center;
export const SingleTheadMessageParent = styled(Box)`
border-radius: 35px 4px 4px 35px;
background: #434448;
display: flex;
padding: 13px;
cursor: pointer;
margin-bottom: 5px;
height: 76px;
align-items: center;
`;
export const ThreadInfoColumn = styled(Box)(({ theme }) => ({
display: "flex",
flexDirection: "column",
width: "170px",
display: 'flex',
flexDirection: 'column',
width: '170px',
gap: '2px',
marginLeft: '10px',
height: '100%',
justifyContent: 'center'
justifyContent: 'center',
}));
export const ThreadInfoColumnNameP = styled(Typography)`
color: #FFF;
font-family: Roboto;
font-size: 16px;
font-style: normal;
font-weight: 900;
line-height: normal;
white-space: nowrap;
color: #fff;
font-family: Roboto;
font-size: 16px;
font-style: normal;
font-weight: 900;
line-height: normal;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
`;
export const ThreadInfoColumnbyP = styled('span')`
color: rgba(255, 255, 255, 0.80);
font-family: Roboto;
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: normal;
color: rgba(255, 255, 255, 0.8);
font-family: Roboto;
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: normal;
`;
export const ThreadInfoColumnTime = styled(Typography)`
color: rgba(255, 255, 255, 0.80);
font-family: Roboto;
font-size: 15px;
font-style: normal;
font-weight: 500;
line-height: normal;
`
color: rgba(255, 255, 255, 0.8);
font-family: Roboto;
font-size: 15px;
font-style: normal;
font-weight: 500;
line-height: normal;
`;
export const ThreadSingleTitle = styled(Typography)`
color: #FFF;
font-family: Roboto;
font-size: 23px;
font-style: normal;
font-weight: 700;
line-height: normal;
white-space: wrap;
color: #fff;
font-family: Roboto;
font-size: 23px;
font-style: normal;
font-weight: 700;
line-height: normal;
white-space: wrap;
text-overflow: ellipsis;
overflow: hidden;
`
`;
export const ThreadSingleLastMessageP = styled(Typography)`
color: #FFF;
font-family: Roboto;
font-size: 12px;
font-style: normal;
font-weight: 600;
line-height: normal;
`
color: #fff;
font-family: Roboto;
font-size: 12px;
font-style: normal;
font-weight: 600;
line-height: normal;
`;
export const ThreadSingleLastMessageSpanP = styled('span')`
color: #FFF;
font-family: Roboto;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: normal;
color: #fff;
font-family: Roboto;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: normal;
`;
export const GroupContainer = styled(Box)`
position: relative;
overflow: auto;
width: 100%;
`
position: relative;
overflow: auto;
width: 100%;
`;
export const CloseContainer = styled(Box)(({ theme }) => ({
display: "flex",
width: "50px",
overflow: "hidden",
alignItems: "center",
cursor: "pointer",
transition: "0.2s background-color",
justifyContent: "center",
display: 'flex',
width: '50px',
overflow: 'hidden',
alignItems: 'center',
cursor: 'pointer',
transition: '0.2s background-color',
justifyContent: 'center',
position: 'absolute',
top: '0px',
right: '0px',
height: '50px',
borderRadius: '0px 12px 0px 0px',
"&:hover": {
backgroundColor: "rgba(162, 31, 31, 1)",
'&:hover': {
backgroundColor: 'rgba(162, 31, 31, 1)',
},
}));

View File

@ -191,7 +191,7 @@ export const NewThread = ({
}
if (!groupInfo) {
errorMsg = 'Cannot access group information';
}
} // TODO translate
// if (!description) missingFields.push('subject')
if (missingFields.length > 0) {

View File

@ -1,18 +1,24 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { createEditor} from 'slate';
import { withReact, Slate, Editable, RenderElementProps, RenderLeafProps } from 'slate-react';
import { createEditor } from 'slate';
import {
withReact,
Slate,
Editable,
RenderElementProps,
RenderLeafProps,
} from 'slate-react';
type ExtendedRenderElementProps = RenderElementProps & { mode?: string }
type ExtendedRenderElementProps = RenderElementProps & { mode?: string };
export const renderElement = ({
attributes,
children,
element,
mode
mode,
}: ExtendedRenderElementProps) => {
switch (element.type) {
case 'block-quote':
return <blockquote {...attributes}>{children}</blockquote>
return <blockquote {...attributes}>{children}</blockquote>;
case 'heading-2':
return (
<h2
@ -22,7 +28,7 @@ export const renderElement = ({
>
{children}
</h2>
)
);
case 'heading-3':
return (
<h3
@ -32,21 +38,21 @@ export const renderElement = ({
>
{children}
</h3>
)
);
case 'code-block':
return (
<pre {...attributes} className="code-block">
<code>{children}</code>
</pre>
)
);
case 'code-line':
return <div {...attributes}>{children}</div>
return <div {...attributes}>{children}</div>;
case 'link':
return (
<a href={element.url} {...attributes}>
{children}
</a>
)
);
default:
return (
<p
@ -56,24 +62,23 @@ export const renderElement = ({
>
{children}
</p>
)
);
}
}
};
export const renderLeaf = ({ attributes, children, leaf }: RenderLeafProps) => {
let el = children
let el = children;
if (leaf.bold) {
el = <strong>{el}</strong>
el = <strong>{el}</strong>;
}
if (leaf.italic) {
el = <em>{el}</em>
el = <em>{el}</em>;
}
if (leaf.underline) {
el = <u>{el}</u>
el = <u>{el}</u>;
}
if (leaf.link) {
@ -81,39 +86,35 @@ export const renderLeaf = ({ attributes, children, leaf }: RenderLeafProps) => {
<a href={leaf.link} {...attributes}>
{el}
</a>
)
);
}
return <span {...attributes}>{el}</span>
}
return <span {...attributes}>{el}</span>;
};
interface ReadOnlySlateProps {
content: any
mode?: string
content: any;
mode?: string;
}
const ReadOnlySlate: React.FC<ReadOnlySlateProps> = ({ content, mode }) => {
const [load, setLoad] = useState(false)
const editor = useMemo(() => withReact(createEditor()), [])
const value = useMemo(() => content, [content])
const [load, setLoad] = useState(false);
const editor = useMemo(() => withReact(createEditor()), []);
const value = useMemo(() => content, [content]);
const performUpdate = useCallback(async()=> {
setLoad(true)
await new Promise<void>((res)=> {
const performUpdate = useCallback(async () => {
setLoad(true);
await new Promise<void>((res) => {
setTimeout(() => {
res()
res();
}, 250);
})
setLoad(false)
}, [])
useEffect(()=> {
});
setLoad(false);
}, []);
useEffect(() => {
performUpdate();
}, [value]);
performUpdate()
}, [value])
if(load) return null
if (load) return null;
return (
<Slate editor={editor} value={value} onChange={() => {}}>
@ -123,7 +124,7 @@ const ReadOnlySlate: React.FC<ReadOnlySlateProps> = ({ content, mode }) => {
renderLeaf={renderLeaf}
/>
</Slate>
)
}
);
};
export default ReadOnlySlate;

View File

@ -1,8 +1,8 @@
import React, { useState } from "react";
import { Avatar, Box, IconButton } from "@mui/material";
import DOMPurify from "dompurify";
import { useState } from 'react';
import { Avatar, Box, IconButton } from '@mui/material';
import DOMPurify from 'dompurify';
import FormatQuoteIcon from '@mui/icons-material/FormatQuote';
import MoreSVG from '../../../assets/svgs/More.svg'
import MoreSVG from '../../../assets/svgs/More.svg';
import {
MoreImg,
@ -11,20 +11,18 @@ import {
ThreadInfoColumn,
ThreadInfoColumnNameP,
ThreadInfoColumnTime,
} from "./Mail-styles";
import { Spacer } from "../../../common/Spacer";
import { DisplayHtml } from "./DisplayHtml";
import { formatTimestampForum } from "../../../utils/time";
import ReadOnlySlate from "./ReadOnlySlate";
import { MessageDisplay } from "../../Chat/MessageDisplay";
import { getBaseApi } from "../../../background";
import { getBaseApiReact } from "../../../App";
import { WrapperUserAction } from "../../WrapperUserAction";
} from './Mail-styles';
import { Spacer } from '../../../common/Spacer';
import { formatTimestampForum } from '../../../utils/time';
import ReadOnlySlate from './ReadOnlySlate';
import { MessageDisplay } from '../../Chat/MessageDisplay';
import { getBaseApiReact } from '../../../App';
import { WrapperUserAction } from '../../WrapperUserAction';
export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => {
const [expandAttachments, setExpandAttachments] = useState<boolean>(false);
let cleanHTML = "";
let cleanHTML = '';
if (message?.htmlContent) {
cleanHTML = DOMPurify.sanitize(message.htmlContent);
}
@ -32,79 +30,94 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => {
return (
<SingleTheadMessageParent
sx={{
height: "auto",
alignItems: "flex-start",
cursor: "default",
borderRadius: '35px 4px 4px 4px'
height: 'auto',
alignItems: 'flex-start',
cursor: 'default',
borderRadius: '35px 4px 4px 4px',
}}
>
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
width: '100%'
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
width: '100%',
}}
>
<Box
sx={{
display: "flex",
alignItems: "flex-start",
gap: "10px",
display: 'flex',
alignItems: 'flex-start',
gap: '10px',
}}
>
<WrapperUserAction disabled={myName === message?.name} address={undefined} name={message?.name}>
<Avatar sx={{
height: '50px',
width: '50px'
}} src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${message?.name}/qortal_avatar?async=true`} alt={message?.name}>{message?.name?.charAt(0)}</Avatar>
</WrapperUserAction>
<WrapperUserAction
disabled={myName === message?.name}
address={undefined}
name={message?.name}
>
<Avatar
sx={{
height: '50px',
width: '50px',
}}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${message?.name}/qortal_avatar?async=true`}
alt={message?.name}
>
{message?.name?.charAt(0)}
</Avatar>
</WrapperUserAction>
<ThreadInfoColumn>
<WrapperUserAction disabled={myName === message?.name} address={undefined} name={message?.name}>
<ThreadInfoColumnNameP>{message?.name}</ThreadInfoColumnNameP>
<WrapperUserAction
disabled={myName === message?.name}
address={undefined}
name={message?.name}
>
<ThreadInfoColumnNameP>{message?.name}</ThreadInfoColumnNameP>
</WrapperUserAction>
<ThreadInfoColumnTime>
{formatTimestampForum(message?.created)}
</ThreadInfoColumnTime>
</ThreadInfoColumn>
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
}}
>
{message?.attachments?.length > 0 && (
<Box
sx={{
width: "100%",
marginTop: "10px",
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
}}
>
{message?.attachments
.map((file: any, index: number) => {
const isFirst = index === 0
return (
<Box
sx={{
display: expandAttachments ? "flex" : !expandAttachments && isFirst ? 'flex' : 'none',
alignItems: "center",
justifyContent: "flex-start",
width: "100%",
}}
>
{message?.attachments?.length > 0 && (
<Box
sx={{
width: '100%',
marginTop: '10px',
}}
>
{message?.attachments.map((file: any, index: number) => {
const isFirst = index === 0;
return (
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "5px",
cursor: "pointer",
width: "auto",
display: expandAttachments
? 'flex'
: !expandAttachments && isFirst
? 'flex'
: 'none',
alignItems: 'center',
justifyContent: 'flex-start',
width: '100%',
}}
>
{/* <FileElement
<Box
sx={{
display: 'flex',
alignItems: 'center',
gap: '5px',
cursor: 'pointer',
width: 'auto',
}}
>
{/* <FileElement
fileInfo={{ ...file, mimeTypeSaved: file?.type }}
title={file?.filename}
mode="mail"
@ -125,78 +138,87 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => {
{file?.originalFilename || file?.filename}
</Typography>
</FileElement> */}
{message?.attachments?.length > 1 && isFirst && (
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "5px",
}}
onClick={() => {
setExpandAttachments(prev => !prev);
}}
>
<MoreImg
{message?.attachments?.length > 1 && isFirst && (
<Box
sx={{
marginLeft: "5px",
transform: expandAttachments
? "rotate(180deg)"
: "unset",
display: 'flex',
alignItems: 'center',
gap: '5px',
}}
src={MoreSVG}
/>
<MoreP>
{expandAttachments ? 'hide' : `(${message?.attachments?.length - 1} more)`}
</MoreP>
</Box>
)}
onClick={() => {
setExpandAttachments((prev) => !prev);
}}
>
<MoreImg
sx={{
marginLeft: '5px',
transform: expandAttachments
? 'rotate(180deg)'
: 'unset',
}}
src={MoreSVG}
/>
<MoreP>
{expandAttachments
? 'hide'
: `(${message?.attachments?.length - 1} more)`}
</MoreP>
</Box>
)}
</Box>
</Box>
</Box>
);
})
}
</Box>
)}
</div>
);
})}
</Box>
)}
</div>
</Box>
<Spacer height="20px" />
{message?.reply?.textContentV2 && (
<>
<Box sx={{
width: '100%',
opacity: 0.7,
borderRadius: '5px',
border: '1px solid gray',
boxSizing: 'border-box',
padding: '5px'
}}>
<Box
sx={{
width: '100%',
opacity: 0.7,
borderRadius: '5px',
border: '1px solid gray',
boxSizing: 'border-box',
padding: '5px',
}}
>
<Box
sx={{
display: "flex",
alignItems: "flex-start",
gap: "10px",
}}
>
<Avatar sx={{
height: '30px',
width: '30px'
}} src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${message?.reply?.name}/qortal_avatar?async=true`} alt={message?.reply?.name}>{message?.reply?.name?.charAt(0)}</Avatar>
<ThreadInfoColumn>
<ThreadInfoColumnNameP sx={{
fontSize: '14px'
}}>{message?.reply?.name}</ThreadInfoColumnNameP>
</ThreadInfoColumn>
</Box>
<MessageDisplay htmlContent={message?.reply?.textContentV2} />
</Box>
<Spacer height="20px" />
sx={{
display: 'flex',
alignItems: 'flex-start',
gap: '10px',
}}
>
<Avatar
sx={{
height: '30px',
width: '30px',
}}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${message?.reply?.name}/qortal_avatar?async=true`}
alt={message?.reply?.name}
>
{message?.reply?.name?.charAt(0)}
</Avatar>
<ThreadInfoColumn>
<ThreadInfoColumnNameP
sx={{
fontSize: '14px',
}}
>
{message?.reply?.name}
</ThreadInfoColumnNameP>
</ThreadInfoColumn>
</Box>
<MessageDisplay htmlContent={message?.reply?.textContentV2} />
</Box>
<Spacer height="20px" />
</>
)}
{message?.textContent && (
@ -208,22 +230,18 @@ export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => {
{message?.htmlContent && (
<div dangerouslySetInnerHTML={{ __html: cleanHTML }} />
)}
<Box sx={{
width: '100%',
display: 'flex',
justifyContent: 'flex-end'
}}>
<IconButton
onClick={() => openNewPostWithQuote(message)}
<Box
sx={{
width: '100%',
display: 'flex',
justifyContent: 'flex-end',
}}
>
<FormatQuoteIcon />
</IconButton>
<IconButton onClick={() => openNewPostWithQuote(message)}>
<FormatQuoteIcon />
</IconButton>
</Box>
</Box>
</SingleTheadMessageParent>
);
};

View File

@ -1,32 +1,33 @@
import React from "react";
import ReactQuill, { Quill } from "react-quill";
import "react-quill/dist/quill.snow.css";
import ImageResize from "quill-image-resize-module-react";
import './texteditor.css'
Quill.register("modules/imageResize", ImageResize);
import ReactQuill, { Quill } from 'react-quill';
import 'react-quill/dist/quill.snow.css';
import ImageResize from 'quill-image-resize-module-react';
import './texteditor.css';
Quill.register('modules/imageResize', ImageResize);
const modules = {
imageResize: {
parchment: Quill.import("parchment"),
modules: ["Resize", "DisplaySize"],
parchment: Quill.import('parchment'),
modules: ['Resize', 'DisplaySize'],
},
toolbar: [
["bold", "italic", "underline", "strike"], // styled text
["blockquote", "code-block"], // blocks
['bold', 'italic', 'underline', 'strike'], // styled text
['blockquote', 'code-block'], // blocks
[{ header: 1 }, { header: 2 }], // custom button values
[{ list: "ordered" }, { list: "bullet" }], // lists
[{ script: "sub" }, { script: "super" }], // superscript/subscript
[{ indent: "-1" }, { indent: "+1" }], // outdent/indent
[{ direction: "rtl" }], // text direction
[{ size: ["small", false, "large", "huge"] }], // custom dropdown
[{ list: 'ordered' }, { list: 'bullet' }], // lists
[{ script: 'sub' }, { script: 'super' }], // superscript/subscript
[{ indent: '-1' }, { indent: '+1' }], // outdent/indent
[{ direction: 'rtl' }], // text direction
[{ size: ['small', false, 'large', 'huge'] }], // custom dropdown
[{ header: [1, 2, 3, 4, 5, 6, false] }], // custom button values
[{ color: [] }, { background: [] }], // dropdown with defaults
[{ font: [] }], // font family
[{ align: [] }], // text align
["clean"], // remove formatting
['clean'], // remove formatting
// ["image"], // image
],
};
export const TextEditor = ({ inlineContent, setInlineContent }: any) => {
return (
<ReactQuill

View File

@ -467,6 +467,7 @@ export const Thread = ({
}
setMessages(fullArrayMsg);
} catch (error) {
console.log(error);
} finally {
}
},
@ -700,7 +701,7 @@ export const Thread = ({
);
}}
disabled={!hasPreviousPage}
variant="contained"
variant="contained" // TODO translate
>
Previous
</Button>

View File

@ -2218,7 +2218,8 @@ export const Group = ({
}}
>
No group selected
</Typography>
</Typography>{' '}
// TODO translate
</Box>
)}
@ -2235,39 +2236,39 @@ export const Group = ({
: '0px',
}}
>
<DesktopHeader
isPrivate={isPrivate}
selectedGroup={selectedGroup}
groupSection={groupSection}
isUnread={isUnread}
goToAnnouncements={goToAnnouncements}
isUnreadChat={isUnreadChat}
goToChat={goToChat}
goToThreads={goToThreads}
setOpenManageMembers={setOpenManageMembers}
groupChatHasUnread={groupChatHasUnread}
groupsAnnHasUnread={groupsAnnHasUnread}
directChatHasUnread={directChatHasUnread}
chatMode={chatMode}
openDrawerGroups={openDrawerGroups}
goToHome={goToHome}
setIsOpenDrawerProfile={setIsOpenDrawerProfile}
mobileViewMode={mobileViewMode}
setMobileViewMode={setMobileViewMode}
setMobileViewModeKeepOpen={setMobileViewModeKeepOpen}
hasUnreadGroups={groupChatHasUnread || groupsAnnHasUnread}
hasUnreadDirects={directChatHasUnread}
myName={userInfo?.name || null}
isHome={groupSection === 'home'}
isGroups={desktopSideView === 'groups'}
isDirects={desktopSideView === 'directs'}
setDesktopSideView={setDesktopSideView}
hasUnreadAnnouncements={isUnread}
isAnnouncement={groupSection === 'announcement'}
isChat={groupSection === 'chat'}
hasUnreadChat={isUnreadChat}
setGroupSection={setGroupSection}
isForum={groupSection === 'forum'}
<DesktopHeader
isPrivate={isPrivate}
selectedGroup={selectedGroup}
groupSection={groupSection}
isUnread={isUnread}
goToAnnouncements={goToAnnouncements}
isUnreadChat={isUnreadChat}
goToChat={goToChat}
goToThreads={goToThreads}
setOpenManageMembers={setOpenManageMembers}
groupChatHasUnread={groupChatHasUnread}
groupsAnnHasUnread={groupsAnnHasUnread}
directChatHasUnread={directChatHasUnread}
chatMode={chatMode}
openDrawerGroups={openDrawerGroups}
goToHome={goToHome}
setIsOpenDrawerProfile={setIsOpenDrawerProfile}
mobileViewMode={mobileViewMode}
setMobileViewMode={setMobileViewMode}
setMobileViewModeKeepOpen={setMobileViewModeKeepOpen}
hasUnreadGroups={groupChatHasUnread || groupsAnnHasUnread}
hasUnreadDirects={directChatHasUnread}
myName={userInfo?.name || null}
isHome={groupSection === 'home'}
isGroups={desktopSideView === 'groups'}
isDirects={desktopSideView === 'directs'}
setDesktopSideView={setDesktopSideView}
hasUnreadAnnouncements={isUnread}
isAnnouncement={groupSection === 'announcement'}
isChat={groupSection === 'chat'}
hasUnreadChat={isUnreadChat}
setGroupSection={setGroupSection}
isForum={groupSection === 'forum'}
/>
<Box

View File

@ -69,7 +69,7 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => {
<Typography
sx={{
fontSize: '1rem',
}}
}} // TODO translate
>
Group Invites{' '}
{groupsWithJoinRequests?.length > 0 &&

View File

@ -82,6 +82,7 @@ export const GroupJoinRequests = ({
);
setGroupsWithJoinRequests(res);
} catch (error) {
console.log(error);
} finally {
setLoading(false);
}
@ -138,7 +139,7 @@ export const GroupJoinRequests = ({
<Typography
sx={{
fontSize: '1rem',
}}
}} // TODO translate
>
Join Requests{' '}
{filteredJoinRequests?.filter((group) => group?.data?.length > 0)

View File

@ -1,22 +1,27 @@
import React, { useState } from "react";
import { useState } from 'react';
import {
Button,
Menu,
MenuItem,
ListItemIcon,
ListItemText,
Badge,
Box,
} from "@mui/material";
import ForumIcon from "@mui/icons-material/Forum";
import GroupIcon from "@mui/icons-material/Group";
import { ArrowDownIcon } from "../../assets/Icons/ArrowDownIcon";
import { NotificationIcon2 } from "../../assets/Icons/NotificationIcon2";
import { ChatIcon } from "../../assets/Icons/ChatIcon";
import { ThreadsIcon } from "../../assets/Icons/ThreadsIcon";
import { MembersIcon } from "../../assets/Icons/MembersIcon";
} from '@mui/material';
import { ArrowDownIcon } from '../../assets/Icons/ArrowDownIcon';
import { NotificationIcon2 } from '../../assets/Icons/NotificationIcon2';
import { ChatIcon } from '../../assets/Icons/ChatIcon';
import { ThreadsIcon } from '../../assets/Icons/ThreadsIcon';
import { MembersIcon } from '../../assets/Icons/MembersIcon';
export const GroupMenu = ({ setGroupSection, groupSection, setOpenManageMembers, goToAnnouncements, goToChat, hasUnreadChat, hasUnreadAnnouncements }) => {
export const GroupMenu = ({
setGroupSection,
groupSection,
setOpenManageMembers,
goToAnnouncements,
goToChat,
hasUnreadChat,
hasUnreadAnnouncements,
}) => {
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
@ -31,170 +36,217 @@ export const GroupMenu = ({ setGroupSection, groupSection, setOpenManageMembers,
return (
<Box
sx={{
width: "100%",
display: "flex",
justifyContent: "center",
width: '100%',
display: 'flex',
justifyContent: 'center',
marginTop: '14px',
marginBottom: '14px'
marginBottom: '14px',
}}
>
<Button
aria-controls={open ? "home-menu" : undefined}
aria-controls={open ? 'home-menu' : undefined}
aria-haspopup="true"
aria-expanded={open ? "true" : undefined}
aria-expanded={open ? 'true' : undefined}
onClick={handleClick}
variant="contained"
sx={{
backgroundColor: "var(--bg-primary)",
width: "148px",
borderRadius: "5px",
fontSize: "12px",
backgroundColor: 'var(--bg-primary)',
width: '148px',
borderRadius: '5px',
fontSize: '12px',
fontWeight: 600,
color: "#fff",
textTransform: "none",
color: '#fff',
textTransform: 'none',
padding: '5px',
height: '25px'
height: '25px',
}}
>
<Box
sx={{
display: "flex",
gap: "6px",
alignItems: "center",
justifyContent: "space-between",
width: '100%'
display: 'flex',
gap: '6px',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
}}
>
<Box
sx={{
display: "flex",
gap: "6px",
alignItems: "center",
display: 'flex',
gap: '6px',
alignItems: 'center',
}}
>
{groupSection === "announcement" &&(
<> <NotificationIcon2 color={hasUnreadAnnouncements || hasUnreadChat ? 'var(--danger)' : 'white'} /> {" Announcements"}</>
{groupSection === 'announcement' && (
<>
{' '}
<NotificationIcon2
color={
hasUnreadAnnouncements || hasUnreadChat
? 'var(--danger)'
: 'white'
}
/>{' '}
{' Announcements'}
</>
)}
{groupSection === "chat" &&(
<> <ChatIcon color={hasUnreadAnnouncements || hasUnreadChat ? 'var(--danger)' : 'white'} /> {" Group Chats"}</>
{groupSection === 'chat' && (
<>
{' '}
<ChatIcon
color={
hasUnreadAnnouncements || hasUnreadChat
? 'var(--danger)'
: 'white'
}
/>{' '}
{' Group Chats'}
</>
)}
{groupSection === "forum" &&(
<> <ThreadsIcon color={hasUnreadAnnouncements || hasUnreadChat ? 'var(--danger)' : 'white'} /> {" Threads"}</>
{groupSection === 'forum' && (
<>
{' '}
<ThreadsIcon
color={
hasUnreadAnnouncements || hasUnreadChat
? 'var(--danger)'
: 'white'
}
/>{' '}
{' Threads'}
</>
)}
</Box>
<ArrowDownIcon color="white" />
</Box>
</Button>
<Menu
id="home-menu"
anchorEl={anchorEl}
open={open}
onClose={handleClose}
MenuListProps={{
"aria-labelledby": "basic-button",
'aria-labelledby': 'basic-button',
}}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center',
}}
slotProps={{
paper: {
sx: {
backgroundColor: 'var(--bg-primary)',
color: '#fff',
width: '148px',
borderRadius: '5px'
},
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center',
}}
slotProps={{
paper: {
sx: {
backgroundColor: 'var(--bg-primary)',
color: '#fff',
width: '148px',
borderRadius: '5px',
},
}}
sx={{
marginTop: '10px'
}}
},
}}
sx={{
marginTop: '10px',
}}
>
<MenuItem
onClick={() => {
goToChat()
goToChat();
handleClose();
}}
>
<ListItemIcon sx={{
minWidth: '24px !important'
}}>
<ChatIcon color={hasUnreadChat ? 'var(--danger)' : "#fff"} />
<ListItemIcon
sx={{
minWidth: '24px !important',
}}
>
<ChatIcon color={hasUnreadChat ? 'var(--danger)' : '#fff'} />
</ListItemIcon>
<ListItemText sx={{
"& .MuiTypography-root": {
fontSize: "12px",
fontWeight: 600,
color: hasUnreadChat ? "var(--danger)" :"#fff"
},
}} primary="Chat" />
<ListItemText
sx={{
'& .MuiTypography-root': {
fontSize: '12px',
fontWeight: 600,
color: hasUnreadChat ? 'var(--danger)' : '#fff',
},
}}
primary="Chat"
/>
</MenuItem>
<MenuItem
onClick={() => {
goToAnnouncements()
goToAnnouncements();
handleClose();
}}
>
<ListItemIcon sx={{
minWidth: '24px !important'
}}>
<NotificationIcon2 color={hasUnreadAnnouncements ? 'var(--danger)' : "#fff" } />
<ListItemIcon
sx={{
minWidth: '24px !important',
}}
>
<NotificationIcon2
color={hasUnreadAnnouncements ? 'var(--danger)' : '#fff'}
/>
</ListItemIcon>
<ListItemText sx={{
"& .MuiTypography-root": {
fontSize: "12px",
fontWeight: 600,
color: hasUnreadAnnouncements ? "var(--danger)" :"#fff"
},
}} primary="Announcements" />
<ListItemText
sx={{
'& .MuiTypography-root': {
fontSize: '12px',
fontWeight: 600,
color: hasUnreadAnnouncements ? 'var(--danger)' : '#fff',
},
}}
primary="Announcements"
/>
</MenuItem>
<MenuItem
onClick={() => {
setGroupSection("forum");
setGroupSection('forum');
handleClose();
}}
>
<ListItemIcon sx={{
minWidth: '24px !important'
}}>
<ThreadsIcon color={"#fff"} />
<ListItemIcon
sx={{
minWidth: '24px !important',
}}
>
<ThreadsIcon color={'#fff'} />
</ListItemIcon>
<ListItemText sx={{
"& .MuiTypography-root": {
fontSize: "12px",
fontWeight: 600,
},
}} primary="Threads" />
<ListItemText
sx={{
'& .MuiTypography-root': {
fontSize: '12px',
fontWeight: 600,
},
}}
primary="Threads"
/>
</MenuItem>
<MenuItem
onClick={() => {
setOpenManageMembers(true)
setOpenManageMembers(true);
handleClose();
}}
>
<ListItemIcon sx={{
minWidth: '24px !important'
}}>
<MembersIcon sx={{ color: "#fff" }} />
<ListItemIcon
sx={{
minWidth: '24px !important',
}}
>
<MembersIcon sx={{ color: '#fff' }} />
</ListItemIcon>
<ListItemText sx={{
"& .MuiTypography-root": {
fontSize: "12px",
fontWeight: 600,
},
}} primary="Members" />
<ListItemText
sx={{
'& .MuiTypography-root': {
fontSize: '12px',
fontWeight: 600,
},
}}
primary="Members"
/>
</MenuItem>
</Menu>
</Box>

View File

@ -1,13 +1,6 @@
import { LoadingButton } from '@mui/lab';
import {
Box,
Button,
Input,
MenuItem,
Select,
SelectChangeEvent,
} from '@mui/material';
import React, { useState } from 'react';
import { Box, Input, MenuItem, Select, SelectChangeEvent } from '@mui/material';
import { useState } from 'react';
import { Spacer } from '../../common/Spacer';
import { Label } from './AddGroup';
import { getFee } from '../../background';
@ -34,6 +27,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
})
.then((response) => {
if (!response?.error) {
// TODO translate
setInfoSnack({
type: 'success',
message: `Successfully invited ${value}. It may take a couple of minutes for the changes to propagate`,

View File

@ -1,16 +1,31 @@
import React, { useEffect, useRef, useState } from 'react';
import { Avatar, Box, Button, ListItem, ListItemAvatar, ListItemButton, ListItemText, Popover } from '@mui/material';
import { AutoSizer, CellMeasurer, CellMeasurerCache, List } from 'react-virtualized';
import { useEffect, useRef, useState } from 'react';
import {
Avatar,
Box,
ListItem,
ListItemAvatar,
ListItemButton,
ListItemText,
Popover,
} from '@mui/material';
import {
AutoSizer,
CellMeasurer,
CellMeasurerCache,
List,
} from 'react-virtualized';
import { getNameInfo } from './Group';
import { getBaseApi, getFee } from '../../background';
import { getFee } from '../../background';
import { LoadingButton } from '@mui/lab';
import { getBaseApiReact } from '../../App';
export const getMemberInvites = async (groupNumber) => {
const response = await fetch(`${getBaseApiReact()}/groups/bans/${groupNumber}?limit=0`);
const response = await fetch(
`${getBaseApiReact()}/groups/bans/${groupNumber}?limit=0`
);
const groupData = await response.json();
return groupData;
}
};
const getNames = async (listOfMembers, includeNoNames) => {
let members = [];
@ -20,14 +35,14 @@ const getNames = async (listOfMembers, includeNoNames) => {
const name = await getNameInfo(member.offender);
if (name) {
members.push({ ...member, name });
} else if(includeNoNames){
members.push({ ...member, name: name || "" });
} else if (includeNoNames) {
members.push({ ...member, name: name || '' });
}
}
}
}
return members;
}
};
const cache = new CellMeasurerCache({
fixedWidth: true,
@ -49,7 +64,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
} catch (error) {
console.error(error);
}
}
};
useEffect(() => {
if (groupId) {
@ -67,33 +82,36 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
setOpenPopoverIndex(null);
};
const handleCancelBan = async (address)=> {
const handleCancelBan = async (address) => {
try {
const fee = await getFee('CANCEL_GROUP_BAN')
// TODO translate
const fee = await getFee('CANCEL_GROUP_BAN');
await show({
message: "Would you like to perform a CANCEL_GROUP_BAN transaction?" ,
publishFee: fee.fee + ' QORT'
})
setIsLoadingUnban(true)
new Promise((res, rej)=> {
window.sendMessage("cancelBan", {
groupId,
qortalAddress: address,
})
message: 'Would you like to perform a CANCEL_GROUP_BAN transaction?',
publishFee: fee.fee + ' QORT',
});
setIsLoadingUnban(true);
new Promise((res, rej) => {
window
.sendMessage('cancelBan', {
groupId,
qortalAddress: address,
})
.then((response) => {
if (!response?.error) {
res(response);
setIsLoadingUnban(false);
setInfoSnack({
type: "success",
message: "Successfully unbanned user. It may take a couple of minutes for the changes to propagate",
type: 'success',
message:
'Successfully unbanned user. It may take a couple of minutes for the changes to propagate',
});
handlePopoverClose();
setOpenSnack(true);
return;
}
setInfoSnack({
type: "error",
type: 'error',
message: response?.error,
});
setOpenSnack(true);
@ -101,20 +119,18 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
})
.catch((error) => {
setInfoSnack({
type: "error",
message: error.message || "An error occurred",
type: 'error',
message: error.message || 'An error occurred',
});
setOpenSnack(true);
rej(error);
});
})
});
} catch (error) {
} finally {
setIsLoadingUnban(false)
setIsLoadingUnban(false);
}
}
};
const rowRenderer = ({ index, key, parent, style }) => {
const member = bans[index];
@ -135,36 +151,47 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
anchorEl={popoverAnchor}
onClose={handlePopoverClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "center",
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: "top",
horizontal: "center",
vertical: 'top',
horizontal: 'center',
}}
style={{ marginTop: "8px" }}
style={{ marginTop: '8px' }}
>
<Box
<Box
sx={{
width: "325px",
height: "250px",
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "10px",
padding: "10px",
width: '325px',
height: '250px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: '10px',
padding: '10px',
}}
>
<LoadingButton loading={isLoadingUnban}
<LoadingButton
loading={isLoadingUnban}
loadingPosition="start"
variant="contained" onClick={()=> handleCancelBan(member?.offender)}>Cancel Ban</LoadingButton>
variant="contained"
onClick={() => handleCancelBan(member?.offender)}
>
Cancel Ban
</LoadingButton>
</Box>
</Popover>
<ListItemButton onClick={(event) => handlePopoverOpen(event, index)}>
<ListItemButton
onClick={(event) => handlePopoverOpen(event, index)}
>
<ListItemAvatar>
<Avatar
alt={member?.name}
src={member?.name ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true` : ''}
src={
member?.name
? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true`
: ''
}
/>
</ListItemAvatar>
<ListItemText primary={member?.name || member?.offender} />
@ -179,7 +206,16 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
return (
<div>
<p>Ban list</p>
<div style={{ position: 'relative', height: '500px', width: '100%', display: 'flex', flexDirection: 'column', flexShrink: 1 }}>
<div
style={{
position: 'relative',
height: '500px',
width: '100%',
display: 'flex',
flexDirection: 'column',
flexShrink: 1,
}}
>
<AutoSizer>
{({ height, width }) => (
<List
@ -196,4 +232,4 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
</div>
</div>
);
}
};

View File

@ -230,7 +230,7 @@ export const ListOfGroupPromotions = () => {
.catch((error) => {
rej(error.message || 'An error occurred');
});
});
}); // TODO translate
setInfoSnack({
type: 'success',
message:

View File

@ -1,16 +1,31 @@
import React, { useEffect, useRef, useState } from 'react';
import { Avatar, Box, Button, ListItem, ListItemAvatar, ListItemButton, ListItemText, Popover } from '@mui/material';
import { AutoSizer, CellMeasurer, CellMeasurerCache, List } from 'react-virtualized';
import { useEffect, useRef, useState } from 'react';
import {
Avatar,
Box,
ListItem,
ListItemAvatar,
ListItemButton,
ListItemText,
Popover,
} from '@mui/material';
import {
AutoSizer,
CellMeasurer,
CellMeasurerCache,
List,
} from 'react-virtualized';
import { getNameInfo } from './Group';
import { getBaseApi, getFee } from '../../background';
import { getFee } from '../../background';
import { LoadingButton } from '@mui/lab';
import { getBaseApiReact } from '../../App';
export const getMemberInvites = async (groupNumber) => {
const response = await fetch(`${getBaseApiReact()}/groups/invites/group/${groupNumber}?limit=0`);
const response = await fetch(
`${getBaseApiReact()}/groups/invites/group/${groupNumber}?limit=0`
);
const groupData = await response.json();
return groupData;
}
};
const getNames = async (listOfMembers, includeNoNames) => {
let members = [];
@ -20,21 +35,26 @@ const getNames = async (listOfMembers, includeNoNames) => {
const name = await getNameInfo(member.invitee);
if (name) {
members.push({ ...member, name });
} else if(includeNoNames){
members.push({ ...member, name: name || "" });
} else if (includeNoNames) {
members.push({ ...member, name: name || '' });
}
}
}
}
return members;
}
};
const cache = new CellMeasurerCache({
fixedWidth: true,
defaultHeight: 50,
});
export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
export const ListOfInvites = ({
groupId,
setInfoSnack,
setOpenSnack,
show,
}) => {
const [invites, setInvites] = useState([]);
const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to
const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open
@ -50,7 +70,7 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) =>
} catch (error) {
console.error(error);
}
}
};
useEffect(() => {
if (groupId) {
@ -68,24 +88,27 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) =>
setOpenPopoverIndex(null);
};
const handleCancelInvitation = async (address)=> {
const handleCancelInvitation = async (address) => {
try {
const fee = await getFee('CANCEL_GROUP_INVITE')
// TODO translate
const fee = await getFee('CANCEL_GROUP_INVITE');
await show({
message: "Would you like to perform a CANCEL_GROUP_INVITE transaction?" ,
publishFee: fee.fee + ' QORT'
})
setIsLoadingCancelInvite(true)
await new Promise((res, rej)=> {
window.sendMessage("cancelInvitationToGroup", {
groupId,
qortalAddress: address,
})
message: 'Would you like to perform a CANCEL_GROUP_INVITE transaction?',
publishFee: fee.fee + ' QORT',
});
setIsLoadingCancelInvite(true);
await new Promise((res, rej) => {
window
.sendMessage('cancelInvitationToGroup', {
groupId,
qortalAddress: address,
})
.then((response) => {
if (!response?.error) {
setInfoSnack({
type: "success",
message: "Successfully canceled invitation. It may take a couple of minutes for the changes to propagate",
type: 'success',
message:
'Successfully canceled invitation. It may take a couple of minutes for the changes to propagate',
});
setOpenSnack(true);
handlePopoverClose();
@ -94,7 +117,7 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) =>
return;
}
setInfoSnack({
type: "error",
type: 'error',
message: response?.error,
});
setOpenSnack(true);
@ -102,20 +125,18 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) =>
})
.catch((error) => {
setInfoSnack({
type: "error",
message: error.message || "An error occurred",
type: 'error',
message: error.message || 'An error occurred',
});
setOpenSnack(true);
rej(error);
});
})
});
} catch (error) {
} finally {
setIsLoadingCancelInvite(false)
setIsLoadingCancelInvite(false);
}
}
};
const rowRenderer = ({ index, key, parent, style }) => {
const member = invites[index];
@ -136,36 +157,47 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) =>
anchorEl={popoverAnchor}
onClose={handlePopoverClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "center",
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: "top",
horizontal: "center",
vertical: 'top',
horizontal: 'center',
}}
style={{ marginTop: "8px" }}
style={{ marginTop: '8px' }}
>
<Box
<Box
sx={{
width: "325px",
height: "250px",
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "10px",
padding: "10px",
width: '325px',
height: '250px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: '10px',
padding: '10px',
}}
>
<LoadingButton loading={isLoadingCancelInvite}
<LoadingButton
loading={isLoadingCancelInvite}
loadingPosition="start"
variant="contained" onClick={()=> handleCancelInvitation(member?.invitee)}>Cancel Invitation</LoadingButton>
variant="contained"
onClick={() => handleCancelInvitation(member?.invitee)}
>
Cancel Invitation
</LoadingButton>
</Box>
</Popover>
<ListItemButton onClick={(event) => handlePopoverOpen(event, index)}>
<ListItemButton
onClick={(event) => handlePopoverOpen(event, index)}
>
<ListItemAvatar>
<Avatar
alt={member?.name}
src={member?.name ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true` : ''}
src={
member?.name
? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true`
: ''
}
/>
</ListItemAvatar>
<ListItemText primary={member?.name || member?.invitee} />
@ -180,7 +212,16 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) =>
return (
<div>
<p>Invitees list</p>
<div style={{ position: 'relative', height: '500px', width: '100%', display: 'flex', flexDirection: 'column', flexShrink: 1 }}>
<div
style={{
position: 'relative',
height: '500px',
width: '100%',
display: 'flex',
flexDirection: 'column',
flexShrink: 1,
}}
>
<AutoSizer>
{({ height, width }) => (
<List
@ -197,4 +238,4 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) =>
</div>
</div>
);
}
};

View File

@ -1,16 +1,31 @@
import React, { useContext, useEffect, useRef, useState } from 'react';
import { Avatar, Box, Button, ListItem, ListItemAvatar, ListItemButton, ListItemText, Popover } from '@mui/material';
import { AutoSizer, CellMeasurer, CellMeasurerCache, List } from 'react-virtualized';
import { useContext, useEffect, useRef, useState } from 'react';
import {
Avatar,
Box,
ListItem,
ListItemAvatar,
ListItemButton,
ListItemText,
Popover,
} from '@mui/material';
import {
AutoSizer,
CellMeasurer,
CellMeasurerCache,
List,
} from 'react-virtualized';
import { getNameInfo } from './Group';
import { getBaseApi, getFee } from '../../background';
import { LoadingButton } from '@mui/lab';
import { MyContext, getBaseApiReact } from '../../App';
export const getMemberInvites = async (groupNumber) => {
const response = await fetch(`${getBaseApiReact()}/groups/joinrequests/${groupNumber}?limit=0`);
const response = await fetch(
`${getBaseApiReact()}/groups/joinrequests/${groupNumber}?limit=0`
);
const groupData = await response.json();
return groupData;
}
};
const getNames = async (listOfMembers, includeNoNames) => {
let members = [];
@ -19,24 +34,29 @@ const getNames = async (listOfMembers, includeNoNames) => {
if (member.joiner) {
const name = await getNameInfo(member.joiner);
if (name) {
members.push({ ...member, name: name || "" });
} else if(includeNoNames){
members.push({ ...member, name: name || "" });
members.push({ ...member, name: name || '' });
} else if (includeNoNames) {
members.push({ ...member, name: name || '' });
}
}
}
}
return members;
}
};
const cache = new CellMeasurerCache({
fixedWidth: true,
defaultHeight: 50,
});
export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
export const ListOfJoinRequests = ({
groupId,
setInfoSnack,
setOpenSnack,
show,
}) => {
const [invites, setInvites] = useState([]);
const {txList, setTxList} = useContext(MyContext)
const { txList, setTxList } = useContext(MyContext);
const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to
const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open
@ -51,7 +71,7 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
} catch (error) {
console.error(error);
}
}
};
useEffect(() => {
if (groupId) {
@ -69,26 +89,28 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
setOpenPopoverIndex(null);
};
const handleAcceptJoinRequest = async (address)=> {
const handleAcceptJoinRequest = async (address) => {
try {
const fee = await getFee('GROUP_INVITE')
const fee = await getFee('GROUP_INVITE'); // TODO translate
await show({
message: "Would you like to perform a GROUP_INVITE transaction?" ,
publishFee: fee.fee + ' QORT'
})
setIsLoadingAccept(true)
await new Promise((res, rej)=> {
window.sendMessage("inviteToGroup", {
groupId,
qortalAddress: address,
inviteTime: 10800,
})
message: 'Would you like to perform a GROUP_INVITE transaction?',
publishFee: fee.fee + ' QORT',
});
setIsLoadingAccept(true);
await new Promise((res, rej) => {
window
.sendMessage('inviteToGroup', {
groupId,
qortalAddress: address,
inviteTime: 10800,
})
.then((response) => {
if (!response?.error) {
setIsLoadingAccept(false);
setInfoSnack({
type: "success",
message: "Successfully accepted join request. It may take a couple of minutes for the changes to propagate",
type: 'success',
message:
'Successfully accepted join request. It may take a couple of minutes for the changes to propagate',
});
setOpenSnack(true);
handlePopoverClose();
@ -111,7 +133,7 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
}
setInfoSnack({
type: "error",
type: 'error',
message: response?.error,
});
setOpenSnack(true);
@ -119,25 +141,28 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
})
.catch((error) => {
setInfoSnack({
type: "error",
message: error?.message || "An error occurred",
type: 'error',
message: error?.message || 'An error occurred',
});
setOpenSnack(true);
rej(error);
});
})
});
} catch (error) {
} finally {
setIsLoadingAccept(false)
setIsLoadingAccept(false);
}
}
};
const rowRenderer = ({ index, key, parent, style }) => {
const member = invites[index];
const findJoinRequsetInTxList = txList?.find((tx)=> tx?.groupId === groupId && tx?.qortalAddress === member?.joiner && tx?.type === 'join-request-accept')
if(findJoinRequsetInTxList) return null
const findJoinRequsetInTxList = txList?.find(
(tx) =>
tx?.groupId === groupId &&
tx?.qortalAddress === member?.joiner &&
tx?.type === 'join-request-accept'
);
if (findJoinRequsetInTxList) return null;
return (
<CellMeasurer
key={key}
@ -154,36 +179,47 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
anchorEl={popoverAnchor}
onClose={handlePopoverClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "center",
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: "top",
horizontal: "center",
vertical: 'top',
horizontal: 'center',
}}
style={{ marginTop: "8px" }}
style={{ marginTop: '8px' }}
>
<Box
<Box
sx={{
width: "325px",
height: "250px",
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "10px",
padding: "10px",
width: '325px',
height: '250px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: '10px',
padding: '10px',
}}
>
<LoadingButton loading={isLoadingAccept}
<LoadingButton
loading={isLoadingAccept}
loadingPosition="start"
variant="contained" onClick={()=> handleAcceptJoinRequest(member?.joiner)}>Accept</LoadingButton>
variant="contained"
onClick={() => handleAcceptJoinRequest(member?.joiner)}
>
Accept
</LoadingButton>
</Box>
</Popover>
<ListItemButton onClick={(event) => handlePopoverOpen(event, index)}>
<ListItemButton
onClick={(event) => handlePopoverOpen(event, index)}
>
<ListItemAvatar>
<Avatar
alt={member?.name}
src={member?.name ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true` : ''}
src={
member?.name
? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true`
: ''
}
/>
</ListItemAvatar>
<ListItemText primary={member?.name || member?.joiner} />
@ -198,7 +234,16 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
return (
<div>
<p>Join request list</p>
<div style={{ position: 'relative', height: '500px', width: '100%', display: 'flex', flexDirection: 'column', flexShrink: 1 }}>
<div
style={{
position: 'relative',
height: '500px',
width: '100%',
display: 'flex',
flexDirection: 'column',
flexShrink: 1,
}}
>
<AutoSizer>
{({ height, width }) => (
<List
@ -215,4 +260,4 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
</div>
</div>
);
}
};

View File

@ -1,29 +1,29 @@
import {
Avatar,
Box,
Button,
ListItem,
ListItemAvatar,
ListItemButton,
ListItemText,
Popover,
Typography,
} from "@mui/material";
import React, { useRef, useState } from "react";
} from '@mui/material';
import { useRef, useState } from 'react';
import {
AutoSizer,
CellMeasurer,
CellMeasurerCache,
List,
} from "react-virtualized";
import { LoadingButton } from "@mui/lab";
import { getBaseApi, getFee } from "../../background";
import { getBaseApiReact } from "../../App";
} from 'react-virtualized';
import { LoadingButton } from '@mui/lab';
import { getFee } from '../../background';
import { getBaseApiReact } from '../../App';
const cache = new CellMeasurerCache({
fixedWidth: true,
defaultHeight: 50,
});
const ListOfMembers = ({
members,
groupId,
@ -40,7 +40,6 @@ const ListOfMembers = ({
const [isLoadingMakeAdmin, setIsLoadingMakeAdmin] = useState(false);
const [isLoadingRemoveAdmin, setIsLoadingRemoveAdmin] = useState(false);
const listRef = useRef();
const handlePopoverOpen = (event, index) => {
@ -55,23 +54,25 @@ const ListOfMembers = ({
const handleKick = async (address) => {
try {
const fee = await getFee("GROUP_KICK");
const fee = await getFee('GROUP_KICK');
await show({
message: "Would you like to perform a GROUP_KICK transaction?",
publishFee: fee.fee + " QORT",
message: 'Would you like to perform a GROUP_KICK transaction?',
publishFee: fee.fee + ' QORT',
});
setIsLoadingKick(true);
new Promise((res, rej) => {
window.sendMessage("kickFromGroup", {
groupId,
qortalAddress: address,
})
window
.sendMessage('kickFromGroup', {
groupId,
qortalAddress: address,
})
.then((response) => {
if (!response?.error) {
setInfoSnack({
type: "success",
message: "Successfully kicked member from group. It may take a couple of minutes for the changes to propagate",
type: 'success',
message:
'Successfully kicked member from group. It may take a couple of minutes for the changes to propagate',
});
setOpenSnack(true);
handlePopoverClose();
@ -79,7 +80,7 @@ const ListOfMembers = ({
return;
}
setInfoSnack({
type: "error",
type: 'error',
message: response?.error,
});
setOpenSnack(true);
@ -87,38 +88,40 @@ const ListOfMembers = ({
})
.catch((error) => {
setInfoSnack({
type: "error",
message: error.message || "An error occurred",
type: 'error',
message: error.message || 'An error occurred',
});
setOpenSnack(true);
rej(error);
});
});
} catch (error) {
console.log(error);
} finally {
setIsLoadingKick(false);
}
};
const handleBan = async (address) => {
try {
const fee = await getFee("GROUP_BAN");
const fee = await getFee('GROUP_BAN'); // TODO translate
await show({
message: "Would you like to perform a GROUP_BAN transaction?",
publishFee: fee.fee + " QORT",
message: 'Would you like to perform a GROUP_BAN transaction?',
publishFee: fee.fee + ' QORT',
});
setIsLoadingBan(true);
await new Promise((res, rej) => {
window.sendMessage("banFromGroup", {
groupId,
qortalAddress: address,
rBanTime: 0,
})
window
.sendMessage('banFromGroup', {
groupId,
qortalAddress: address,
rBanTime: 0,
})
.then((response) => {
if (!response?.error) {
setInfoSnack({
type: "success",
message: "Successfully banned member from group. It may take a couple of minutes for the changes to propagate",
type: 'success',
message:
'Successfully banned member from group. It may take a couple of minutes for the changes to propagate',
});
setOpenSnack(true);
handlePopoverClose();
@ -126,7 +129,7 @@ const ListOfMembers = ({
return;
}
setInfoSnack({
type: "error",
type: 'error',
message: response?.error,
});
setOpenSnack(true);
@ -134,13 +137,12 @@ const ListOfMembers = ({
})
.catch((error) => {
setInfoSnack({
type: "error",
message: error.message || "An error occurred",
type: 'error',
message: error.message || 'An error occurred',
});
setOpenSnack(true);
rej(error);
});
});
} catch (error) {
} finally {
@ -150,22 +152,24 @@ const ListOfMembers = ({
const makeAdmin = async (address) => {
try {
const fee = await getFee("ADD_GROUP_ADMIN");
const fee = await getFee('ADD_GROUP_ADMIN');
await show({
message: "Would you like to perform a ADD_GROUP_ADMIN transaction?",
publishFee: fee.fee + " QORT",
message: 'Would you like to perform a ADD_GROUP_ADMIN transaction?',
publishFee: fee.fee + ' QORT',
});
setIsLoadingMakeAdmin(true);
await new Promise((res, rej) => {
window.sendMessage("makeAdmin", {
groupId,
qortalAddress: address,
})
window
.sendMessage('makeAdmin', {
groupId,
qortalAddress: address,
})
.then((response) => {
if (!response?.error) {
setInfoSnack({
type: "success",
message: "Successfully made member an admin. It may take a couple of minutes for the changes to propagate",
type: 'success',
message:
'Successfully made member an admin. It may take a couple of minutes for the changes to propagate',
});
setOpenSnack(true);
handlePopoverClose();
@ -173,7 +177,7 @@ const ListOfMembers = ({
return;
}
setInfoSnack({
type: "error",
type: 'error',
message: response?.error,
});
setOpenSnack(true);
@ -181,13 +185,12 @@ const ListOfMembers = ({
})
.catch((error) => {
setInfoSnack({
type: "error",
message: error.message || "An error occurred",
type: 'error',
message: error.message || 'An error occurred',
});
setOpenSnack(true);
rej(error);
});
});
} catch (error) {
} finally {
@ -197,22 +200,24 @@ const ListOfMembers = ({
const removeAdmin = async (address) => {
try {
const fee = await getFee("REMOVE_GROUP_ADMIN");
const fee = await getFee('REMOVE_GROUP_ADMIN');
await show({
message: "Would you like to perform a REMOVE_GROUP_ADMIN transaction?",
publishFee: fee.fee + " QORT",
message: 'Would you like to perform a REMOVE_GROUP_ADMIN transaction?',
publishFee: fee.fee + ' QORT',
});
setIsLoadingRemoveAdmin(true);
await new Promise((res, rej) => {
window.sendMessage("removeAdmin", {
groupId,
qortalAddress: address,
})
window
.sendMessage('removeAdmin', {
groupId,
qortalAddress: address,
})
.then((response) => {
if (!response?.error) {
setInfoSnack({
type: "success",
message: "Successfully removed member as an admin. It may take a couple of minutes for the changes to propagate",
type: 'success',
message:
'Successfully removed member as an admin. It may take a couple of minutes for the changes to propagate',
});
setOpenSnack(true);
handlePopoverClose();
@ -220,7 +225,7 @@ const ListOfMembers = ({
return;
}
setInfoSnack({
type: "error",
type: 'error',
message: response?.error,
});
setOpenSnack(true);
@ -228,13 +233,12 @@ const ListOfMembers = ({
})
.catch((error) => {
setInfoSnack({
type: "error",
message: error.message || "An error occurred",
type: 'error',
message: error.message || 'An error occurred',
});
setOpenSnack(true);
rej(error);
});
});
} catch (error) {
} finally {
@ -260,24 +264,24 @@ const ListOfMembers = ({
anchorEl={popoverAnchor}
onClose={handlePopoverClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "center",
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: "top",
horizontal: "center",
vertical: 'top',
horizontal: 'center',
}}
style={{ marginTop: "8px" }}
style={{ marginTop: '8px' }}
>
<Box
sx={{
width: "325px",
height: "250px",
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "10px",
padding: "10px",
width: '325px',
height: '250px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: '10px',
padding: '10px',
}}
>
{isOwner && (
@ -336,21 +340,28 @@ const ListOfMembers = ({
<ListItemAvatar>
<Avatar
alt={member?.name || member?.member}
src={member?.name ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true` : ''}
src={
member?.name
? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true`
: ''
}
/>
</ListItemAvatar>
<ListItemText
id={""}
id={''}
primary={member?.name || member?.member}
/>
{member?.isAdmin && (
<Typography sx={{
color: 'white',
marginLeft: 'auto'
}}>Admin</Typography>
)}
<Typography
sx={{
color: 'white',
marginLeft: 'auto',
}}
>
Admin
</Typography>
)}
</ListItemButton>
</ListItem>
</div>
)}
@ -363,11 +374,11 @@ const ListOfMembers = ({
<p>Member list</p>
<div
style={{
position: "relative",
height: "500px",
width: "100%",
display: "flex",
flexDirection: "column",
position: 'relative',
height: '500px',
width: '100%',
display: 'flex',
flexDirection: 'column',
flexShrink: 1,
}}
>

View File

@ -42,7 +42,7 @@ export const ListOfThreadPostsWatched = () => {
rej(response.error);
})
.catch((error) => {
rej(error.message || 'An error occurred');
rej(error.message || 'An error occurred'); // TODO translate
});
});
} catch (error) {

View File

@ -105,7 +105,7 @@ export const ManageMembers = ({
rej(response.error);
})
.catch((error) => {
rej(error.message || 'An error occurred');
rej(error.message || 'An error occurred'); // TODO translate
});
});
} catch (error) {

View File

@ -87,7 +87,7 @@ export const QMailMessages = ({ userName, userAddress }) => {
rej(response.error);
})
.catch((error) => {
rej(error.message || 'An error occurred');
rej(error.message || 'An error occurred'); // TODO translate
});
});
} catch (error) {

View File

@ -79,7 +79,7 @@ export const Settings = ({ address, open, setOpen }) => {
if (response?.error) {
console.error('Error adding user settings:', response.error);
} else {
console.log('User settings added successfully');
console.log('User settings added successfully'); // TODO translate
}
})
.catch((error) => {

View File

@ -1,240 +1,266 @@
import { Box, Button, ListItem, ListItemButton, ListItemText, Popover, Typography } from '@mui/material';
import React, { useContext, useEffect, useRef, useState } from 'react'
import { AutoSizer, CellMeasurer, CellMeasurerCache, List } from 'react-virtualized';
import {
Box,
ListItem,
ListItemButton,
ListItemText,
Popover,
Typography,
} from '@mui/material';
import { useContext, useEffect, useRef, useState } from 'react';
import {
AutoSizer,
CellMeasurer,
CellMeasurerCache,
List,
} from 'react-virtualized';
import { MyContext, getBaseApiReact } from '../../App';
import { LoadingButton } from '@mui/lab';
import { getBaseApi, getFee } from '../../background';
import { getFee } from '../../background';
import LockIcon from '@mui/icons-material/Lock';
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
import { Spacer } from "../../common/Spacer";
import { Spacer } from '../../common/Spacer';
const cache = new CellMeasurerCache({
fixedWidth: true,
defaultHeight: 50,
});
fixedWidth: true,
defaultHeight: 50,
});
const getGroupInfo = async (groupId)=> {
const getGroupInfo = async (groupId) => {
const response = await fetch(`${getBaseApiReact()}/groups/` + groupId);
const groupData = await response.json();
if (groupData) {
return groupData
return groupData;
}
}
export const getGroupNames = async (listOfGroups) => {
let groups = [];
if (listOfGroups && Array.isArray(listOfGroups)) {
for (const group of listOfGroups) {
const groupInfo = await getGroupInfo(group.groupId);
if (groupInfo) {
groups.push({ ...group, ...groupInfo });
}
};
export const getGroupNames = async (listOfGroups) => {
let groups = [];
if (listOfGroups && Array.isArray(listOfGroups)) {
for (const group of listOfGroups) {
const groupInfo = await getGroupInfo(group.groupId);
if (groupInfo) {
groups.push({ ...group, ...groupInfo });
}
}
return groups;
}
return groups;
};
export const UserListOfInvites = ({myAddress, setInfoSnack, setOpenSnack}) => {
const {txList, setTxList, show} = useContext(MyContext)
const [invites, setInvites] = useState<any[]>([]);
const [isLoading, setIsLoading] = useState(false);
export const UserListOfInvites = ({
myAddress,
setInfoSnack,
setOpenSnack,
}) => {
const { txList, setTxList, show } = useContext(MyContext);
const [invites, setInvites] = useState<any[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to
const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open
const listRef = useRef();
const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to
const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open
const listRef = useRef();
const getRequests = async () => {
try {
const response = await fetch(`${getBaseApiReact()}/groups/invites/${myAddress}/?limit=0`);
const inviteData = await response.json();
const getRequests = async () => {
try {
const response = await fetch(
`${getBaseApiReact()}/groups/invites/${myAddress}/?limit=0`
);
const inviteData = await response.json();
const resMoreData = await getGroupNames(inviteData)
setInvites(resMoreData);
} catch (error) {
console.error(error);
}
const resMoreData = await getGroupNames(inviteData);
setInvites(resMoreData);
} catch (error) {
console.error(error);
}
};
useEffect(() => {
useEffect(() => {
getRequests();
}, []);
getRequests();
const handlePopoverOpen = (event, index) => {
setPopoverAnchor(event.currentTarget);
setOpenPopoverIndex(index);
};
}, []);
const handlePopoverClose = () => {
setPopoverAnchor(null);
setOpenPopoverIndex(null);
};
const handlePopoverOpen = (event, index) => {
setPopoverAnchor(event.currentTarget);
setOpenPopoverIndex(index);
};
const handleJoinGroup = async (groupId, groupName) => {
try {
const fee = await getFee('JOIN_GROUP'); // TODO translate
await show({
message: 'Would you like to perform an JOIN_GROUP transaction?',
publishFee: fee.fee + ' QORT',
});
const handlePopoverClose = () => {
setPopoverAnchor(null);
setOpenPopoverIndex(null);
};
setIsLoading(true);
const handleJoinGroup = async (groupId, groupName)=> {
try {
const fee = await getFee('JOIN_GROUP')
await show({
message: "Would you like to perform an JOIN_GROUP transaction?" ,
publishFee: fee.fee + ' QORT'
})
setIsLoading(true);
await new Promise((res, rej)=> {
window.sendMessage("joinGroup", {
await new Promise((res, rej) => {
window
.sendMessage('joinGroup', {
groupId,
})
.then((response) => {
if (!response?.error) {
setTxList((prev) => [
{
...response,
type: 'joined-group',
label: `Joined Group ${groupName}: awaiting confirmation`,
labelDone: `Joined Group ${groupName}: success!`,
done: false,
groupId,
},
...prev,
]);
res(response);
setInfoSnack({
type: "success",
message: "Successfully requested to join group. It may take a couple of minutes for the changes to propagate",
});
setOpenSnack(true);
handlePopoverClose();
return;
}
.then((response) => {
if (!response?.error) {
setTxList((prev) => [
{
...response,
type: 'joined-group',
label: `Joined Group ${groupName}: awaiting confirmation`,
labelDone: `Joined Group ${groupName}: success!`,
done: false,
groupId,
},
...prev,
]);
res(response);
setInfoSnack({
type: "error",
message: response?.error,
type: 'success',
message:
'Successfully requested to join group. It may take a couple of minutes for the changes to propagate',
});
setOpenSnack(true);
rej(response.error);
})
.catch((error) => {
setInfoSnack({
type: "error",
message: error.message || "An error occurred",
});
setOpenSnack(true);
rej(error);
handlePopoverClose();
return;
}
setInfoSnack({
type: 'error',
message: response?.error,
});
setOpenSnack(true);
rej(response.error);
})
} catch (error) {
} finally {
setIsLoading(false);
}
.catch((error) => {
setInfoSnack({
type: 'error',
message: error.message || 'An error occurred',
});
setOpenSnack(true);
rej(error);
});
});
} catch (error) {
} finally {
setIsLoading(false);
}
};
const rowRenderer = ({ index, key, parent, style }) => {
const invite = invites[index];
return (
<CellMeasurer
key={key}
cache={cache}
parent={parent}
columnIndex={0}
rowIndex={index}
>
{({ measure }) => (
<div style={style} onLoad={measure}>
<ListItem disablePadding>
<Popover
open={openPopoverIndex === index}
anchorEl={popoverAnchor}
onClose={handlePopoverClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "center",
}}
transformOrigin={{
vertical: "top",
horizontal: "center",
}}
style={{ marginTop: "8px" }}
>
<Box
sx={{
width: "325px",
height: "250px",
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "10px",
padding: "10px",
}}
>
<Typography>Join {invite?.groupName}</Typography>
<LoadingButton
loading={isLoading}
loadingPosition="start"
variant="contained" onClick={()=> handleJoinGroup(invite?.groupId, invite?.groupName)}>Join group</LoadingButton>
</Box>
</Popover>
<ListItemButton onClick={(event) => handlePopoverOpen(event, index)}>
{invite?.isOpen === false && (
<LockIcon sx={{
color: 'var(--green)'
}} />
)}
{invite?.isOpen === true && (
<NoEncryptionGmailerrorredIcon sx={{
color: 'var(--danger)'
}} />
)}
<Spacer width="15px" />
<ListItemText primary={invite?.groupName} secondary={invite?.description} />
</ListItemButton>
</ListItem>
</div>
)}
</CellMeasurer>
);
};
const rowRenderer = ({ index, key, parent, style }) => {
const invite = invites[index];
return (
<Box sx={{
<CellMeasurer
key={key}
cache={cache}
parent={parent}
columnIndex={0}
rowIndex={index}
>
{({ measure }) => (
<div style={style} onLoad={measure}>
<ListItem disablePadding>
<Popover
open={openPopoverIndex === index}
anchorEl={popoverAnchor}
onClose={handlePopoverClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center',
}}
style={{ marginTop: '8px' }}
>
<Box
sx={{
width: '325px',
height: '250px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: '10px',
padding: '10px',
}}
>
<Typography>Join {invite?.groupName}</Typography>
<LoadingButton
loading={isLoading}
loadingPosition="start"
variant="contained"
onClick={() =>
handleJoinGroup(invite?.groupId, invite?.groupName)
}
>
Join group
</LoadingButton>
</Box>
</Popover>
<ListItemButton
onClick={(event) => handlePopoverOpen(event, index)}
>
{invite?.isOpen === false && (
<LockIcon
sx={{
color: 'var(--green)',
}}
/>
)}
{invite?.isOpen === true && (
<NoEncryptionGmailerrorredIcon
sx={{
color: 'var(--danger)',
}}
/>
)}
<Spacer width="15px" />
<ListItemText
primary={invite?.groupName}
secondary={invite?.description}
/>
</ListItemButton>
</ListItem>
</div>
)}
</CellMeasurer>
);
};
return (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
flexGrow: 1
}}>
<p>Invite list</p>
<div
flexGrow: 1,
}}
>
<p>Invite list</p>
<div
style={{
position: "relative",
width: "100%",
display: "flex",
flexDirection: "column",
position: 'relative',
width: '100%',
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
}}
>
<AutoSizer>
{({ height, width }) => (
<List
ref={listRef}
width={width}
height={height}
rowCount={invites.length}
rowHeight={cache.rowHeight}
rowRenderer={rowRenderer}
deferredMeasurementCache={cache}
/>
)}
</AutoSizer>
</div>
</Box>
);
}
<AutoSizer>
{({ height, width }) => (
<List
ref={listRef}
width={width}
height={height}
rowCount={invites.length}
rowHeight={cache.rowHeight}
rowRenderer={rowRenderer}
deferredMeasurementCache={cache}
/>
)}
</AutoSizer>
</div>
</Box>
);
};

View File

@ -88,7 +88,7 @@ export const WalletsAppWrapper = () => {
justifyContent: 'space-between',
}}
>
<Typography>Q-Wallets</Typography>
<Typography>Q-Wallets</Typography> // TODO translate
<ButtonBase onClick={handleClose}>
<CloseIcon
sx={{

View File

@ -1,12 +1,16 @@
import React, { useEffect, useRef } from 'react';
import { getBaseApiReactSocket, pauseAllQueues, resumeAllQueues } from '../../App';
import { useEffect, useRef } from 'react';
import {
getBaseApiReactSocket,
pauseAllQueues,
resumeAllQueues,
} from '../../App';
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
const socketRef = useRef(null); // WebSocket reference
const timeoutIdRef = useRef(null); // Timeout ID reference
const groupSocketTimeoutRef = useRef(null); // Group Socket Timeout reference
const initiateRef = useRef(null)
const initiateRef = useRef(null);
const forceCloseWebSocket = () => {
if (socketRef.current) {
clearTimeout(timeoutIdRef.current);
@ -17,21 +21,20 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
};
const logoutEventFunc = () => {
forceCloseWebSocket()
forceCloseWebSocket();
};
useEffect(() => {
subscribeToEvent("logout-event", logoutEventFunc);
subscribeToEvent('logout-event', logoutEventFunc);
return () => {
unsubscribeFromEvent("logout-event", logoutEventFunc);
unsubscribeFromEvent('logout-event', logoutEventFunc);
};
}, []);
useEffect(() => {
if (!myAddress) return; // Only proceed if myAddress is set
const pingHeads = () => {
try {
if (socketRef.current?.readyState === WebSocket.OPEN) {
@ -53,10 +56,9 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
const currentAddress = myAddress;
try {
if(!initiateRef.current) {
setIsLoadingGroups(true)
pauseAllQueues()
if (!initiateRef.current) {
setIsLoadingGroups(true);
pauseAllQueues();
}
const socketLink = `${getBaseApiReactSocket()}/websockets/chat/active/${currentAddress}?encoding=BASE64&haschatreference=false`;
socketRef.current = new WebSocket(socketLink);
@ -71,34 +73,46 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
clearTimeout(timeoutIdRef.current);
groupSocketTimeoutRef.current = setTimeout(pingHeads, 45000); // Ping every 45 seconds
} else {
if(!initiateRef.current) {
setIsLoadingGroups(false)
initiateRef.current = true
resumeAllQueues()
if (!initiateRef.current) {
setIsLoadingGroups(false);
initiateRef.current = true;
resumeAllQueues();
}
const data = JSON.parse(e.data);
const copyGroups = [...(data?.groups || [])]
const findIndex = copyGroups?.findIndex(item => item?.groupId === 0)
if(findIndex !== -1){
const copyGroups = [...(data?.groups || [])];
const findIndex = copyGroups?.findIndex(
(item) => item?.groupId === 0
);
if (findIndex !== -1) {
copyGroups[findIndex] = {
...(copyGroups[findIndex] || {}),
groupId: "0"
}
groupId: '0',
};
}
const filteredGroups = copyGroups
const sortedGroups = filteredGroups.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
const sortedDirects = (data?.direct || []).filter(item =>
item?.name !== 'extension-proxy' && item?.address !== 'QSMMGSgysEuqDCuLw3S4cHrQkBrh3vP3VH'
).sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
const filteredGroups = copyGroups;
const sortedGroups = filteredGroups.sort(
(a, b) => (b.timestamp || 0) - (a.timestamp || 0)
);
const sortedDirects = (data?.direct || [])
.filter(
(item) =>
item?.name !== 'extension-proxy' &&
item?.address !== 'QSMMGSgysEuqDCuLw3S4cHrQkBrh3vP3VH'
)
.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
window.sendMessage("handleActiveGroupDataFromSocket", {
groups: sortedGroups,
directs: sortedDirects,
}).catch((error) => {
console.error("Failed to handle active group data from socket:", error.message || "An error occurred");
window
.sendMessage('handleActiveGroupDataFromSocket', {
groups: sortedGroups,
directs: sortedDirects,
})
.catch((error) => {
// TODO translate
console.error(
'Failed to handle active group data from socket:',
error.message || 'An error occurred'
);
});
}
} catch (error) {
console.error('Error parsing onmessage data:', error);
@ -127,9 +141,7 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
}
};
initWebsocketMessageGroup(); // Initialize WebSocket on component mount
initWebsocketMessageGroup(); // Initialize WebSocket on component mount
return () => {
forceCloseWebSocket(); // Clean up WebSocket on component unmount

View File

@ -1,218 +1,194 @@
import React, { useCallback, useEffect, useRef } from "react";
import { getBaseApiReact } from "../../App";
import { truncate } from "lodash";
import { useCallback, useEffect, useRef } from 'react';
export const useBlockedAddresses = () => {
const userBlockedRef = useRef({})
const userNamesBlockedRef = useRef({})
const getAllBlockedUsers = useCallback(()=> {
const userBlockedRef = useRef({});
const userNamesBlockedRef = useRef({});
const getAllBlockedUsers = useCallback(() => {
return {
names: userNamesBlockedRef.current,
addresses: userBlockedRef.current
}
}, [])
addresses: userBlockedRef.current,
};
}, []);
const isUserBlocked = useCallback((address, name)=> {
const isUserBlocked = useCallback((address, name) => {
try {
if(!address) return false
if(userBlockedRef.current[address]) return true
return false
if (!address) return false;
if (userBlockedRef.current[address]) return true;
return false;
} catch (error) {
//error
//error
}
}, [])
}, []);
useEffect(()=> {
const fetchBlockedList = async ()=> {
useEffect(() => {
const fetchBlockedList = async () => {
try {
const response = await new Promise((res, rej) => {
window.sendMessage("listActions", {
const response = await new Promise((res, rej) => {
window
.sendMessage('listActions', {
type: 'get',
listName: `blockedAddresses`,
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
res(response);
}
})
.catch((error) => {
console.error('Failed qortalRequest', error);
});
});
const blockedUsers = {};
response?.forEach((item) => {
blockedUsers[item] = true;
});
userBlockedRef.current = blockedUsers;
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
res(response);
}
})
.catch((error) => {
console.error("Failed qortalRequest", error);
});
})
const blockedUsers = {}
response?.forEach((item)=> {
blockedUsers[item] = true
})
userBlockedRef.current = blockedUsers
const response2 = await new Promise((res, rej) => {
window.sendMessage("listActions", {
const response2 = await new Promise((res, rej) => {
window
.sendMessage('listActions', {
type: 'get',
listName: `blockedNames`,
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
res(response);
}
})
.catch((error) => {
console.error('Failed qortalRequest', error);
});
});
const blockedUsers2 = {};
response2?.forEach((item) => {
blockedUsers2[item] = true;
});
userNamesBlockedRef.current = blockedUsers2;
} catch (error) {
console.error(error);
}
};
fetchBlockedList();
}, []);
const removeBlockFromList = useCallback(async (address, name) => {
if (name) {
await new Promise((res, rej) => {
window
.sendMessage('listActions', {
type: 'remove',
items: [name],
listName: 'blockedNames',
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
const copyObject = { ...userNamesBlockedRef.current };
delete copyObject[name];
userNamesBlockedRef.current = copyObject;
res(response);
}
})
.catch((error) => {
console.error("Failed qortalRequest", error);
console.error('Failed qortalRequest', error);
});
})
const blockedUsers2 = {}
response2?.forEach((item)=> {
blockedUsers2[item] = true
})
userNamesBlockedRef.current = blockedUsers2
} catch (error) {
console.error(error)
}
}
fetchBlockedList()
}, [])
const removeBlockFromList = useCallback(async (address, name)=> {
if(name){
await new Promise((res, rej) => {
window.sendMessage("listActions", {
type: 'remove',
items: [name] ,
listName: 'blockedNames'
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
const copyObject = {...userNamesBlockedRef.current}
delete copyObject[name]
userNamesBlockedRef.current = copyObject
res(response);
}
})
.catch((error) => {
console.error("Failed qortalRequest", error);
});
})
});
}
if(address){
if (address) {
await new Promise((res, rej) => {
window.sendMessage("listActions", {
window
.sendMessage('listActions', {
type: 'remove',
items: [address],
listName: 'blockedAddresses'
listName: 'blockedAddresses',
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
const copyObject = { ...userBlockedRef.current };
delete copyObject[address];
userBlockedRef.current = copyObject;
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
const copyObject = {...userBlockedRef.current}
delete copyObject[address]
userBlockedRef.current = copyObject
res(response);
}
})
.catch((error) => {
console.error("Failed qortalRequest", error);
});
})
res(response);
}
})
.catch((error) => {
console.error('Failed qortalRequest', error);
});
});
}
}, []);
}, [])
const addToBlockList = useCallback(async (address, name)=> {
if(name){
const addToBlockList = useCallback(async (address, name) => {
if (name) {
await new Promise((res, rej) => {
window.sendMessage("listActions", {
window
.sendMessage('listActions', {
type: 'add',
items: [name],
listName: 'blockedNames'
listName: 'blockedNames',
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
const copyObject = { ...userNamesBlockedRef.current };
copyObject[name] = true;
userNamesBlockedRef.current = copyObject;
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
const copyObject = {...userNamesBlockedRef.current}
copyObject[name] = true
userNamesBlockedRef.current = copyObject
res(response);
}
})
.catch((error) => {
console.error("Failed qortalRequest", error);
});
})
res(response);
}
})
.catch((error) => {
console.error('Failed qortalRequest', error);
});
});
}
if(address){
if (address) {
await new Promise((res, rej) => {
window.sendMessage("listActions", {
window
.sendMessage('listActions', {
type: 'add',
items: [address],
listName: 'blockedAddresses'
listName: 'blockedAddresses',
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
const copyObject = { ...userBlockedRef.current };
copyObject[address] = true;
userBlockedRef.current = copyObject;
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
const copyObject = {...userBlockedRef.current}
copyObject[address] = true
userBlockedRef.current = copyObject
res(response);
}
})
.catch((error) => {
console.error("Failed qortalRequest", error);
});
})
res(response);
}
})
.catch((error) => {
console.error('Failed qortalRequest', error);
});
});
}
}, [])
}, []);
return {
isUserBlocked,
addToBlockList,
removeBlockFromList,
getAllBlockedUsers
getAllBlockedUsers,
};
};

View File

@ -1,32 +1,30 @@
import React, { useCallback, useRef } from "react";
import { getBaseApiReact } from "../../App";
import { useCallback, useRef } from 'react';
import { getBaseApiReact } from '../../App';
export const useHandleUserInfo = () => {
const userInfoRef = useRef({})
const userInfoRef = useRef({});
const getIndividualUserInfo = useCallback(async (address)=> {
const getIndividualUserInfo = useCallback(async (address) => {
try {
if(!address) return null
if(userInfoRef.current[address] !== undefined) return userInfoRef.current[address]
if (!address) return null;
if (userInfoRef.current[address] !== undefined)
return userInfoRef.current[address];
const url = `${getBaseApiReact()}/addresses/${address}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error("network error");
}
const data = await response.json();
userInfoRef.current = {
...userInfoRef.current,
[address]: data?.level
}
return data?.level
const response = await fetch(url);
if (!response.ok) {
throw new Error('network error');
}
const data = await response.json();
userInfoRef.current = {
...userInfoRef.current,
[address]: data?.level,
};
return data?.level;
} catch (error) {
//error
//error
}
}, [])
}, []);
return {
getIndividualUserInfo,

View File

@ -1,40 +1,40 @@
import { Box, ButtonBase, Typography } from "@mui/material";
import React from "react";
import { Spacer } from "../../common/Spacer";
import { Box, ButtonBase, Typography } from '@mui/material';
import { Spacer } from '../../common/Spacer';
export const NewUsersCTA = ({ balance }) => {
if (balance === undefined || +balance > 0) return null;
return (
<Box
sx={{
width: "100%",
display: "flex",
flexDirection: "column",
alignItems: "center",
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Spacer height="40px" />
<Box
sx={{
width: "320px",
justifyContent: "center",
flexDirection: "column",
alignItems: "center",
padding: "15px",
outline: "1px solid gray",
borderRadius: "4px",
width: '320px',
justifyContent: 'center',
flexDirection: 'column',
alignItems: 'center',
padding: '15px',
outline: '1px solid gray',
borderRadius: '4px',
}}
>
<Typography
sx={{
textAlign: "center",
fontSize: "1.2rem",
fontWeight: "bold",
textAlign: 'center',
fontSize: '1.2rem',
fontWeight: 'bold',
}}
>
Are you a new user?
</Typography>
</Typography>{' '}
// TODO translate
<Spacer height="20px" />
<Typography>
Please message us on Telegram or Discord if you need 4 QORT to start
@ -43,25 +43,25 @@ export const NewUsersCTA = ({ balance }) => {
<Spacer height="20px" />
<Box
sx={{
width: "100%",
display: "flex",
gap: "10px",
justifyContent: "center",
width: '100%',
display: 'flex',
gap: '10px',
justifyContent: 'center',
}}
>
<ButtonBase
sx={{
textDecoration: "underline",
textDecoration: 'underline',
}}
onClick={() => {
if (window?.electronAPI?.openExternal) {
window.electronAPI.openExternal(
"https://link.qortal.dev/telegram-invite"
'https://link.qortal.dev/telegram-invite'
);
} else {
window.open(
"https://link.qortal.dev/telegram-invite",
"_blank"
'https://link.qortal.dev/telegram-invite',
'_blank'
);
}
}}
@ -70,15 +70,15 @@ export const NewUsersCTA = ({ balance }) => {
</ButtonBase>
<ButtonBase
sx={{
textDecoration: "underline",
textDecoration: 'underline',
}}
onClick={() => {
if (window?.electronAPI?.openExternal) {
window.electronAPI.openExternal(
"https://link.qortal.dev/discord-invite"
'https://link.qortal.dev/discord-invite'
);
} else {
window.open("https://link.qortal.dev/discord-invite", "_blank");
window.open('https://link.qortal.dev/discord-invite', '_blank');
}
}}
>

View File

@ -206,7 +206,6 @@ export const Minting = ({
window
.sendMessage(
'ADMIN_ACTION',
{
type: 'addmintingaccount',
value: val,
@ -266,7 +265,7 @@ export const Minting = ({
rej({ message: response.error });
})
.catch((error) => {
rej({ message: error.message || 'An error occurred' });
rej({ message: error.message || 'An error occurred' }); //TODO translate
});
});
} catch (error) {