mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-06-12 19:21:22 +00:00
Add theme to all chat pages
This commit is contained in:
parent
cf335a6d0a
commit
905cddf29a
@ -4,7 +4,7 @@ import {
|
|||||||
AuthenticatedContainerInnerTop,
|
AuthenticatedContainerInnerTop,
|
||||||
CustomButton,
|
CustomButton,
|
||||||
} from '../../styles/App-styles';
|
} from '../../styles/App-styles';
|
||||||
import { Box, CircularProgress } from '@mui/material';
|
import { Box, CircularProgress, useTheme } from '@mui/material';
|
||||||
import { objectToBase64 } from '../../qdn/encryption/group-encryption';
|
import { objectToBase64 } from '../../qdn/encryption/group-encryption';
|
||||||
import ShortUniqueId from 'short-unique-id';
|
import ShortUniqueId from 'short-unique-id';
|
||||||
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
|
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
|
||||||
@ -39,6 +39,7 @@ export const AnnouncementDiscussion = ({
|
|||||||
myName,
|
myName,
|
||||||
isPrivate,
|
isPrivate,
|
||||||
}) => {
|
}) => {
|
||||||
|
const theme = useTheme();
|
||||||
const [isSending, setIsSending] = useState(false);
|
const [isSending, setIsSending] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [isFocusedParent, setIsFocusedParent] = useState(false);
|
const [isFocusedParent, setIsFocusedParent] = useState(false);
|
||||||
@ -212,6 +213,7 @@ export const AnnouncementDiscussion = ({
|
|||||||
getData({ name: data.name, identifier: data.identifier }, isPrivate);
|
getData({ name: data.name, identifier: data.identifier }, isPrivate);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|
||||||
@ -274,19 +276,19 @@ export const AnnouncementDiscussion = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
height: isMobile ? '100%' : '100%',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
height: isMobile ? '100%' : '100%',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: 'relative',
|
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
|
position: 'relative',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AuthenticatedContainerInnerTop
|
<AuthenticatedContainerInnerTop
|
||||||
@ -301,6 +303,7 @@ export const AnnouncementDiscussion = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</AuthenticatedContainerInnerTop>
|
</AuthenticatedContainerInnerTop>
|
||||||
|
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
</div>
|
</div>
|
||||||
<AnnouncementList
|
<AnnouncementList
|
||||||
@ -314,30 +317,27 @@ export const AnnouncementDiscussion = ({
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
// position: 'fixed',
|
backgroundColor: theme.palette.background.default,
|
||||||
// bottom: '0px',
|
bottom: isFocusedParent ? '0px' : 'unset',
|
||||||
backgroundColor: '#232428',
|
boxSizing: 'border-box',
|
||||||
minHeight: isMobile ? '0px' : '150px',
|
|
||||||
maxHeight: isMobile ? 'auto' : '400px',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
overflow: 'hidden',
|
|
||||||
width: '100%',
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
padding: isMobile ? '10px' : '20px',
|
|
||||||
position: isFocusedParent ? 'fixed' : 'relative',
|
|
||||||
bottom: isFocusedParent ? '0px' : 'unset',
|
|
||||||
top: isFocusedParent ? '0px' : 'unset',
|
|
||||||
zIndex: isFocusedParent ? 5 : 'unset',
|
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
|
maxHeight: '400px',
|
||||||
|
minHeight: '150px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
padding: '20px',
|
||||||
|
position: isFocusedParent ? 'fixed' : 'relative',
|
||||||
|
top: isFocusedParent ? '0px' : 'unset',
|
||||||
|
width: '100%',
|
||||||
|
zIndex: isFocusedParent ? 5 : 'unset',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
// height: '100%',
|
flexGrow: 1,
|
||||||
flexGrow: isMobile && 1,
|
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -353,11 +353,11 @@ export const AnnouncementDiscussion = ({
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
width: '100&',
|
flexShrink: 0,
|
||||||
gap: '10px',
|
gap: '10px',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
flexShrink: 0,
|
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
width: '100&',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isFocusedParent && (
|
{isFocusedParent && (
|
||||||
@ -369,13 +369,13 @@ export const AnnouncementDiscussion = ({
|
|||||||
// Unfocus the editor
|
// Unfocus the editor
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
marginTop: 'auto',
|
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
|
background: 'red',
|
||||||
cursor: isSending ? 'default' : 'pointer',
|
cursor: isSending ? 'default' : 'pointer',
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
padding: isMobile && '5px',
|
fontSize: '14px',
|
||||||
fontSize: isMobile && '14px',
|
marginTop: 'auto',
|
||||||
background: 'red',
|
padding: '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{` Close`}
|
{` Close`}
|
||||||
@ -387,25 +387,25 @@ export const AnnouncementDiscussion = ({
|
|||||||
publishComment();
|
publishComment();
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
marginTop: 'auto',
|
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
|
background: theme.palette.background.default,
|
||||||
cursor: isSending ? 'default' : 'pointer',
|
cursor: isSending ? 'default' : 'pointer',
|
||||||
background: isSending && 'rgba(0, 0, 0, 0.8)',
|
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
padding: isMobile && '5px',
|
fontSize: '14px',
|
||||||
fontSize: isMobile && '14px',
|
marginTop: 'auto',
|
||||||
|
padding: '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isSending && (
|
{isSending && (
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
size={18}
|
size={18}
|
||||||
sx={{
|
sx={{
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
left: '50%',
|
||||||
|
marginLeft: '-12px',
|
||||||
|
marginTop: '-12px',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '50%',
|
top: '50%',
|
||||||
left: '50%',
|
|
||||||
marginTop: '-12px',
|
|
||||||
marginLeft: '-12px',
|
|
||||||
color: 'white',
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -1,173 +1,206 @@
|
|||||||
import { Message } from "@chatscope/chat-ui-kit-react";
|
import React, { useEffect, useState } from 'react';
|
||||||
import React, { useEffect, useState } from "react";
|
import { MessageDisplay } from './MessageDisplay';
|
||||||
import { useInView } from "react-intersection-observer";
|
import { Avatar, Box, Typography, useTheme } from '@mui/material';
|
||||||
import { MessageDisplay } from "./MessageDisplay";
|
import { formatTimestamp } from '../../utils/time';
|
||||||
import { Avatar, Box, Typography } from "@mui/material";
|
|
||||||
import { formatTimestamp } from "../../utils/time";
|
|
||||||
import ChatBubbleIcon from '@mui/icons-material/ChatBubble';
|
import ChatBubbleIcon from '@mui/icons-material/ChatBubble';
|
||||||
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
|
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
|
||||||
import { getBaseApi } from "../../background";
|
import { getBaseApi } from '../../background';
|
||||||
import { requestQueueCommentCount } from "./GroupAnnouncements";
|
import { requestQueueCommentCount } from './GroupAnnouncements';
|
||||||
import { CustomLoader } from "../../common/CustomLoader";
|
import { CustomLoader } from '../../common/CustomLoader';
|
||||||
import { getArbitraryEndpointReact, getBaseApiReact } from "../../App";
|
import { getArbitraryEndpointReact, getBaseApiReact } from '../../App';
|
||||||
import { WrapperUserAction } from "../WrapperUserAction";
|
import { WrapperUserAction } from '../WrapperUserAction';
|
||||||
export const AnnouncementItem = ({ message, messageData, setSelectedAnnouncement, disableComment, myName }) => {
|
|
||||||
|
|
||||||
const [commentLength, setCommentLength] = useState(0)
|
export const AnnouncementItem = ({
|
||||||
const getNumberOfComments = React.useCallback(
|
message,
|
||||||
async () => {
|
messageData,
|
||||||
try {
|
setSelectedAnnouncement,
|
||||||
const offset = 0;
|
disableComment,
|
||||||
|
myName,
|
||||||
|
}) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const [commentLength, setCommentLength] = useState(0);
|
||||||
|
const getNumberOfComments = React.useCallback(async () => {
|
||||||
|
try {
|
||||||
|
const offset = 0;
|
||||||
|
|
||||||
// dispatch(setIsLoadingGlobal(true))
|
// dispatch(setIsLoadingGlobal(true))
|
||||||
const identifier = `cm-${message.identifier}`;
|
const identifier = `cm-${message.identifier}`;
|
||||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=0&includemetadata=false&offset=${offset}&reverse=true&prefix=true`;
|
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=0&includemetadata=false&offset=${offset}&reverse=true&prefix=true`;
|
||||||
|
|
||||||
const response = await requestQueueCommentCount.enqueue(() => {
|
const response = await requestQueueCommentCount.enqueue(() => {
|
||||||
return fetch(url, {
|
return fetch(url, {
|
||||||
method: "GET",
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
const responseData = await response.json();
|
const responseData = await response.json();
|
||||||
|
|
||||||
|
setCommentLength(responseData?.length);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (disableComment) return;
|
||||||
|
getNumberOfComments();
|
||||||
|
}, []);
|
||||||
|
|
||||||
setCommentLength(responseData?.length);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
} finally {
|
|
||||||
// dispatch(setIsLoadingGlobal(false))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
useEffect(()=> {
|
|
||||||
if(disableComment) return
|
|
||||||
getNumberOfComments()
|
|
||||||
}, [])
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
padding: "10px",
|
backgroundColor: theme.palette.background.default,
|
||||||
backgroundColor: "#232428",
|
borderRadius: '7px',
|
||||||
borderRadius: "7px",
|
display: 'flex',
|
||||||
width: "95%",
|
flexDirection: 'column',
|
||||||
display: "flex",
|
|
||||||
gap: '7px',
|
gap: '7px',
|
||||||
flexDirection: 'column'
|
padding: '10px',
|
||||||
|
width: '95%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{
|
|
||||||
display: "flex",
|
|
||||||
gap: '7px',
|
|
||||||
width: '100%',
|
|
||||||
wordBreak: 'break-word'
|
|
||||||
}}>
|
|
||||||
<WrapperUserAction disabled={myName === message?.name} address={undefined} name={message?.name}>
|
|
||||||
<Avatar
|
|
||||||
sx={{
|
|
||||||
backgroundColor: '#27282c',
|
|
||||||
color: 'white'
|
|
||||||
}}
|
|
||||||
alt={message?.name}
|
|
||||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${message?.name}/qortal_avatar?async=true`}
|
|
||||||
>
|
|
||||||
{message?.name?.charAt(0)}
|
|
||||||
</Avatar>
|
|
||||||
</WrapperUserAction>
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
gap: '7px',
|
||||||
gap: "7px",
|
width: '100%',
|
||||||
width: '100%'
|
wordBreak: 'break-word',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<WrapperUserAction disabled={myName === message?.name} address={undefined} name={message?.name}>
|
<WrapperUserAction
|
||||||
<Typography
|
disabled={myName === message?.name}
|
||||||
|
address={undefined}
|
||||||
|
name={message?.name}
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
sx={{
|
||||||
|
backgroundColor: theme.palette.background.default,
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
}}
|
||||||
|
alt={message?.name}
|
||||||
|
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${message?.name}/qortal_avatar?async=true`}
|
||||||
|
>
|
||||||
|
{message?.name?.charAt(0)}
|
||||||
|
</Avatar>
|
||||||
|
</WrapperUserAction>
|
||||||
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
fontWight: 600,
|
display: 'flex',
|
||||||
fontFamily: "Inter",
|
flexDirection: 'column',
|
||||||
color: "cadetBlue",
|
gap: '7px',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{message?.name}
|
<WrapperUserAction
|
||||||
</Typography>
|
disabled={myName === message?.name}
|
||||||
</WrapperUserAction>
|
address={undefined}
|
||||||
{!messageData?.decryptedData && (
|
name={message?.name}
|
||||||
<Box sx={{
|
>
|
||||||
width: '100%',
|
<Typography
|
||||||
display: 'flex',
|
sx={{
|
||||||
justifyContent: 'center'
|
fontWight: 600,
|
||||||
}}>
|
fontFamily: 'Inter',
|
||||||
<CustomLoader />
|
color: 'cadetBlue',
|
||||||
</Box>
|
}}
|
||||||
)}
|
>
|
||||||
{messageData?.decryptedData?.message && (
|
{message?.name}
|
||||||
<>
|
</Typography>
|
||||||
{messageData?.type === "notification" ? (
|
</WrapperUserAction>
|
||||||
<MessageDisplay htmlContent={messageData?.decryptedData?.message} />
|
{!messageData?.decryptedData && (
|
||||||
) : (
|
<Box
|
||||||
<MessageDisplay htmlContent={messageData?.decryptedData?.message} />
|
sx={{
|
||||||
)}
|
width: '100%',
|
||||||
</>
|
display: 'flex',
|
||||||
)}
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CustomLoader />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{messageData?.decryptedData?.message && (
|
||||||
|
<>
|
||||||
|
{messageData?.type === 'notification' ? (
|
||||||
|
<MessageDisplay
|
||||||
|
htmlContent={messageData?.decryptedData?.message}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<MessageDisplay
|
||||||
|
htmlContent={messageData?.decryptedData?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Box
|
||||||
<Box sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
width: '100%'
|
width: '100%',
|
||||||
}}>
|
}}
|
||||||
<Typography sx={{
|
>
|
||||||
fontSize: '14px',
|
<Typography
|
||||||
color: 'gray',
|
sx={{
|
||||||
fontFamily: 'Inter'
|
color: theme.palette.text.secondary,
|
||||||
}}>{formatTimestamp(message.created)}</Typography>
|
fontFamily: 'Inter',
|
||||||
|
fontSize: '14px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{formatTimestamp(message.created)}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
{!disableComment && (
|
||||||
{!disableComment && (
|
<Box
|
||||||
<Box sx={{
|
sx={{
|
||||||
display: 'flex',
|
alignItems: 'center',
|
||||||
width: '100%',
|
borderTop: '1px solid white',
|
||||||
alignItems: 'center',
|
cursor: 'pointer',
|
||||||
justifyContent: 'space-between',
|
display: 'flex',
|
||||||
padding: '20px',
|
justifyContent: 'space-between',
|
||||||
cursor: 'pointer',
|
opacity: 0.4,
|
||||||
opacity: 0.4,
|
padding: '20px',
|
||||||
borderTop: '1px solid white',
|
width: '100%',
|
||||||
|
}}
|
||||||
}} onClick={()=> setSelectedAnnouncement(message)}>
|
onClick={() => setSelectedAnnouncement(message)}
|
||||||
|
>
|
||||||
<Box sx={{
|
<Box
|
||||||
display: 'flex',
|
sx={{
|
||||||
width: '100%',
|
alignItems: 'center',
|
||||||
gap: '25px',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
gap: '25px',
|
||||||
|
width: '100%',
|
||||||
}}>
|
}}
|
||||||
<ChatBubbleIcon sx={{
|
>
|
||||||
fontSize: '20px'
|
<ChatBubbleIcon
|
||||||
}} />
|
sx={{
|
||||||
{commentLength ? (
|
fontSize: '20px',
|
||||||
<Typography sx={{
|
}}
|
||||||
fontSize: '14px'
|
/>
|
||||||
}}>{`${commentLength > 1 ? `${commentLength} comments` : `${commentLength} comment`}`}</Typography>
|
{commentLength ? (
|
||||||
) : (
|
<Typography
|
||||||
<Typography sx={{
|
sx={{
|
||||||
fontSize: '14px'
|
fontSize: '14px',
|
||||||
}}>Leave comment</Typography>
|
}}
|
||||||
)}
|
>{`${commentLength > 1 ? `${commentLength} comments` : `${commentLength} comment`}`}</Typography>
|
||||||
|
) : (
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: '14px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Leave comment
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<ArrowForwardIosIcon
|
||||||
|
sx={{
|
||||||
|
fontSize: '20px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<ArrowForwardIosIcon sx={{
|
)}
|
||||||
fontSize: '20px'
|
|
||||||
}} />
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
import React, { useCallback, useState, useEffect, useRef } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
import {
|
import { CellMeasurerCache } from 'react-virtualized';
|
||||||
List,
|
|
||||||
AutoSizer,
|
|
||||||
CellMeasurerCache,
|
|
||||||
CellMeasurer,
|
|
||||||
} from 'react-virtualized';
|
|
||||||
import { AnnouncementItem } from './AnnouncementItem';
|
import { AnnouncementItem } from './AnnouncementItem';
|
||||||
import { Box } from '@mui/material';
|
import { Box } from '@mui/material';
|
||||||
import { CustomButton } from '../../styles/App-styles';
|
import { CustomButton } from '../../styles/App-styles';
|
||||||
@ -37,12 +32,12 @@ export const AnnouncementList = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: 'relative',
|
|
||||||
flexGrow: 1,
|
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
flexGrow: 1,
|
||||||
flexShrink: 1,
|
flexShrink: 1,
|
||||||
|
position: 'relative',
|
||||||
|
width: '100%',
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -57,11 +52,11 @@ export const AnnouncementList = ({
|
|||||||
<div
|
<div
|
||||||
key={message?.identifier}
|
key={message?.identifier}
|
||||||
style={{
|
style={{
|
||||||
marginBottom: '10px',
|
alignItems: 'center',
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
alignItems: 'center',
|
marginBottom: '10px',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AnnouncementItem
|
<AnnouncementItem
|
||||||
@ -89,10 +84,10 @@ export const AnnouncementList = ({
|
|||||||
</AutoSizer> */}
|
</AutoSizer> */}
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
|
||||||
marginTop: '25px',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
|
marginTop: '25px',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{showLoadMore && (
|
{showLoadMore && (
|
||||||
|
@ -7,15 +7,10 @@ import React, {
|
|||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { CreateCommonSecret } from './CreateCommonSecret';
|
|
||||||
import { reusableGet } from '../../qdn/publish/pubish';
|
|
||||||
import { uint8ArrayToObject } from '../../backgroundFunctions/encryption';
|
|
||||||
import {
|
import {
|
||||||
base64ToUint8Array,
|
|
||||||
decodeBase64ForUIChatMessages,
|
decodeBase64ForUIChatMessages,
|
||||||
objectToBase64,
|
objectToBase64,
|
||||||
} from '../../qdn/encryption/group-encryption';
|
} from '../../qdn/encryption/group-encryption';
|
||||||
import { ChatContainerComp } from './ChatContainer';
|
|
||||||
import { ChatList } from './ChatList';
|
import { ChatList } from './ChatList';
|
||||||
import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css';
|
import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css';
|
||||||
import Tiptap from './TipTap';
|
import Tiptap from './TipTap';
|
||||||
@ -38,7 +33,7 @@ import {
|
|||||||
subscribeToEvent,
|
subscribeToEvent,
|
||||||
unsubscribeFromEvent,
|
unsubscribeFromEvent,
|
||||||
} from '../../utils/events';
|
} from '../../utils/events';
|
||||||
import { Box, ButtonBase, Divider, Typography } from '@mui/material';
|
import { Box, ButtonBase, Divider, Typography, useTheme } from '@mui/material';
|
||||||
import ShortUniqueId from 'short-unique-id';
|
import ShortUniqueId from 'short-unique-id';
|
||||||
import { ReplyPreview } from './MessageItem';
|
import { ReplyPreview } from './MessageItem';
|
||||||
import { ExitIcon } from '../../assets/Icons/ExitIcon';
|
import { ExitIcon } from '../../assets/Icons/ExitIcon';
|
||||||
@ -1001,16 +996,18 @@ export const ChatGroup = ({
|
|||||||
setIsOpenQManager(true);
|
setIsOpenQManager(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
height: isMobile ? '100%' : '100%',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
width: '100%',
|
height: '100%',
|
||||||
|
left: hide && '-100000px',
|
||||||
opacity: hide ? 0 : 1,
|
opacity: hide ? 0 : 1,
|
||||||
position: hide ? 'absolute' : 'relative',
|
position: hide ? 'absolute' : 'relative',
|
||||||
left: hide && '-100000px',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ChatList
|
<ChatList
|
||||||
@ -1035,40 +1032,38 @@ export const ChatGroup = ({
|
|||||||
{(!!secretKey || isPrivate === false) && (
|
{(!!secretKey || isPrivate === false) && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
// position: 'fixed',
|
backgroundColor: theme.palette.background.default,
|
||||||
// bottom: '0px',
|
bottom: isFocusedParent ? '0px' : 'unset',
|
||||||
backgroundColor: '#232428',
|
boxSizing: 'border-box',
|
||||||
minHeight: isMobile ? '0px' : '150px',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
overflow: 'hidden',
|
|
||||||
width: '100%',
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
padding: isMobile ? '10px' : '20px',
|
|
||||||
position: isFocusedParent ? 'fixed' : 'relative',
|
|
||||||
bottom: isFocusedParent ? '0px' : 'unset',
|
|
||||||
top: isFocusedParent ? '0px' : 'unset',
|
|
||||||
zIndex: isFocusedParent ? 5 : 'unset',
|
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
|
minHeight: '150px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
padding: '20px',
|
||||||
|
position: isFocusedParent ? 'fixed' : 'relative',
|
||||||
|
top: isFocusedParent ? '0px' : 'unset',
|
||||||
|
width: '100%',
|
||||||
|
zIndex: isFocusedParent ? 5 : 'unset',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
flexGrow: isMobile && 1,
|
flexGrow: 1,
|
||||||
overflow: !isMobile && 'auto',
|
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
width: 'calc(100% - 100px)',
|
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
|
overflow: 'auto',
|
||||||
|
width: 'calc(100% - 100px)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{replyMessage && (
|
{replyMessage && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
alignItems: 'flex-start',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: '5px',
|
gap: '5px',
|
||||||
alignItems: 'flex-start',
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -1088,9 +1083,9 @@ export const ChatGroup = ({
|
|||||||
{onEditMessage && (
|
{onEditMessage && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
alignItems: 'flex-start',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: '5px',
|
gap: '5px',
|
||||||
alignItems: 'flex-start',
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -1123,9 +1118,9 @@ export const ChatGroup = ({
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
width: '100%',
|
|
||||||
justifyContent: 'flex-start',
|
justifyContent: 'flex-start',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
@ -1141,11 +1136,11 @@ export const ChatGroup = ({
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
width: '100px',
|
flexShrink: 0,
|
||||||
gap: '10px',
|
gap: '10px',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
flexShrink: 0,
|
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
width: '100px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomButton
|
<CustomButton
|
||||||
@ -1154,26 +1149,28 @@ export const ChatGroup = ({
|
|||||||
sendMessage();
|
sendMessage();
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
marginTop: 'auto',
|
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
|
background: isSending
|
||||||
|
? theme.palette.background.default
|
||||||
|
: theme.palette.background.paper,
|
||||||
cursor: isSending ? 'default' : 'pointer',
|
cursor: isSending ? 'default' : 'pointer',
|
||||||
background: isSending && 'rgba(0, 0, 0, 0.8)',
|
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
|
marginTop: 'auto',
|
||||||
|
minWidth: 'auto',
|
||||||
padding: '5px',
|
padding: '5px',
|
||||||
width: '100px',
|
width: '100px',
|
||||||
minWidth: 'auto',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isSending && (
|
{isSending && (
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
size={18}
|
size={18}
|
||||||
sx={{
|
sx={{
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
left: '50%',
|
||||||
|
marginLeft: '-12px',
|
||||||
|
marginTop: '-12px',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '50%',
|
top: '50%',
|
||||||
left: '50%',
|
|
||||||
marginTop: '-12px',
|
|
||||||
marginLeft: '-12px',
|
|
||||||
color: 'white',
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -1185,25 +1182,24 @@ export const ChatGroup = ({
|
|||||||
{isOpenQManager !== null && (
|
{isOpenQManager !== null && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
position: 'fixed',
|
backgroundColor: theme.palette.background.default,
|
||||||
height: '600px',
|
|
||||||
|
|
||||||
maxHeight: '100vh',
|
|
||||||
width: '400px',
|
|
||||||
maxWidth: '100vw',
|
|
||||||
backgroundColor: '#27282c',
|
|
||||||
zIndex: 100,
|
|
||||||
bottom: 0,
|
|
||||||
right: 0,
|
|
||||||
overflow: 'hidden',
|
|
||||||
borderTopLeftRadius: '10px',
|
borderTopLeftRadius: '10px',
|
||||||
borderTopRightRadius: '10px',
|
borderTopRightRadius: '10px',
|
||||||
|
bottom: 0,
|
||||||
|
boxShadow: 4,
|
||||||
display: hideView
|
display: hideView
|
||||||
? 'none'
|
? 'none'
|
||||||
: isOpenQManager === true
|
: isOpenQManager === true
|
||||||
? 'block'
|
? 'block'
|
||||||
: 'none',
|
: 'none',
|
||||||
boxShadow: 4,
|
height: '600px',
|
||||||
|
maxHeight: '100vh',
|
||||||
|
maxWidth: '100vw',
|
||||||
|
overflow: 'hidden',
|
||||||
|
position: 'fixed',
|
||||||
|
right: 0,
|
||||||
|
width: '400px',
|
||||||
|
zIndex: 100,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
@ -1214,12 +1210,11 @@ export const ChatGroup = ({
|
|||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
height: '40px',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
padding: '5px',
|
display: 'flex',
|
||||||
|
height: '40px',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
|
padding: '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>Q-Manager</Typography>
|
<Typography>Q-Manager</Typography>
|
||||||
@ -1251,12 +1246,14 @@ export const ChatGroup = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* <ChatContainerComp messages={formatMessages} /> */}
|
{/* <ChatContainerComp messages={formatMessages} /> */}
|
||||||
|
|
||||||
<LoadingSnackbar
|
<LoadingSnackbar
|
||||||
open={isLoading}
|
open={isLoading}
|
||||||
info={{
|
info={{
|
||||||
message: 'Loading chat... please wait.',
|
message: 'Loading chat... please wait.',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CustomizedSnackbars
|
<CustomizedSnackbars
|
||||||
open={openSnack}
|
open={openSnack}
|
||||||
setOpen={setOpenSnack}
|
setOpen={setOpenSnack}
|
||||||
|
@ -1,35 +1,56 @@
|
|||||||
import { Box, Button, Typography } from '@mui/material'
|
import React, { useContext } from 'react';
|
||||||
import React, { useContext } from 'react'
|
import { Box, Button, Typography, useTheme } from '@mui/material';
|
||||||
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||||
import { LoadingButton } from '@mui/lab';
|
import { LoadingButton } from '@mui/lab';
|
||||||
import { MyContext, getArbitraryEndpointReact, getBaseApiReact, pauseAllQueues } from '../../App';
|
import {
|
||||||
|
MyContext,
|
||||||
|
getArbitraryEndpointReact,
|
||||||
|
getBaseApiReact,
|
||||||
|
pauseAllQueues,
|
||||||
|
} from '../../App';
|
||||||
import { getFee } from '../../background';
|
import { getFee } from '../../background';
|
||||||
import { decryptResource, getGroupAdmins, validateSecretKey } from '../Group/Group';
|
import {
|
||||||
|
decryptResource,
|
||||||
|
getGroupAdmins,
|
||||||
|
validateSecretKey,
|
||||||
|
} from '../Group/Group';
|
||||||
import { base64ToUint8Array } from '../../qdn/encryption/group-encryption';
|
import { base64ToUint8Array } from '../../qdn/encryption/group-encryption';
|
||||||
import { uint8ArrayToObject } from '../../backgroundFunctions/encryption';
|
import { uint8ArrayToObject } from '../../backgroundFunctions/encryption';
|
||||||
|
|
||||||
export const CreateCommonSecret = ({groupId, secretKey, isOwner, myAddress, secretKeyDetails, userInfo, noSecretKey, setHideCommonKeyPopup, setIsForceShowCreationKeyPopup, isForceShowCreationKeyPopup}) => {
|
export const CreateCommonSecret = ({
|
||||||
|
groupId,
|
||||||
|
secretKey,
|
||||||
|
isOwner,
|
||||||
|
myAddress,
|
||||||
|
secretKeyDetails,
|
||||||
|
userInfo,
|
||||||
|
noSecretKey,
|
||||||
|
setHideCommonKeyPopup,
|
||||||
|
setIsForceShowCreationKeyPopup,
|
||||||
|
isForceShowCreationKeyPopup,
|
||||||
|
}) => {
|
||||||
const { show, setTxList } = useContext(MyContext);
|
const { show, setTxList } = useContext(MyContext);
|
||||||
|
|
||||||
const [openSnack, setOpenSnack] = React.useState(false);
|
const [openSnack, setOpenSnack] = React.useState(false);
|
||||||
const [infoSnack, setInfoSnack] = React.useState(null);
|
const [infoSnack, setInfoSnack] = React.useState(null);
|
||||||
const [isLoading, setIsLoading] = React.useState(false)
|
const [isLoading, setIsLoading] = React.useState(false);
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
const getPublishesFromAdmins = async (admins: string[]) => {
|
const getPublishesFromAdmins = async (admins: string[]) => {
|
||||||
// const validApi = await findUsableApi();
|
// const validApi = await findUsableApi();
|
||||||
const queryString = admins.map((name) => `name=${name}`).join("&");
|
const queryString = admins.map((name) => `name=${name}`).join('&');
|
||||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT_PRIVATE&identifier=symmetric-qchat-group-${
|
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT_PRIVATE&identifier=symmetric-qchat-group-${
|
||||||
groupId
|
groupId
|
||||||
}&exactmatchnames=true&limit=0&reverse=true&${queryString}&prefix=true`;
|
}&exactmatchnames=true&limit=0&reverse=true&${queryString}&prefix=true`;
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
if(!response.ok){
|
if (!response.ok) {
|
||||||
throw new Error('network error')
|
throw new Error('network error');
|
||||||
}
|
}
|
||||||
const adminData = await response.json();
|
const adminData = await response.json();
|
||||||
|
|
||||||
const filterId = adminData.filter(
|
const filterId = adminData.filter(
|
||||||
(data: any) =>
|
(data: any) => data.identifier === `symmetric-qchat-group-${groupId}`
|
||||||
data.identifier === `symmetric-qchat-group-${groupId}`
|
|
||||||
);
|
);
|
||||||
if (filterId?.length === 0) {
|
if (filterId?.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
@ -38,149 +59,182 @@ export const CreateCommonSecret = ({groupId, secretKey, isOwner, myAddress, sec
|
|||||||
// Get the most recent date for both a and b
|
// Get the most recent date for both a and b
|
||||||
const dateA = a.updated ? new Date(a.updated) : new Date(a.created);
|
const dateA = a.updated ? new Date(a.updated) : new Date(a.created);
|
||||||
const dateB = b.updated ? new Date(b.updated) : new Date(b.created);
|
const dateB = b.updated ? new Date(b.updated) : new Date(b.created);
|
||||||
|
|
||||||
// Sort by most recent
|
// Sort by most recent
|
||||||
return dateB.getTime() - dateA.getTime();
|
return dateB.getTime() - dateA.getTime();
|
||||||
});
|
});
|
||||||
|
|
||||||
return sortedData[0];
|
return sortedData[0];
|
||||||
};
|
};
|
||||||
const getSecretKey = async (loadingGroupParam?: boolean, secretKeyToPublish?: boolean) => {
|
|
||||||
|
const getSecretKey = async (
|
||||||
|
loadingGroupParam?: boolean,
|
||||||
|
secretKeyToPublish?: boolean
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
pauseAllQueues()
|
pauseAllQueues();
|
||||||
|
|
||||||
|
const { names } = await getGroupAdmins(groupId);
|
||||||
|
if (!names.length) {
|
||||||
const {names} = await getGroupAdmins(groupId);
|
throw new Error('Network error');
|
||||||
if(!names.length){
|
|
||||||
throw new Error('Network error')
|
|
||||||
}
|
}
|
||||||
const publish = await getPublishesFromAdmins(names);
|
const publish = await getPublishesFromAdmins(names);
|
||||||
|
|
||||||
|
|
||||||
if (publish === false) {
|
if (publish === false) {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${
|
`${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${
|
||||||
publish.identifier
|
publish.identifier
|
||||||
}?encoding=base64&rebuild=true`
|
}?encoding=base64&rebuild=true`
|
||||||
);
|
);
|
||||||
const data = await res.text();
|
const data = await res.text();
|
||||||
|
|
||||||
const decryptedKey: any = await decryptResource(data);
|
const decryptedKey: any = await decryptResource(data);
|
||||||
|
|
||||||
const dataint8Array = base64ToUint8Array(decryptedKey.data);
|
const dataint8Array = base64ToUint8Array(decryptedKey.data);
|
||||||
const decryptedKeyToObject = uint8ArrayToObject(dataint8Array);
|
const decryptedKeyToObject = uint8ArrayToObject(dataint8Array);
|
||||||
|
|
||||||
if (!validateSecretKey(decryptedKeyToObject))
|
if (!validateSecretKey(decryptedKeyToObject))
|
||||||
throw new Error("SecretKey is not valid");
|
throw new Error('SecretKey is not valid');
|
||||||
|
|
||||||
if (decryptedKeyToObject) {
|
if (decryptedKeyToObject) {
|
||||||
|
|
||||||
return decryptedKeyToObject;
|
return decryptedKeyToObject;
|
||||||
} else {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
|
||||||
} finally {
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const createCommonSecret = async ()=> {
|
const createCommonSecret = async () => {
|
||||||
try {
|
try {
|
||||||
const fee = await getFee('ARBITRARY')
|
const fee = await getFee('ARBITRARY');
|
||||||
await show({
|
await show({
|
||||||
message: "Would you like to perform an ARBITRARY transaction?" ,
|
message: 'Would you like to perform an ARBITRARY transaction?',
|
||||||
publishFee: fee.fee + ' QORT'
|
publishFee: fee.fee + ' QORT',
|
||||||
})
|
});
|
||||||
setIsLoading(true)
|
setIsLoading(true);
|
||||||
|
|
||||||
const secretKey2 = await getSecretKey()
|
const secretKey2 = await getSecretKey();
|
||||||
if((!secretKey2 && secretKey2 !== false)) throw new Error('invalid secret key')
|
if (!secretKey2 && secretKey2 !== false)
|
||||||
if (secretKey2 && !validateSecretKey(secretKey2)) throw new Error('invalid secret key')
|
throw new Error('invalid secret key');
|
||||||
|
if (secretKey2 && !validateSecretKey(secretKey2))
|
||||||
|
throw new Error('invalid secret key');
|
||||||
|
|
||||||
const secretKeyToSend = !secretKey2 ? null : secretKey2
|
const secretKeyToSend = !secretKey2 ? null : secretKey2;
|
||||||
|
|
||||||
|
window
|
||||||
window.sendMessage("encryptAndPublishSymmetricKeyGroupChat", {
|
.sendMessage('encryptAndPublishSymmetricKeyGroupChat', {
|
||||||
groupId: groupId,
|
groupId: groupId,
|
||||||
previousData: secretKeyToSend,
|
previousData: secretKeyToSend,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "success",
|
type: 'success',
|
||||||
message: "Successfully re-encrypted secret key. It may take a couple of minutes for the changes to propagate. Refresh the group in 5 mins.",
|
message:
|
||||||
});
|
'Successfully re-encrypted secret key. It may take a couple of minutes for the changes to propagate. Refresh the group in 5 mins.',
|
||||||
setOpenSnack(true);
|
|
||||||
setTxList((prev) => [
|
|
||||||
{
|
|
||||||
...response,
|
|
||||||
type: 'created-common-secret',
|
|
||||||
label: `Published secret key for group ${groupId}: awaiting confirmation`,
|
|
||||||
labelDone: `Published secret key for group ${groupId}: success!`,
|
|
||||||
done: false,
|
|
||||||
groupId,
|
|
||||||
},
|
|
||||||
...prev,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
setIsLoading(false);
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsForceShowCreationKeyPopup(false)
|
|
||||||
}, 1000);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error("Failed to encrypt and publish symmetric key for group chat:", error.message || "An error occurred");
|
|
||||||
setIsLoading(false);
|
|
||||||
});
|
});
|
||||||
|
setOpenSnack(true);
|
||||||
} catch (error) {
|
setTxList((prev) => [
|
||||||
|
{
|
||||||
}
|
...response,
|
||||||
|
type: 'created-common-secret',
|
||||||
|
label: `Published secret key for group ${groupId}: awaiting confirmation`,
|
||||||
|
labelDone: `Published secret key for group ${groupId}: success!`,
|
||||||
|
done: false,
|
||||||
|
groupId,
|
||||||
|
},
|
||||||
|
...prev,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsForceShowCreationKeyPopup(false);
|
||||||
|
}, 1000);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(
|
||||||
|
'Failed to encrypt and publish symmetric key for group chat:',
|
||||||
|
error.message || 'An error occurred'
|
||||||
|
);
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<Box
|
||||||
padding: '25px',
|
sx={{
|
||||||
display: 'flex',
|
background: theme.palette.background.default,
|
||||||
flexDirection: 'column',
|
|
||||||
gap: '25px',
|
|
||||||
maxWidth: '350px',
|
|
||||||
background: '#444444'
|
|
||||||
}}>
|
|
||||||
<LoadingButton loading={isLoading} loadingPosition="start" color="warning" variant='contained' onClick={createCommonSecret}>Re-encrypt key</LoadingButton>
|
|
||||||
{noSecretKey ? (
|
|
||||||
<Box>
|
|
||||||
<Typography>There is no group secret key. Be the first admin to publish one!</Typography>
|
|
||||||
</Box>
|
|
||||||
) : isOwner && secretKeyDetails && userInfo?.name && userInfo.name !== secretKeyDetails?.name ? (
|
|
||||||
<Box>
|
|
||||||
<Typography>The latest group secret key was published by a non-owner. As the owner of the group please re-encrypt the key as a safeguard</Typography>
|
|
||||||
</Box>
|
|
||||||
): isForceShowCreationKeyPopup ? null : (
|
|
||||||
<Box>
|
|
||||||
<Typography>The group member list has changed. Please re-encrypt the secret key.</Typography>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
<Box sx={{
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
width: '100%',
|
flexDirection: 'column',
|
||||||
justifyContent: 'flex-end'
|
gap: '25px',
|
||||||
}}>
|
maxWidth: '350px',
|
||||||
<Button onClick={()=> {
|
padding: '25px',
|
||||||
setHideCommonKeyPopup(true)
|
}}
|
||||||
setIsForceShowCreationKeyPopup(false)
|
>
|
||||||
}} size='small'>Hide</Button>
|
<LoadingButton
|
||||||
|
loading={isLoading}
|
||||||
|
loadingPosition="start"
|
||||||
|
color="warning"
|
||||||
|
variant="contained"
|
||||||
|
onClick={createCommonSecret}
|
||||||
|
>
|
||||||
|
Re-encrypt key
|
||||||
|
</LoadingButton>
|
||||||
|
|
||||||
|
{noSecretKey ? (
|
||||||
|
<Box>
|
||||||
|
<Typography>
|
||||||
|
There is no group secret key. Be the first admin to publish one!
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
) : isOwner &&
|
||||||
|
secretKeyDetails &&
|
||||||
|
userInfo?.name &&
|
||||||
|
userInfo.name !== secretKeyDetails?.name ? (
|
||||||
|
<Box>
|
||||||
|
<Typography>
|
||||||
|
The latest group secret key was published by a non-owner. As the
|
||||||
|
owner of the group please re-encrypt the key as a safeguard
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
) : isForceShowCreationKeyPopup ? null : (
|
||||||
|
<Box>
|
||||||
|
<Typography>
|
||||||
|
The group member list has changed. Please re-encrypt the secret key.
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setHideCommonKeyPopup(true);
|
||||||
|
setIsForceShowCreationKeyPopup(false);
|
||||||
|
}}
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
Hide
|
||||||
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<CustomizedSnackbars open={openSnack} setOpen={setOpenSnack} info={infoSnack} setInfo={setInfoSnack} />
|
|
||||||
|
<CustomizedSnackbars
|
||||||
|
open={openSnack}
|
||||||
|
setOpen={setOpenSnack}
|
||||||
|
info={infoSnack}
|
||||||
|
setInfo={setInfoSnack}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
);
|
||||||
)
|
};
|
||||||
}
|
|
||||||
|
@ -5,31 +5,22 @@ import React, {
|
|||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { CreateCommonSecret } from './CreateCommonSecret';
|
|
||||||
import { reusableGet } from '../../qdn/publish/pubish';
|
|
||||||
import { uint8ArrayToObject } from '../../backgroundFunctions/encryption';
|
import { uint8ArrayToObject } from '../../backgroundFunctions/encryption';
|
||||||
import {
|
import {
|
||||||
base64ToUint8Array,
|
base64ToUint8Array,
|
||||||
objectToBase64,
|
objectToBase64,
|
||||||
} from '../../qdn/encryption/group-encryption';
|
} from '../../qdn/encryption/group-encryption';
|
||||||
import { ChatContainerComp } from './ChatContainer';
|
|
||||||
import { ChatList } from './ChatList';
|
|
||||||
import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css';
|
import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css';
|
||||||
import Tiptap from './TipTap';
|
import Tiptap from './TipTap';
|
||||||
import {
|
import { CustomButton } from '../../styles/App-styles';
|
||||||
AuthenticatedContainerInnerTop,
|
|
||||||
CustomButton,
|
|
||||||
} from '../../styles/App-styles';
|
|
||||||
import CircularProgress from '@mui/material/CircularProgress';
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
import { getBaseApi, getFee } from '../../background';
|
import { getFee } from '../../background';
|
||||||
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
|
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
|
||||||
import { Box, Typography } from '@mui/material';
|
import { Box, Typography, useTheme } from '@mui/material';
|
||||||
import { Spacer } from '../../common/Spacer';
|
import { Spacer } from '../../common/Spacer';
|
||||||
import ShortUniqueId from 'short-unique-id';
|
import ShortUniqueId from 'short-unique-id';
|
||||||
import { AnnouncementList } from './AnnouncementList';
|
import { AnnouncementList } from './AnnouncementList';
|
||||||
const uid = new ShortUniqueId({ length: 8 });
|
|
||||||
import CampaignIcon from '@mui/icons-material/Campaign';
|
import CampaignIcon from '@mui/icons-material/Campaign';
|
||||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
|
||||||
import { AnnouncementDiscussion } from './AnnouncementDiscussion';
|
import { AnnouncementDiscussion } from './AnnouncementDiscussion';
|
||||||
import {
|
import {
|
||||||
MyContext,
|
MyContext,
|
||||||
@ -42,9 +33,11 @@ import {
|
|||||||
import { RequestQueueWithPromise } from '../../utils/queue/queue';
|
import { RequestQueueWithPromise } from '../../utils/queue/queue';
|
||||||
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||||
import { addDataPublishesFunc, getDataPublishesFunc } from '../Group/Group';
|
import { addDataPublishesFunc, getDataPublishesFunc } from '../Group/Group';
|
||||||
import { getRootHeight } from '../../utils/mobile/mobileUtils';
|
|
||||||
|
const uid = new ShortUniqueId({ length: 8 });
|
||||||
|
|
||||||
export const requestQueueCommentCount = new RequestQueueWithPromise(3);
|
export const requestQueueCommentCount = new RequestQueueWithPromise(3);
|
||||||
|
|
||||||
export const requestQueuePublishedAccouncements = new RequestQueueWithPromise(
|
export const requestQueuePublishedAccouncements = new RequestQueueWithPromise(
|
||||||
3
|
3
|
||||||
);
|
);
|
||||||
@ -125,6 +118,7 @@ export const handleUnencryptedPublishes = (publishes) => {
|
|||||||
});
|
});
|
||||||
return publishesData;
|
return publishesData;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GroupAnnouncements = ({
|
export const GroupAnnouncements = ({
|
||||||
selectedGroup,
|
selectedGroup,
|
||||||
secretKey,
|
secretKey,
|
||||||
@ -264,6 +258,7 @@ export const GroupAnnouncements = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearEditorContent = () => {
|
const clearEditorContent = () => {
|
||||||
if (editorRef.current) {
|
if (editorRef.current) {
|
||||||
editorRef.current.chain().focus().clearContent().run();
|
editorRef.current.chain().focus().clearContent().run();
|
||||||
@ -301,10 +296,12 @@ export const GroupAnnouncements = ({
|
|||||||
try {
|
try {
|
||||||
pauseAllQueues();
|
pauseAllQueues();
|
||||||
const fee = await getFee('ARBITRARY');
|
const fee = await getFee('ARBITRARY');
|
||||||
|
|
||||||
await show({
|
await show({
|
||||||
message: 'Would you like to perform a ARBITRARY transaction?',
|
message: 'Would you like to perform a ARBITRARY transaction?',
|
||||||
publishFee: fee.fee + ' QORT',
|
publishFee: fee.fee + ' QORT',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isSending) return;
|
if (isSending) return;
|
||||||
if (editorRef.current) {
|
if (editorRef.current) {
|
||||||
const htmlContent = editorRef.current.getHTML();
|
const htmlContent = editorRef.current.getHTML();
|
||||||
@ -387,8 +384,7 @@ export const GroupAnnouncements = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
} finally {
|
console.log(error);
|
||||||
// dispatch(setIsLoadingGlobal(false))
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[secretKey]
|
[secretKey]
|
||||||
@ -437,6 +433,8 @@ export const GroupAnnouncements = ({
|
|||||||
|
|
||||||
const interval = useRef<any>(null);
|
const interval = useRef<any>(null);
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
const checkNewMessages = React.useCallback(async () => {
|
const checkNewMessages = React.useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const identifier = `grp-${selectedGroup}-anc-`;
|
const identifier = `grp-${selectedGroup}-anc-`;
|
||||||
@ -485,7 +483,7 @@ export const GroupAnnouncements = ({
|
|||||||
}
|
}
|
||||||
setAnnouncements((prev) => [...newArray, ...prev]);
|
setAnnouncements((prev) => [...newArray, ...prev]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
} finally {
|
console.log(error);
|
||||||
}
|
}
|
||||||
}, [announcements, secretKey, selectedGroup]);
|
}, [announcements, secretKey, selectedGroup]);
|
||||||
|
|
||||||
@ -537,10 +535,10 @@ export const GroupAnnouncements = ({
|
|||||||
: 'calc(100vh - 70px)',
|
: 'calc(100vh - 70px)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
width: '100%',
|
|
||||||
visibility: hide && 'hidden',
|
|
||||||
position: hide && 'fixed',
|
|
||||||
left: hide && '-1000px',
|
left: hide && '-1000px',
|
||||||
|
position: hide && 'fixed',
|
||||||
|
visibility: hide && 'hidden',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AnnouncementDiscussion
|
<AnnouncementDiscussion
|
||||||
@ -560,54 +558,54 @@ export const GroupAnnouncements = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
// reference to change height
|
|
||||||
height: isMobile ? `calc(${rootHeight} - 127px` : 'calc(100vh - 70px)',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
width: '100%',
|
height: 'calc(100vh - 70px)',
|
||||||
visibility: hide && 'hidden',
|
|
||||||
position: hide && 'fixed',
|
|
||||||
left: hide && '-1000px',
|
left: hide && '-1000px',
|
||||||
|
position: hide && 'fixed',
|
||||||
|
visibility: hide && 'hidden',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: 'relative',
|
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
|
position: 'relative',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
padding: isMobile ? '8px' : '25px',
|
|
||||||
fontSize: isMobile ? '16px' : '20px',
|
|
||||||
gap: '20px',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
display: 'flex',
|
||||||
|
fontSize: '20px',
|
||||||
|
gap: '20px',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: '25px',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CampaignIcon
|
<CampaignIcon
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: isMobile ? '16px' : '30px',
|
fontSize: '30px',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
Group Announcements
|
Group Announcements
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Spacer height={isMobile ? '0px' : '25px'} />
|
<Spacer height={'25px'} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!isLoading && combinedListTempAndReal?.length === 0 && (
|
{!isLoading && combinedListTempAndReal?.length === 0 && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
@ -634,31 +632,28 @@ export const GroupAnnouncements = ({
|
|||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
// position: 'fixed',
|
backgroundColor: theme.palette.background.default,
|
||||||
// bottom: '0px',
|
bottom: isFocusedParent ? '0px' : 'unset',
|
||||||
backgroundColor: '#232428',
|
boxSizing: 'border-box',
|
||||||
minHeight: isMobile ? '0px' : '150px',
|
|
||||||
maxHeight: isMobile ? 'auto' : '400px',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
overflow: 'hidden',
|
|
||||||
width: '100%',
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
padding: isMobile ? '10px' : '20px',
|
|
||||||
position: isFocusedParent ? 'fixed' : 'relative',
|
|
||||||
bottom: isFocusedParent ? '0px' : 'unset',
|
|
||||||
top: isFocusedParent ? '0px' : 'unset',
|
|
||||||
zIndex: isFocusedParent ? 5 : 'unset',
|
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
|
maxHeight: '400px',
|
||||||
|
minHeight: '150px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
padding: '20px',
|
||||||
|
position: isFocusedParent ? 'fixed' : 'relative',
|
||||||
|
top: isFocusedParent ? '0px' : 'unset',
|
||||||
|
width: '100%',
|
||||||
|
zIndex: isFocusedParent ? 5 : 'unset',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
flexGrow: isMobile && 1,
|
flexGrow: 1,
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
// height: '100%',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tiptap
|
<Tiptap
|
||||||
@ -670,14 +665,15 @@ export const GroupAnnouncements = ({
|
|||||||
setIsFocusedParent={setIsFocusedParent}
|
setIsFocusedParent={setIsFocusedParent}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
width: '100&',
|
flexShrink: 0,
|
||||||
gap: '10px',
|
gap: '10px',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
flexShrink: 0,
|
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
width: '100&',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isFocusedParent && (
|
{isFocusedParent && (
|
||||||
@ -692,43 +688,46 @@ export const GroupAnnouncements = ({
|
|||||||
// Unfocus the editor
|
// Unfocus the editor
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
marginTop: 'auto',
|
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
cursor: isSending ? 'default' : 'pointer',
|
|
||||||
background: 'var(--danger)',
|
background: 'var(--danger)',
|
||||||
|
cursor: isSending ? 'default' : 'pointer',
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
padding: isMobile && '5px',
|
fontSize: '14px',
|
||||||
fontSize: isMobile && '14px',
|
marginTop: 'auto',
|
||||||
|
padding: '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{` Close`}
|
{` Close`}
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<CustomButton
|
<CustomButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (isSending) return;
|
if (isSending) return;
|
||||||
publishAnnouncement();
|
publishAnnouncement();
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
marginTop: 'auto',
|
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
|
background: isSending
|
||||||
|
? theme.palette.background.default
|
||||||
|
: theme.palette.background.paper,
|
||||||
cursor: isSending ? 'default' : 'pointer',
|
cursor: isSending ? 'default' : 'pointer',
|
||||||
background: isSending && 'rgba(0, 0, 0, 0.8)',
|
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
padding: isMobile && '5px',
|
fontSize: '14px',
|
||||||
fontSize: isMobile && '14px',
|
marginTop: 'auto',
|
||||||
|
padding: '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isSending && (
|
{isSending && (
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
size={18}
|
size={18}
|
||||||
sx={{
|
sx={{
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
left: '50%',
|
||||||
|
marginLeft: '-12px',
|
||||||
|
marginTop: '-12px',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '50%',
|
top: '50%',
|
||||||
left: '50%',
|
|
||||||
marginTop: '-12px',
|
|
||||||
marginLeft: '-12px',
|
|
||||||
color: 'white',
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -1,19 +1,6 @@
|
|||||||
import React, {
|
import { useContext, useEffect, useState } from 'react';
|
||||||
useCallback,
|
import { GroupMail } from '../Group/Forum/GroupMail';
|
||||||
useContext,
|
import { MyContext, isMobile } from '../../App';
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from "react";
|
|
||||||
import { GroupMail } from "../Group/Forum/GroupMail";
|
|
||||||
import { MyContext, isMobile } from "../../App";
|
|
||||||
import { getRootHeight } from "../../utils/mobile/mobileUtils";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const GroupForum = ({
|
export const GroupForum = ({
|
||||||
selectedGroup,
|
selectedGroup,
|
||||||
@ -23,12 +10,13 @@ export const GroupForum = ({
|
|||||||
isAdmin,
|
isAdmin,
|
||||||
myAddress,
|
myAddress,
|
||||||
hide,
|
hide,
|
||||||
defaultThread,
|
defaultThread,
|
||||||
setDefaultThread,
|
setDefaultThread,
|
||||||
isPrivate
|
isPrivate,
|
||||||
}) => {
|
}) => {
|
||||||
const { rootHeight } = useContext(MyContext);
|
const { rootHeight } = useContext(MyContext);
|
||||||
const [isMoved, setIsMoved] = useState(false);
|
const [isMoved, setIsMoved] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hide) {
|
if (hide) {
|
||||||
setTimeout(() => setIsMoved(true), 300); // Wait for the fade-out to complete before moving
|
setTimeout(() => setIsMoved(true), 300); // Wait for the fade-out to complete before moving
|
||||||
@ -39,20 +27,27 @@ export const GroupForum = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
// reference to change height
|
display: 'flex',
|
||||||
height: isMobile ? `calc(${rootHeight} - 127px` : "calc(100vh - 70px)",
|
flexDirection: 'column',
|
||||||
display: "flex",
|
height: 'calc(100vh - 70px)',
|
||||||
flexDirection: "column",
|
left: hide && '-1000px',
|
||||||
width: "100%",
|
opacity: hide ? 0 : 1,
|
||||||
opacity: hide ? 0 : 1,
|
position: hide ? 'fixed' : 'relative',
|
||||||
visibility: hide && 'hidden',
|
visibility: hide && 'hidden',
|
||||||
position: hide ? 'fixed' : 'relative',
|
width: '100%',
|
||||||
left: hide && '-1000px'
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<GroupMail
|
||||||
<GroupMail isPrivate={isPrivate} hide={hide} getSecretKey={getSecretKey} selectedGroup={selectedGroup} userInfo={userInfo} secretKey={secretKey} defaultThread={defaultThread} setDefaultThread={setDefaultThread} />
|
isPrivate={isPrivate}
|
||||||
|
hide={hide}
|
||||||
</div>
|
getSecretKey={getSecretKey}
|
||||||
|
selectedGroup={selectedGroup}
|
||||||
|
userInfo={userInfo}
|
||||||
|
secretKey={secretKey}
|
||||||
|
defaultThread={defaultThread}
|
||||||
|
setDefaultThread={setDefaultThread}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,69 +1,68 @@
|
|||||||
import React, {
|
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
|
||||||
forwardRef, useEffect, useImperativeHandle,
|
|
||||||
useState,
|
export default forwardRef((props, ref) => {
|
||||||
} from 'react'
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||||
|
|
||||||
export default forwardRef((props, ref) => {
|
const selectItem = (index) => {
|
||||||
const [selectedIndex, setSelectedIndex] = useState(0)
|
const item = props.items[index];
|
||||||
|
|
||||||
const selectItem = index => {
|
if (item) {
|
||||||
const item = props.items[index]
|
props.command(item);
|
||||||
|
}
|
||||||
if (item) {
|
};
|
||||||
props.command(item)
|
|
||||||
|
const upHandler = () => {
|
||||||
|
setSelectedIndex(
|
||||||
|
(selectedIndex + props.items.length - 1) % props.items.length
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const downHandler = () => {
|
||||||
|
setSelectedIndex((selectedIndex + 1) % props.items.length);
|
||||||
|
};
|
||||||
|
|
||||||
|
const enterHandler = () => {
|
||||||
|
selectItem(selectedIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => setSelectedIndex(0), [props.items]);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
onKeyDown: ({ event }) => {
|
||||||
|
if (event.key === 'ArrowUp') {
|
||||||
|
upHandler();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (event.key === 'ArrowDown') {
|
||||||
const upHandler = () => {
|
downHandler();
|
||||||
setSelectedIndex((selectedIndex + props.items.length - 1) % props.items.length)
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const downHandler = () => {
|
if (event.key === 'Enter') {
|
||||||
setSelectedIndex((selectedIndex + 1) % props.items.length)
|
enterHandler();
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
const enterHandler = () => {
|
|
||||||
selectItem(selectedIndex)
|
return false;
|
||||||
}
|
},
|
||||||
|
}));
|
||||||
useEffect(() => setSelectedIndex(0), [props.items])
|
|
||||||
|
return (
|
||||||
useImperativeHandle(ref, () => ({
|
<div className="dropdown-menu">
|
||||||
onKeyDown: ({ event }) => {
|
{props.items.length ? (
|
||||||
if (event.key === 'ArrowUp') {
|
props.items.map((item, index) => (
|
||||||
upHandler()
|
<button
|
||||||
return true
|
className={index === selectedIndex ? 'is-selected' : ''}
|
||||||
}
|
key={item.id || index}
|
||||||
|
onClick={() => selectItem(index)}
|
||||||
if (event.key === 'ArrowDown') {
|
>
|
||||||
downHandler()
|
{item.label}
|
||||||
return true
|
</button>
|
||||||
}
|
))
|
||||||
|
) : (
|
||||||
if (event.key === 'Enter') {
|
<div className="item">No result</div>
|
||||||
enterHandler()
|
)}
|
||||||
return true
|
</div>
|
||||||
}
|
);
|
||||||
|
});
|
||||||
return false
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="dropdown-menu">
|
|
||||||
{props.items.length
|
|
||||||
? props.items.map((item, index) => (
|
|
||||||
<button
|
|
||||||
className={index === selectedIndex ? 'is-selected' : ''}
|
|
||||||
key={item.id || index}
|
|
||||||
onClick={() => selectItem(index)}
|
|
||||||
>
|
|
||||||
{item.label}
|
|
||||||
</button>
|
|
||||||
))
|
|
||||||
: <div className="item">No result</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import React, { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import { NodeViewWrapper } from '@tiptap/react';
|
import { NodeViewWrapper } from '@tiptap/react';
|
||||||
|
import { useTheme } from '@mui/material';
|
||||||
|
|
||||||
const ResizableImage = ({ node, updateAttributes, selected }) => {
|
const ResizableImage = ({ node, updateAttributes, selected }) => {
|
||||||
const imgRef = useRef(null);
|
const imgRef = useRef(null);
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
const startResizing = (e) => {
|
const startResizing = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -40,18 +42,23 @@ const ResizableImage = ({ node, updateAttributes, selected }) => {
|
|||||||
src={node.attrs.src}
|
src={node.attrs.src}
|
||||||
alt={node.attrs.alt || ''}
|
alt={node.attrs.alt || ''}
|
||||||
title={node.attrs.title || ''}
|
title={node.attrs.title || ''}
|
||||||
style={{ width: node.attrs.width || 'auto', display: 'block', margin: '0 auto' }}
|
style={{
|
||||||
|
width: node.attrs.width || 'auto',
|
||||||
|
display: 'block',
|
||||||
|
margin: '0 auto',
|
||||||
|
}}
|
||||||
draggable={false} // Prevent image dragging
|
draggable={false} // Prevent image dragging
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
bottom: 0,
|
||||||
|
cursor: 'nwse-resize',
|
||||||
|
height: '10px',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
right: 0,
|
right: 0,
|
||||||
bottom: 0,
|
|
||||||
width: '10px',
|
width: '10px',
|
||||||
height: '10px',
|
|
||||||
backgroundColor: 'gray',
|
|
||||||
cursor: 'nwse-resize',
|
|
||||||
zIndex: 1, // Ensure the resize handle is above other content
|
zIndex: 1, // Ensure the resize handle is above other content
|
||||||
}}
|
}}
|
||||||
onMouseDown={startResizing}
|
onMouseDown={startResizing}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user