mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-04-23 19:37:52 +00:00
chat performance improvements
This commit is contained in:
parent
cbdb28c5d5
commit
44dd926869
@ -673,7 +673,7 @@ const handleNotification = async (groups) => {
|
|||||||
|
|
||||||
let mutedGroups = (await getUserSettings({ key: "mutedGroups" })) || [];
|
let mutedGroups = (await getUserSettings({ key: "mutedGroups" })) || [];
|
||||||
if (!isArray(mutedGroups)) mutedGroups = [];
|
if (!isArray(mutedGroups)) mutedGroups = [];
|
||||||
|
mutedGroups.push('0')
|
||||||
let isFocused;
|
let isFocused;
|
||||||
const data = groups.filter(
|
const data = groups.filter(
|
||||||
(group) =>
|
(group) =>
|
||||||
@ -3182,6 +3182,7 @@ export const checkNewMessages = async () => {
|
|||||||
try {
|
try {
|
||||||
let mutedGroups = (await getUserSettings({ key: "mutedGroups" })) || [];
|
let mutedGroups = (await getUserSettings({ key: "mutedGroups" })) || [];
|
||||||
if (!isArray(mutedGroups)) mutedGroups = [];
|
if (!isArray(mutedGroups)) mutedGroups = [];
|
||||||
|
mutedGroups.push('0')
|
||||||
let myName = "";
|
let myName = "";
|
||||||
const userData = await getUserInfo();
|
const userData = await getUserInfo();
|
||||||
if (userData?.name) {
|
if (userData?.name) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect, useMemo } from 'react';
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
import './styles.css';
|
import './styles.css';
|
||||||
import { executeEvent } from '../../utils/events';
|
import { executeEvent } from '../../utils/events';
|
||||||
@ -63,30 +63,34 @@ function processText(input) {
|
|||||||
return wrapper.innerHTML;
|
return wrapper.innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MessageDisplay = ({ htmlContent, isReply }) => {
|
const linkify = (text) => {
|
||||||
const linkify = (text) => {
|
if (!text) return ""; // Return an empty string if text is null or undefined
|
||||||
if (!text) return ""; // Return an empty string if text is null or undefined
|
|
||||||
|
|
||||||
let textFormatted = text;
|
|
||||||
const urlPattern = /(\bhttps?:\/\/[^\s<]+|\bwww\.[^\s<]+)/g;
|
|
||||||
textFormatted = text.replace(urlPattern, (url) => {
|
|
||||||
const href = url.startsWith('http') ? url : `https://${url}`;
|
|
||||||
return `<a href="${DOMPurify.sanitize(href)}" class="auto-link">${DOMPurify.sanitize(url)}</a>`;
|
|
||||||
});
|
|
||||||
return processText(textFormatted);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const sanitizedContent = DOMPurify.sanitize(linkify(htmlContent), {
|
let textFormatted = text;
|
||||||
ALLOWED_TAGS: [
|
const urlPattern = /(\bhttps?:\/\/[^\s<]+|\bwww\.[^\s<]+)/g;
|
||||||
'a', 'b', 'i', 'em', 'strong', 'p', 'br', 'div', 'span', 'img',
|
textFormatted = text.replace(urlPattern, (url) => {
|
||||||
'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'code', 'pre', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 's', 'hr'
|
const href = url.startsWith('http') ? url : `https://${url}`;
|
||||||
],
|
return `<a href="${DOMPurify.sanitize(href)}" class="auto-link">${DOMPurify.sanitize(url)}</a>`;
|
||||||
ALLOWED_ATTR: [
|
});
|
||||||
'href', 'target', 'rel', 'class', 'src', 'alt', 'title',
|
return processText(textFormatted);
|
||||||
'width', 'height', 'style', 'align', 'valign', 'colspan', 'rowspan', 'border', 'cellpadding', 'cellspacing', 'data-url'
|
};
|
||||||
],
|
|
||||||
}).replace(/<span[^>]*data-url="qortal:\/\/use-embed\/[^"]*"[^>]*>.*?<\/span>/g, '');;
|
|
||||||
|
export const MessageDisplay = ({ htmlContent, isReply }) => {
|
||||||
|
|
||||||
|
|
||||||
|
const sanitizedContent = useMemo(()=> {
|
||||||
|
return DOMPurify.sanitize(linkify(htmlContent), {
|
||||||
|
ALLOWED_TAGS: [
|
||||||
|
'a', 'b', 'i', 'em', 'strong', 'p', 'br', 'div', 'span', 'img',
|
||||||
|
'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'code', 'pre', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 's', 'hr'
|
||||||
|
],
|
||||||
|
ALLOWED_ATTR: [
|
||||||
|
'href', 'target', 'rel', 'class', 'src', 'alt', 'title',
|
||||||
|
'width', 'height', 'style', 'align', 'valign', 'colspan', 'rowspan', 'border', 'cellpadding', 'cellspacing', 'data-url'
|
||||||
|
],
|
||||||
|
}).replace(/<span[^>]*data-url="qortal:\/\/use-embed\/[^"]*"[^>]*>.*?<\/span>/g, '');
|
||||||
|
}, [])
|
||||||
|
|
||||||
const handleClick = async (e) => {
|
const handleClick = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Message } from "@chatscope/chat-ui-kit-react";
|
import { Message } from "@chatscope/chat-ui-kit-react";
|
||||||
import React, { useContext, useEffect, useState } from "react";
|
import React, { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
||||||
import { useInView } from "react-intersection-observer";
|
import { useInView } from "react-intersection-observer";
|
||||||
import { MessageDisplay } from "./MessageDisplay";
|
import { MessageDisplay } from "./MessageDisplay";
|
||||||
import { Avatar, Box, Button, ButtonBase, List, ListItem, ListItemText, Popover, Tooltip, Typography } from "@mui/material";
|
import { Avatar, Box, Button, ButtonBase, List, ListItem, ListItemText, Popover, Tooltip, Typography } from "@mui/material";
|
||||||
@ -50,7 +50,7 @@ const getBadgeImg = (level)=> {
|
|||||||
default: return level0Img
|
default: return level0Img
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const MessageItem = ({
|
export const MessageItem = React.memo(({
|
||||||
message,
|
message,
|
||||||
onSeen,
|
onSeen,
|
||||||
isLast,
|
isLast,
|
||||||
@ -68,36 +68,78 @@ export const MessageItem = ({
|
|||||||
onEdit,
|
onEdit,
|
||||||
isPrivate
|
isPrivate
|
||||||
}) => {
|
}) => {
|
||||||
const { ref, inView } = useInView({
|
|
||||||
threshold: 0.7, // Fully visible
|
|
||||||
triggerOnce: false, // Only trigger once when it becomes visible
|
|
||||||
});
|
|
||||||
const {getIndividualUserInfo} = useContext(MyContext)
|
const {getIndividualUserInfo} = useContext(MyContext)
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
const [anchorEl, setAnchorEl] = useState(null);
|
||||||
const [selectedReaction, setSelectedReaction] = useState(null);
|
const [selectedReaction, setSelectedReaction] = useState(null);
|
||||||
const userInfo = useRecoilValue(addressInfoKeySelector(message?.sender));
|
const [userInfo, setUserInfo] = useState(null)
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (inView && isLast && onSeen) {
|
|
||||||
onSeen(message.id);
|
|
||||||
}
|
|
||||||
}, [inView, message.id, isLast]);
|
|
||||||
|
|
||||||
useEffect(()=> {
|
useEffect(()=> {
|
||||||
if(message?.sender){
|
const getInfo = async ()=> {
|
||||||
getIndividualUserInfo(message?.sender)
|
if(!message?.sender) return
|
||||||
|
try {
|
||||||
|
const res = await getIndividualUserInfo(message?.sender)
|
||||||
|
if(!res) return null
|
||||||
|
setUserInfo(res)
|
||||||
|
} catch (error) {
|
||||||
|
//
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [message?.sender])
|
|
||||||
|
getInfo()
|
||||||
|
}, [message?.sender, getIndividualUserInfo])
|
||||||
|
|
||||||
|
const htmlText = useMemo(()=> {
|
||||||
|
|
||||||
|
if(message?.messageText){
|
||||||
|
return generateHTML(message?.messageText, [
|
||||||
|
StarterKit,
|
||||||
|
Underline,
|
||||||
|
Highlight,
|
||||||
|
Mention,
|
||||||
|
TextStyle
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const htmlReply = useMemo(()=> {
|
||||||
|
|
||||||
|
if(reply?.messageText){
|
||||||
|
return generateHTML(reply?.messageText, [
|
||||||
|
StarterKit,
|
||||||
|
Underline,
|
||||||
|
Highlight,
|
||||||
|
Mention,
|
||||||
|
TextStyle
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const userAvatarUrl = useMemo(()=> {
|
||||||
|
return message?.senderName ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||||
|
message?.senderName
|
||||||
|
}/qortal_avatar?async=true` : ''
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const onSeenFunc = useCallback(()=> {
|
||||||
|
onSeen(message.id);
|
||||||
|
}, [message?.id])
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<MessageWragger lastMessage={lastSignature === message?.signature} isLast={isLast} onSeen={onSeenFunc}>
|
||||||
|
|
||||||
{message?.divide && (
|
{message?.divide && (
|
||||||
<div className="unread-divider" id="unread-divider-id">
|
<div className="unread-divider" id="unread-divider-id">
|
||||||
Unread messages below
|
Unread messages below
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
ref={lastSignature === message?.signature ? ref : null}
|
|
||||||
style={{
|
style={{
|
||||||
padding: "10px",
|
padding: "10px",
|
||||||
backgroundColor: "#232428",
|
backgroundColor: "#232428",
|
||||||
@ -132,25 +174,25 @@ useEffect(()=> {
|
|||||||
sx={{
|
sx={{
|
||||||
backgroundColor: "#27282c",
|
backgroundColor: "#27282c",
|
||||||
color: "white",
|
color: "white",
|
||||||
|
height: '40px',
|
||||||
|
width: '40px'
|
||||||
}}
|
}}
|
||||||
alt={message?.senderName}
|
alt={message?.senderName}
|
||||||
src={message?.senderName ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
src={userAvatarUrl}
|
||||||
message?.senderName
|
|
||||||
}/qortal_avatar?async=true` : ''}
|
|
||||||
>
|
>
|
||||||
{message?.senderName?.charAt(0)}
|
{message?.senderName?.charAt(0)}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
|
|
||||||
|
|
||||||
</WrapperUserAction>
|
</WrapperUserAction>
|
||||||
<Tooltip disableFocusListener title={`level ${userInfo?.level}`}>
|
<Tooltip disableFocusListener title={`level ${userInfo}`}>
|
||||||
|
|
||||||
|
|
||||||
<img style={{
|
<img style={{
|
||||||
visibility: userInfo?.level !== undefined ? 'visible' : 'hidden',
|
visibility: userInfo !== undefined ? 'visible' : 'hidden',
|
||||||
width: '30px',
|
width: '30px',
|
||||||
height: 'auto'
|
height: 'auto'
|
||||||
}} src={getBadgeImg(userInfo?.level)} />
|
}} src={getBadgeImg(userInfo)} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
@ -257,13 +299,7 @@ useEffect(()=> {
|
|||||||
}}>Replied to {reply?.senderName || reply?.senderAddress}</Typography>
|
}}>Replied to {reply?.senderName || reply?.senderAddress}</Typography>
|
||||||
{reply?.messageText && (
|
{reply?.messageText && (
|
||||||
<MessageDisplay
|
<MessageDisplay
|
||||||
htmlContent={generateHTML(reply?.messageText, [
|
htmlContent={htmlReply}
|
||||||
StarterKit,
|
|
||||||
Underline,
|
|
||||||
Highlight,
|
|
||||||
Mention,
|
|
||||||
TextStyle
|
|
||||||
])}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{reply?.decryptedData?.type === "notification" ? (
|
{reply?.decryptedData?.type === "notification" ? (
|
||||||
@ -275,17 +311,11 @@ useEffect(()=> {
|
|||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{message?.messageText && (
|
|
||||||
<MessageDisplay
|
<MessageDisplay
|
||||||
htmlContent={generateHTML(message?.messageText, [
|
htmlContent={htmlText}
|
||||||
StarterKit,
|
|
||||||
Underline,
|
|
||||||
Highlight,
|
|
||||||
Mention,
|
|
||||||
TextStyle
|
|
||||||
])}
|
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
{message?.decryptedData?.type === "notification" ? (
|
{message?.decryptedData?.type === "notification" ? (
|
||||||
<MessageDisplay htmlContent={message.decryptedData?.data?.message} />
|
<MessageDisplay htmlContent={message.decryptedData?.data?.message} />
|
||||||
) : (
|
) : (
|
||||||
@ -457,21 +487,11 @@ useEffect(()=> {
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* <Message
|
|
||||||
model={{
|
|
||||||
direction: 'incoming',
|
|
||||||
message: message.text,
|
|
||||||
position: 'single',
|
|
||||||
sender: message.senderName,
|
|
||||||
sentTime: message.timestamp
|
|
||||||
}}
|
|
||||||
|
|
||||||
></Message> */}
|
|
||||||
{/* {!message.unread && <span style={{ color: 'green' }}> Seen</span>} */}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</MessageWragger>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
|
|
||||||
export const ReplyPreview = ({message, isEdit})=> {
|
export const ReplyPreview = ({message, isEdit})=> {
|
||||||
@ -530,4 +550,36 @@ export const ReplyPreview = ({message, isEdit})=> {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const MessageWragger = ({lastMessage, onSeen, isLast, children})=> {
|
||||||
|
|
||||||
|
if(lastMessage){
|
||||||
|
return (
|
||||||
|
<WatchComponent onSeen={onSeen} isLast={isLast}>{children}</WatchComponent>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return children
|
||||||
|
}
|
||||||
|
|
||||||
|
const WatchComponent = ({onSeen, isLast, children})=> {
|
||||||
|
const { ref, inView } = useInView({
|
||||||
|
threshold: 0.7, // Fully visible
|
||||||
|
triggerOnce: true, // Only trigger once when it becomes visible
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (inView && isLast && onSeen) {
|
||||||
|
onSeen();
|
||||||
|
}
|
||||||
|
}, [inView, isLast, onSeen]);
|
||||||
|
|
||||||
|
return <div ref={ref} style={{
|
||||||
|
width: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center'
|
||||||
|
}}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
|
||||||
}
|
}
|
@ -118,7 +118,7 @@ export const DesktopHeader = ({
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{selectedGroup?.groupName}
|
{selectedGroup?.groupId === '0' ? 'General' :selectedGroup?.groupName}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
@ -126,76 +126,10 @@ export const DesktopHeader = ({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
gap: "20px",
|
gap: "20px",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
visibility: selectedGroup?.groupId === '0' ? 'hidden' : 'visibile'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* <ButtonBase
|
|
||||||
onClick={() => {
|
|
||||||
goToHome();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconWrapper
|
|
||||||
color="rgba(250, 250, 250, 0.5)"
|
|
||||||
label="Home"
|
|
||||||
selected={isHome}
|
|
||||||
>
|
|
||||||
<HomeIcon
|
|
||||||
height={25}
|
|
||||||
color={isHome ? "white" : "rgba(250, 250, 250, 0.5)"}
|
|
||||||
/>
|
|
||||||
</IconWrapper>
|
|
||||||
</ButtonBase>
|
|
||||||
<ButtonBase
|
|
||||||
onClick={() => {
|
|
||||||
setDesktopSideView("groups");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconWrapper
|
|
||||||
color="rgba(250, 250, 250, 0.5)"
|
|
||||||
label="Groups"
|
|
||||||
selected={isGroups}
|
|
||||||
>
|
|
||||||
<HubsIcon
|
|
||||||
height={25}
|
|
||||||
color={
|
|
||||||
hasUnreadGroups
|
|
||||||
? "var(--danger)"
|
|
||||||
: isGroups
|
|
||||||
? "white"
|
|
||||||
: "rgba(250, 250, 250, 0.5)"
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</IconWrapper>
|
|
||||||
</ButtonBase>
|
|
||||||
<ButtonBase
|
|
||||||
onClick={() => {
|
|
||||||
setDesktopSideView("directs");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconWrapper
|
|
||||||
color="rgba(250, 250, 250, 0.5)"
|
|
||||||
label="Messaging"
|
|
||||||
selected={isDirects}
|
|
||||||
>
|
|
||||||
<MessagingIcon
|
|
||||||
height={25}
|
|
||||||
color={
|
|
||||||
hasUnreadDirects
|
|
||||||
? "var(--danger)"
|
|
||||||
: isDirects
|
|
||||||
? "white"
|
|
||||||
: "rgba(250, 250, 250, 0.5)"
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</IconWrapper>
|
|
||||||
</ButtonBase> */}
|
|
||||||
{/* <Box
|
|
||||||
sx={{
|
|
||||||
width: "1px",
|
|
||||||
height: "50px",
|
|
||||||
background: "white",
|
|
||||||
borderRadius: "50px",
|
|
||||||
}}
|
|
||||||
/> */}
|
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
goToAnnouncements()
|
goToAnnouncements()
|
||||||
|
@ -447,12 +447,14 @@ export const Group = ({
|
|||||||
const setUserInfoForLevels = useSetRecoilState(addressInfoControllerAtom);
|
const setUserInfoForLevels = useSetRecoilState(addressInfoControllerAtom);
|
||||||
|
|
||||||
const isPrivate = useMemo(()=> {
|
const isPrivate = useMemo(()=> {
|
||||||
|
if(selectedGroup?.groupId === '0') return false
|
||||||
if(!selectedGroup?.groupId || !groupsProperties[selectedGroup?.groupId]) return null
|
if(!selectedGroup?.groupId || !groupsProperties[selectedGroup?.groupId]) return null
|
||||||
if(groupsProperties[selectedGroup?.groupId]?.isOpen === true) return false
|
if(groupsProperties[selectedGroup?.groupId]?.isOpen === true) return false
|
||||||
if(groupsProperties[selectedGroup?.groupId]?.isOpen === false) return true
|
if(groupsProperties[selectedGroup?.groupId]?.isOpen === false) return true
|
||||||
return null
|
return null
|
||||||
}, [selectedGroup])
|
}, [selectedGroup])
|
||||||
|
|
||||||
|
|
||||||
const setSelectedGroupId = useSetRecoilState(selectedGroupIdAtom)
|
const setSelectedGroupId = useSetRecoilState(selectedGroupIdAtom)
|
||||||
const toggleSideViewDirects = ()=> {
|
const toggleSideViewDirects = ()=> {
|
||||||
if(isOpenSideViewGroups){
|
if(isOpenSideViewGroups){
|
||||||
@ -1937,7 +1939,7 @@ export const Group = ({
|
|||||||
|
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={group.groupName}
|
primary={group.groupId === '0' ? 'General' : group.groupName}
|
||||||
secondary={!group?.timestamp ? 'no messages' :`last message: ${formatEmailDate(group?.timestamp)}`}
|
secondary={!group?.timestamp ? 'no messages' :`last message: ${formatEmailDate(group?.timestamp)}`}
|
||||||
primaryTypographyProps={{
|
primaryTypographyProps={{
|
||||||
style: {
|
style: {
|
||||||
|
@ -78,7 +78,15 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
const data = JSON.parse(e.data);
|
const data = JSON.parse(e.data);
|
||||||
const filteredGroups = data.groups?.filter(item => item?.groupId !== 0) || [];
|
const copyGroups = [...(data?.groups || [])]
|
||||||
|
const findIndex = copyGroups?.findIndex(item => item?.groupId === 0)
|
||||||
|
if(findIndex !== -1){
|
||||||
|
copyGroups[findIndex] = {
|
||||||
|
...(copyGroups[findIndex] || {}),
|
||||||
|
groupId: "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const filteredGroups = copyGroups
|
||||||
const sortedGroups = filteredGroups.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
|
const sortedGroups = filteredGroups.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
|
||||||
const sortedDirects = (data?.direct || []).filter(item =>
|
const sortedDirects = (data?.direct || []).filter(item =>
|
||||||
item?.name !== 'extension-proxy' && item?.address !== 'QSMMGSgysEuqDCuLw3S4cHrQkBrh3vP3VH'
|
item?.name !== 'extension-proxy' && item?.address !== 'QSMMGSgysEuqDCuLw3S4cHrQkBrh3vP3VH'
|
||||||
|
@ -1,34 +1,32 @@
|
|||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useRef } from "react";
|
||||||
import { getBaseApiReact } from "../../App";
|
import { getBaseApiReact } from "../../App";
|
||||||
import { useRecoilState, useSetRecoilState } from "recoil";
|
|
||||||
import { addressInfoControllerAtom } from "../../atoms/global";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const useHandleUserInfo = () => {
|
export const useHandleUserInfo = () => {
|
||||||
const [userInfo, setUserInfo] = useRecoilState(addressInfoControllerAtom);
|
const userInfoRef = useRef({})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const getIndividualUserInfo = useCallback(async (address)=> {
|
const getIndividualUserInfo = useCallback(async (address)=> {
|
||||||
try {
|
try {
|
||||||
if(!address || userInfo[address]) return
|
if(!address) return null
|
||||||
|
if(userInfoRef.current[address] !== undefined) return userInfoRef.current[address]
|
||||||
|
|
||||||
const url = `${getBaseApiReact()}/addresses/${address}`;
|
const url = `${getBaseApiReact()}/addresses/${address}`;
|
||||||
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 data = await response.json();
|
const data = await response.json();
|
||||||
setUserInfo((prev)=> {
|
userInfoRef.current = {
|
||||||
return {
|
...userInfoRef.current,
|
||||||
...prev,
|
[address]: data?.level
|
||||||
[address]: data
|
}
|
||||||
}
|
return data?.level
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
//error
|
//error
|
||||||
}
|
}
|
||||||
}, [userInfo])
|
}, [])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getIndividualUserInfo,
|
getIndividualUserInfo,
|
||||||
|
@ -46,81 +46,83 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Popover */}
|
{/* Popover */}
|
||||||
<Popover
|
{open && (
|
||||||
id={id}
|
<Popover
|
||||||
open={open}
|
id={id}
|
||||||
anchorEl={anchorEl}
|
open={open}
|
||||||
onClose={handleClose} // Close popover on click outside
|
anchorEl={anchorEl}
|
||||||
anchorOrigin={{
|
onClose={handleClose} // Close popover on click outside
|
||||||
vertical: 'bottom',
|
anchorOrigin={{
|
||||||
horizontal: 'center',
|
vertical: 'bottom',
|
||||||
}}
|
horizontal: 'center',
|
||||||
transformOrigin={{
|
}}
|
||||||
vertical: 'top',
|
transformOrigin={{
|
||||||
horizontal: 'center',
|
vertical: 'top',
|
||||||
}}
|
horizontal: 'center',
|
||||||
componentsProps={{
|
}}
|
||||||
paper: {
|
componentsProps={{
|
||||||
onClick: (event) => event.stopPropagation(), // Stop propagation inside popover
|
paper: {
|
||||||
},
|
onClick: (event) => event.stopPropagation(), // Stop propagation inside popover
|
||||||
}}
|
},
|
||||||
>
|
}}
|
||||||
<Box sx={{ p: 2, display: 'flex', flexDirection: 'column', gap: 1 }}>
|
>
|
||||||
{/* Option 1: Message */}
|
<Box sx={{ p: 2, display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||||
<Button
|
{/* Option 1: Message */}
|
||||||
variant="text"
|
<Button
|
||||||
onClick={() => {
|
variant="text"
|
||||||
|
onClick={() => {
|
||||||
handleClose();
|
|
||||||
setTimeout(() => {
|
handleClose();
|
||||||
executeEvent('openDirectMessageInternal', {
|
setTimeout(() => {
|
||||||
address,
|
executeEvent('openDirectMessageInternal', {
|
||||||
name,
|
address,
|
||||||
});
|
name,
|
||||||
}, 200);
|
});
|
||||||
}}
|
}, 200);
|
||||||
sx={{
|
}}
|
||||||
color: 'white',
|
sx={{
|
||||||
justifyContent: 'flex-start'
|
color: 'white',
|
||||||
}}
|
justifyContent: 'flex-start'
|
||||||
>
|
}}
|
||||||
Message
|
>
|
||||||
</Button>
|
Message
|
||||||
|
</Button>
|
||||||
{/* Option 2: Send QORT */}
|
|
||||||
<Button
|
{/* Option 2: Send QORT */}
|
||||||
variant="text"
|
<Button
|
||||||
onClick={() => {
|
variant="text"
|
||||||
executeEvent('openPaymentInternal', {
|
onClick={() => {
|
||||||
address,
|
executeEvent('openPaymentInternal', {
|
||||||
name,
|
address,
|
||||||
});
|
name,
|
||||||
handleClose();
|
});
|
||||||
|
handleClose();
|
||||||
}}
|
|
||||||
sx={{
|
}}
|
||||||
color: 'white',
|
sx={{
|
||||||
justifyContent: 'flex-start'
|
color: 'white',
|
||||||
}}
|
justifyContent: 'flex-start'
|
||||||
>
|
}}
|
||||||
Send QORT
|
>
|
||||||
</Button>
|
Send QORT
|
||||||
<Button
|
</Button>
|
||||||
variant="text"
|
<Button
|
||||||
onClick={() => {
|
variant="text"
|
||||||
navigator.clipboard.writeText(address|| "");
|
onClick={() => {
|
||||||
handleClose();
|
navigator.clipboard.writeText(address|| "");
|
||||||
|
handleClose();
|
||||||
}}
|
|
||||||
sx={{
|
}}
|
||||||
color: 'white',
|
sx={{
|
||||||
justifyContent: 'flex-start'
|
color: 'white',
|
||||||
}}
|
justifyContent: 'flex-start'
|
||||||
>
|
}}
|
||||||
Copy address
|
>
|
||||||
</Button>
|
Copy address
|
||||||
</Box>
|
</Button>
|
||||||
</Popover>
|
</Box>
|
||||||
|
</Popover>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user