This commit is contained in:
PhilReact 2025-02-26 16:03:20 +02:00
parent 19b183aabf
commit ce530293ab
10 changed files with 617 additions and 22 deletions

View File

@ -143,6 +143,7 @@ import { Minting } from "./components/Minting/Minting";
import { isRunningGateway } from "./qortalRequests"; import { isRunningGateway } from "./qortalRequests";
import { QMailStatus } from "./components/QMailStatus"; import { QMailStatus } from "./components/QMailStatus";
import { GlobalActions } from "./components/GlobalActions/GlobalActions"; import { GlobalActions } from "./components/GlobalActions/GlobalActions";
import { useBlockedAddresses } from "./components/Group/useBlockUsers";
type extStates = type extStates =
| "not-authenticated" | "not-authenticated"
@ -402,6 +403,9 @@ function App() {
const [isOpenSendQort, setIsOpenSendQort] = useState(false); const [isOpenSendQort, setIsOpenSendQort] = useState(false);
const [isOpenSendQortSuccess, setIsOpenSendQortSuccess] = useState(false); const [isOpenSendQortSuccess, setIsOpenSendQortSuccess] = useState(false);
const [rootHeight, setRootHeight] = useState("100%"); const [rootHeight, setRootHeight] = useState("100%");
const {isUserBlocked,
addToBlockList,
removeBlockFromList, getAllBlockedUsers} = useBlockedAddresses()
const [currentNode, setCurrentNode] = useState({ const [currentNode, setCurrentNode] = useState({
url: "http://127.0.0.1:12391", url: "http://127.0.0.1:12391",
}); });
@ -1630,7 +1634,11 @@ function App() {
infoSnackCustom: infoSnack, infoSnackCustom: infoSnack,
setInfoSnackCustom: setInfoSnack, setInfoSnackCustom: setInfoSnack,
downloadResource, downloadResource,
getIndividualUserInfo getIndividualUserInfo,
isUserBlocked,
addToBlockList,
removeBlockFromList,
getAllBlockedUsers
}} }}
> >
<TaskManager getUserInfo={getUserInfo} /> <TaskManager getUserInfo={getUserInfo} />
@ -1751,7 +1759,11 @@ function App() {
infoSnackCustom: infoSnack, infoSnackCustom: infoSnack,
setInfoSnackCustom: setInfoSnack, setInfoSnackCustom: setInfoSnack,
downloadResource, downloadResource,
getIndividualUserInfo getIndividualUserInfo,
isUserBlocked,
addToBlockList,
removeBlockFromList,
getAllBlockedUsers
}} }}
> >
<Box <Box

View File

@ -12,6 +12,7 @@ import {
checkNewMessages, checkNewMessages,
checkThreads, checkThreads,
clearAllNotifications, clearAllNotifications,
createEndpoint,
createGroup, createGroup,
decryptDirectFunc, decryptDirectFunc,
decryptSingleForPublishes, decryptSingleForPublishes,
@ -1284,6 +1285,85 @@ export async function getTimestampEnterChatCase(request, event) {
} }
} }
export async function listActionsCase(request, event) {
try {
const { type, listName = '', items = [] } = request.payload;
let responseData
if(type === 'get'){
const url = await createEndpoint(`/lists/${listName}`);
const response = await fetch(url);
if (!response.ok) throw new Error("Failed to fetch");
responseData = await response.json();
} else if(type === 'remove'){
const url = await createEndpoint(`/lists/${listName}`);
const body = {
items: items ,
};
const bodyToString = JSON.stringify(body);
const response = await fetch(url, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: bodyToString,
});
if (!response.ok) throw new Error("Failed to remove from list");
let res;
try {
res = await response.clone().json();
} catch (e) {
res = await response.text();
}
responseData = res;
} else if(type === 'add'){
const url = await createEndpoint(`/lists/${listName}`);
const body = {
items: items ,
};
const bodyToString = JSON.stringify(body);
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: bodyToString,
});
if (!response.ok) throw new Error("Failed to add to list");
let res;
try {
res = await response.clone().json();
} catch (e) {
res = await response.text();
}
responseData = res;
}
event.source.postMessage(
{
requestId: request.requestId,
action: "listActions",
payload: responseData,
type: "backgroundMessageResponse",
},
event.origin
);
} catch (error) {
event.source.postMessage(
{
requestId: request.requestId,
action: "listActions",
error: error?.message,
type: "backgroundMessageResponse",
},
event.origin
);
}
}
export async function getTimestampMentionCase(request, event) { export async function getTimestampMentionCase(request, event) {
try { try {
const response = await getTimestampMention(); const response = await getTimestampMention();

View File

@ -74,6 +74,7 @@ import {
joinGroupCase, joinGroupCase,
kickFromGroupCase, kickFromGroupCase,
leaveGroupCase, leaveGroupCase,
listActionsCase,
ltcBalanceCase, ltcBalanceCase,
makeAdminCase, makeAdminCase,
nameCase, nameCase,
@ -3022,6 +3023,9 @@ function setupMessageListener() {
case "getTimestampEnterChat": case "getTimestampEnterChat":
getTimestampEnterChatCase(request, event); getTimestampEnterChatCase(request, event);
break; break;
case "listActions":
listActionsCase(request, event);
break;
case "addTimestampMention": case "addTimestampMention":
addTimestampMentionCase(request, event); addTimestampMentionCase(request, event);
break; break;
@ -3154,9 +3158,16 @@ const checkGroupList = async () => {
}, },
}); });
const data = await response.json(); const data = await response.json();
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 filteredGroups =
data.groups?.filter((item) => item?.groupId !== 0) || [];
const sortedGroups = filteredGroups.sort( const sortedGroups = filteredGroups.sort(
(a, b) => (b.timestamp || 0) - (a.timestamp || 0) (a, b) => (b.timestamp || 0) - (a.timestamp || 0)
); );

View File

@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react' import React, { useCallback, useContext, useEffect, useMemo, useReducer, useRef, useState } from 'react'
import { CreateCommonSecret } from './CreateCommonSecret' import { CreateCommonSecret } from './CreateCommonSecret'
import { reusableGet } from '../../qdn/publish/pubish' import { reusableGet } from '../../qdn/publish/pubish'
import { uint8ArrayToObject } from '../../backgroundFunctions/encryption' import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'
@ -10,11 +10,11 @@ import Tiptap from './TipTap'
import { CustomButton } from '../../App-styles' import { CustomButton } from '../../App-styles'
import CircularProgress from '@mui/material/CircularProgress'; import CircularProgress from '@mui/material/CircularProgress';
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar' import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'
import { getBaseApiReact, getBaseApiReactSocket, isMobile, pauseAllQueues, resumeAllQueues } from '../../App' import { getBaseApiReact, getBaseApiReactSocket, isMobile, MyContext, pauseAllQueues, resumeAllQueues } from '../../App'
import { CustomizedSnackbars } from '../Snackbar/Snackbar' import { CustomizedSnackbars } from '../Snackbar/Snackbar'
import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from '../../constants/codes' import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from '../../constants/codes'
import { useMessageQueue } from '../../MessageQueueContext' import { useMessageQueue } from '../../MessageQueueContext'
import { executeEvent } from '../../utils/events' import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'
import { Box, ButtonBase, Divider, Typography } from '@mui/material' import { Box, ButtonBase, Divider, Typography } from '@mui/material'
import ShortUniqueId from "short-unique-id"; import ShortUniqueId from "short-unique-id";
import { ReplyPreview } from './MessageItem' import { ReplyPreview } from './MessageItem'
@ -28,6 +28,7 @@ import { throttle } from 'lodash'
const uid = new ShortUniqueId({ length: 5 }); const uid = new ShortUniqueId({ length: 5 });
export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, myAddress, handleNewEncryptionNotification, hide, handleSecretKeyCreationInProgress, triedToFetchSecretKey, myName, balance, getTimestampEnterChatParent, hideView, isPrivate}) => { export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, myAddress, handleNewEncryptionNotification, hide, handleSecretKeyCreationInProgress, triedToFetchSecretKey, myName, balance, getTimestampEnterChatParent, hideView, isPrivate}) => {
const {isUserBlocked} = useContext(MyContext)
const [messages, setMessages] = useState([]) const [messages, setMessages] = useState([])
const [chatReferences, setChatReferences] = useState({}) const [chatReferences, setChatReferences] = useState({})
const [isSending, setIsSending] = useState(false) const [isSending, setIsSending] = useState(false)
@ -158,10 +159,28 @@ const [messageSize, setMessageSize] = useState(0)
}) })
} }
const updateChatMessagesWithBlocksFunc = (e) => {
if(e.detail){
setMessages((prev)=> prev?.filter((item)=> {
return !isUserBlocked(item?.sender, item?.senderName)
}))
}
};
useEffect(() => {
subscribeToEvent("updateChatMessagesWithBlocks", updateChatMessagesWithBlocksFunc);
return () => {
unsubscribeFromEvent("updateChatMessagesWithBlocks", updateChatMessagesWithBlocksFunc);
};
}, []);
const middletierFunc = async (data: any, groupId: string) => { const middletierFunc = async (data: any, groupId: string) => {
try { try {
if (hasInitialized.current) { if (hasInitialized.current) {
decryptMessages(data, true); const dataRemovedBlock = data?.filter((item)=> !isUserBlocked(item?.sender, item?.senderName))
decryptMessages(dataRemovedBlock, true);
return; return;
} }
hasInitialized.current = true; hasInitialized.current = true;
@ -173,7 +192,11 @@ const [messageSize, setMessageSize] = useState(0)
}, },
}); });
const responseData = await response.json(); const responseData = await response.json();
decryptMessages(responseData, false); const dataRemovedBlock = responseData?.filter((item)=> {
return !isUserBlocked(item?.sender, item?.senderName)
})
decryptMessages(dataRemovedBlock, false);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }

View File

@ -132,13 +132,16 @@ const onSeenFunc = useCallback(()=> {
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>
)} )}
<MessageWragger lastMessage={lastSignature === message?.signature} isLast={isLast} onSeen={onSeenFunc}>
<div <div
style={{ style={{
padding: "10px", padding: "10px",
@ -492,6 +495,7 @@ const onSeenFunc = useCallback(()=> {
</div> </div>
</MessageWragger> </MessageWragger>
</>
); );
}); });

View File

@ -0,0 +1,190 @@
import {
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
TextField,
Typography,
} from "@mui/material";
import React, { useContext, useEffect, useState } from "react";
import { MyContext } from "../../App";
import { Spacer } from "../../common/Spacer";
import { executeEvent } from "../../utils/events";
export const BlockedUsersModal = ({ close }) => {
const [hasChanged, setHasChanged] = useState(false);
const [value, setValue] = useState("");
const { getAllBlockedUsers, removeBlockFromList, addToBlockList } = useContext(MyContext);
const [blockedUsers, setBlockedUsers] = useState({
addresses: {},
names: {},
});
const fetchBlockedUsers = () => {
setBlockedUsers(getAllBlockedUsers());
};
useEffect(() => {
fetchBlockedUsers();
}, []);
return (
<Dialog
open={true}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle>Blocked Users</DialogTitle>
<DialogContent sx={{
padding: '20px'
}}>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "10px",
}}
>
<TextField
placeholder="Name"
value={value}
onChange={(e) => {
setValue(e.target.value);
}}
/>
<Button variant="contained" onClick={async ()=> {
try {
if(!value) return
await addToBlockList(undefined, value)
fetchBlockedUsers()
setHasChanged(true)
} catch (error) {
console.error(error)
}
}}>Block</Button>
</Box>
{Object.entries(blockedUsers?.addresses).length > 0 && (
<>
<Spacer height="20px" />
<DialogContentText id="alert-dialog-description">
Blocked Users for Chat ( addresses )
</DialogContentText>
<Spacer height="10px" />
</>
)}
<Box sx={{
display: 'flex',
flexDirection: 'column',
gap: '10px'
}}>
{Object.entries(blockedUsers?.addresses || {})?.map(
([key, value]) => {
return (
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "10px",
width: '100%',
justifyContent: 'space-between'
}}
>
<Typography>{key}</Typography>
<Button
variant="contained"
onClick={async () => {
try {
await removeBlockFromList(key, undefined);
setHasChanged(true);
setValue('')
fetchBlockedUsers();
} catch (error) {
console.error(error);
}
}}
>
Unblock
</Button>
</Box>
);
}
)}
</Box>
{Object.entries(blockedUsers?.names).length > 0 && (
<>
<Spacer height="20px" />
<DialogContentText id="alert-dialog-description">
Blocked Users for QDN and Chat (names)
</DialogContentText>
<Spacer height="10px" />
</>
)}
<Box sx={{
display: 'flex',
flexDirection: 'column',
gap: '10px'
}}>
{Object.entries(blockedUsers?.names || {})?.map(([key, value]) => {
return (
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "10px",
width: '100%',
justifyContent: 'space-between'
}}
>
<Typography>{key}</Typography>
<Button
variant="contained"
onClick={async () => {
try {
await removeBlockFromList(undefined, key);
setHasChanged(true);
fetchBlockedUsers();
} catch (error) {
console.error(error);
}
}}
>
Unblock
</Button>
</Box>
);
})}
</Box>
</DialogContent>
<DialogActions>
<Button
sx={{
backgroundColor: "var(--green)",
color: "black",
fontWeight: "bold",
opacity: 0.7,
"&:hover": {
backgroundColor: "var(--green)",
color: "black",
opacity: 1,
},
}}
variant="contained"
onClick={()=> {
if(hasChanged){
executeEvent('updateChatMessagesWithBlocks', true)
}
close()
}}
>
close
</Button>
</DialogActions>
</Dialog>
);
};

View File

@ -77,9 +77,10 @@ import { AdminSpace } from "../Chat/AdminSpace";
import { useSetRecoilState } from "recoil"; import { useSetRecoilState } from "recoil";
import { addressInfoControllerAtom, selectedGroupIdAtom } from "../../atoms/global"; import { addressInfoControllerAtom, selectedGroupIdAtom } from "../../atoms/global";
import { sortArrayByTimestampAndGroupName } from "../../utils/time"; import { sortArrayByTimestampAndGroupName } from "../../utils/time";
import BlockIcon from '@mui/icons-material/Block';
import LockIcon from '@mui/icons-material/Lock'; import LockIcon from '@mui/icons-material/Lock';
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
import { BlockedUsersModal } from "./BlockedUsersModal";
export const getPublishesFromAdmins = async (admins: string[], groupId) => { export const getPublishesFromAdmins = async (admins: string[], groupId) => {
@ -419,6 +420,8 @@ export const Group = ({
const [groupAnnouncements, setGroupAnnouncements] = React.useState({}); const [groupAnnouncements, setGroupAnnouncements] = React.useState({});
const [defaultThread, setDefaultThread] = React.useState(null); const [defaultThread, setDefaultThread] = React.useState(null);
const [isOpenDrawer, setIsOpenDrawer] = React.useState(false); const [isOpenDrawer, setIsOpenDrawer] = React.useState(false);
const [isOpenBlockedUserModal, setIsOpenBlockedUserModal] = React.useState(false);
const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false); const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false);
const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState(""); const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState("");
const [drawerMode, setDrawerMode] = React.useState("groups"); const [drawerMode, setDrawerMode] = React.useState("groups");
@ -768,7 +771,10 @@ export const Group = ({
} }
if(isPrivate === false){ if(isPrivate === false){
setTriedToFetchSecretKey(true); setTriedToFetchSecretKey(true);
getAdminsForPublic(selectedGroup) if(selectedGroup?.groupId !== '0'){
getAdminsForPublic(selectedGroup)
}
} }
}, [selectedGroup, isPrivate]); }, [selectedGroup, isPrivate]);
@ -853,7 +859,7 @@ export const Group = ({
// Update the component state with the received 'sendqort' state // Update the component state with the received 'sendqort' state
setGroups(sortArrayByTimestampAndGroupName(message.payload)); setGroups(sortArrayByTimestampAndGroupName(message.payload));
getLatestRegularChat(message.payload); getLatestRegularChat(message.payload);
setMemberGroups(message.payload); setMemberGroups(message.payload?.filter((item)=> item?.groupId !== '0'));
if (selectedGroupRef.current && groupSectionRef.current === "chat") { if (selectedGroupRef.current && groupSectionRef.current === "chat") {
window.sendMessage("addTimestampEnterChat", { window.sendMessage("addTimestampEnterChat", {
@ -944,7 +950,7 @@ export const Group = ({
!initiatedGetMembers.current && !initiatedGetMembers.current &&
selectedGroup?.groupId && selectedGroup?.groupId &&
secretKey && secretKey &&
admins.includes(myAddress) admins.includes(myAddress) && selectedGroup?.groupId !== '0'
) { ) {
// getAdmins(selectedGroup?.groupId); // getAdmins(selectedGroup?.groupId);
getMembers(selectedGroup?.groupId); getMembers(selectedGroup?.groupId);
@ -1998,9 +2004,11 @@ export const Group = ({
width: "100%", width: "100%",
justifyContent: "center", justifyContent: "center",
padding: "10px", padding: "10px",
gap: '10px'
}} }}
> >
{chatMode === "groups" && ( {chatMode === "groups" && (
<>
<CustomButton <CustomButton
onClick={() => { onClick={() => {
setOpenAddGroup(true); setOpenAddGroup(true);
@ -2013,6 +2021,22 @@ export const Group = ({
/> />
Group Mgmt Group Mgmt
</CustomButton> </CustomButton>
<CustomButton
onClick={() => {
setIsOpenBlockedUserModal(true);
}}
sx={{
minWidth: 'unset',
padding: '10px'
}}
>
<BlockIcon
sx={{
color: "white",
}}
/>
</CustomButton>
</>
)} )}
{chatMode === "directs" && ( {chatMode === "directs" && (
<CustomButton <CustomButton
@ -2438,7 +2462,11 @@ export const Group = ({
/> />
)} )}
</div> </div>
{isOpenBlockedUserModal && (
<BlockedUsersModal close={()=> {
setIsOpenBlockedUserModal(false)
}} />
)}
{selectedDirect && !newChat && ( {selectedDirect && !newChat && (
<> <>

View File

@ -91,7 +91,7 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
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'
).sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0)); ).sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
window.sendMessage("handleActiveGroupDataFromSocket", { window.sendMessage("handleActiveGroupDataFromSocket", {
groups: sortedGroups, groups: sortedGroups,
directs: sortedDirects, directs: sortedDirects,

View File

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

View File

@ -1,6 +1,7 @@
import React, { useState } from 'react'; import React, { useCallback, useContext, useEffect, useState } from 'react';
import { Popover, Button, Box } from '@mui/material'; import { Popover, Button, Box, CircularProgress } from '@mui/material';
import { executeEvent } from '../utils/events'; import { executeEvent } from '../utils/events';
import { MyContext } from '../App';
export const WrapperUserAction = ({ children, address, name, disabled }) => { export const WrapperUserAction = ({ children, address, name, disabled }) => {
const [anchorEl, setAnchorEl] = useState(null); const [anchorEl, setAnchorEl] = useState(null);
@ -12,9 +13,9 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
}; };
// Handle closing the Popover // Handle closing the Popover
const handleClose = () => { const handleClose = useCallback(() => {
setAnchorEl(null); setAnchorEl(null);
}; }, []);
// Determine if the popover is open // Determine if the popover is open
const open = Boolean(anchorEl); const open = Boolean(anchorEl);
@ -120,9 +121,63 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
> >
Copy address Copy address
</Button> </Button>
<BlockUser handleClose={handleClose} address={address} name={name} />
</Box> </Box>
</Popover> </Popover>
)} )}
</> </>
); );
}; };
const BlockUser = ({address, name, handleClose})=> {
const [isAlreadyBlocked, setIsAlreadyBlocked] = useState(null)
const [isLoading, setIsLoading] = useState(false)
const {isUserBlocked,
addToBlockList,
removeBlockFromList} = useContext(MyContext)
useEffect(()=> {
if(!address) return
setIsAlreadyBlocked(isUserBlocked(address, name))
}, [address, setIsAlreadyBlocked, isUserBlocked, name])
return (
<Button
variant="text"
onClick={async () => {
try {
setIsLoading(true)
if(isAlreadyBlocked === true){
await removeBlockFromList(address, name)
} else if(isAlreadyBlocked === false) {
await addToBlockList(address, name)
}
executeEvent('updateChatMessagesWithBlocks', true)
} catch (error) {
console.error(error)
} finally {
setIsLoading(false)
handleClose();
}
}}
sx={{
color: 'white',
justifyContent: 'flex-start',
gap: '10px'
}}
>
{(isAlreadyBlocked === null || isLoading) && (
<CircularProgress color="secondary" size={24} />
)}
{isAlreadyBlocked && (
'Unblock name'
)}
{isAlreadyBlocked === false && (
'Block name'
)}
</Button>
)
}