diff --git a/src/App.tsx b/src/App.tsx
index c6dbd42..3453234 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -143,6 +143,7 @@ import { Minting } from "./components/Minting/Minting";
import { isRunningGateway } from "./qortalRequests";
import { QMailStatus } from "./components/QMailStatus";
import { GlobalActions } from "./components/GlobalActions/GlobalActions";
+import { useBlockedAddresses } from "./components/Group/useBlockUsers";
type extStates =
| "not-authenticated"
@@ -402,6 +403,9 @@ function App() {
const [isOpenSendQort, setIsOpenSendQort] = useState(false);
const [isOpenSendQortSuccess, setIsOpenSendQortSuccess] = useState(false);
const [rootHeight, setRootHeight] = useState("100%");
+ const {isUserBlocked,
+ addToBlockList,
+ removeBlockFromList, getAllBlockedUsers} = useBlockedAddresses()
const [currentNode, setCurrentNode] = useState({
url: "http://127.0.0.1:12391",
});
@@ -1630,7 +1634,11 @@ function App() {
infoSnackCustom: infoSnack,
setInfoSnackCustom: setInfoSnack,
downloadResource,
- getIndividualUserInfo
+ getIndividualUserInfo,
+ isUserBlocked,
+ addToBlockList,
+ removeBlockFromList,
+ getAllBlockedUsers
}}
>
@@ -1751,7 +1759,11 @@ function App() {
infoSnackCustom: infoSnack,
setInfoSnackCustom: setInfoSnack,
downloadResource,
- getIndividualUserInfo
+ getIndividualUserInfo,
+ isUserBlocked,
+ addToBlockList,
+ removeBlockFromList,
+ getAllBlockedUsers
}}
>
{
},
});
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(
(a, b) => (b.timestamp || 0) - (a.timestamp || 0)
);
diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx
index c41265d..e054bd9 100644
--- a/src/components/Chat/ChatGroup.tsx
+++ b/src/components/Chat/ChatGroup.tsx
@@ -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 { reusableGet } from '../../qdn/publish/pubish'
import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'
@@ -10,11 +10,11 @@ import Tiptap from './TipTap'
import { CustomButton } from '../../App-styles'
import CircularProgress from '@mui/material/CircularProgress';
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 { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from '../../constants/codes'
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 ShortUniqueId from "short-unique-id";
import { ReplyPreview } from './MessageItem'
@@ -28,6 +28,7 @@ import { throttle } from 'lodash'
const uid = new ShortUniqueId({ length: 5 });
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 [chatReferences, setChatReferences] = useState({})
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) => {
try {
if (hasInitialized.current) {
- decryptMessages(data, true);
+ const dataRemovedBlock = data?.filter((item)=> !isUserBlocked(item?.sender, item?.senderName))
+
+ decryptMessages(dataRemovedBlock, true);
return;
}
hasInitialized.current = true;
@@ -173,7 +192,11 @@ const [messageSize, setMessageSize] = useState(0)
},
});
const responseData = await response.json();
- decryptMessages(responseData, false);
+ const dataRemovedBlock = responseData?.filter((item)=> {
+ return !isUserBlocked(item?.sender, item?.senderName)
+ })
+
+ decryptMessages(dataRemovedBlock, false);
} catch (error) {
console.error(error);
}
diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx
index 8b43e77..5123ee2 100644
--- a/src/components/Chat/MessageItem.tsx
+++ b/src/components/Chat/MessageItem.tsx
@@ -132,13 +132,16 @@ const onSeenFunc = useCallback(()=> {
return (
-
-
- {message?.divide && (
+ <>
+ {message?.divide && (
Unread messages below
)}
+
+
+
+
{
+ >
);
});
diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx
new file mode 100644
index 0000000..84fa3fa
--- /dev/null
+++ b/src/components/Group/BlockedUsersModal.tsx
@@ -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 (
+
+ );
+};
diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx
index 73d80ba..48ef02c 100644
--- a/src/components/Group/Group.tsx
+++ b/src/components/Group/Group.tsx
@@ -77,9 +77,10 @@ import { AdminSpace } from "../Chat/AdminSpace";
import { useSetRecoilState } from "recoil";
import { addressInfoControllerAtom, selectedGroupIdAtom } from "../../atoms/global";
import { sortArrayByTimestampAndGroupName } from "../../utils/time";
-
+import BlockIcon from '@mui/icons-material/Block';
import LockIcon from '@mui/icons-material/Lock';
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
+import { BlockedUsersModal } from "./BlockedUsersModal";
export const getPublishesFromAdmins = async (admins: string[], groupId) => {
@@ -419,6 +420,8 @@ export const Group = ({
const [groupAnnouncements, setGroupAnnouncements] = React.useState({});
const [defaultThread, setDefaultThread] = React.useState(null);
const [isOpenDrawer, setIsOpenDrawer] = React.useState(false);
+ const [isOpenBlockedUserModal, setIsOpenBlockedUserModal] = React.useState(false);
+
const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false);
const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState("");
const [drawerMode, setDrawerMode] = React.useState("groups");
@@ -768,7 +771,10 @@ export const Group = ({
}
if(isPrivate === false){
setTriedToFetchSecretKey(true);
- getAdminsForPublic(selectedGroup)
+ if(selectedGroup?.groupId !== '0'){
+ getAdminsForPublic(selectedGroup)
+ }
+
}
}, [selectedGroup, isPrivate]);
@@ -853,7 +859,7 @@ export const Group = ({
// Update the component state with the received 'sendqort' state
setGroups(sortArrayByTimestampAndGroupName(message.payload));
getLatestRegularChat(message.payload);
- setMemberGroups(message.payload);
+ setMemberGroups(message.payload?.filter((item)=> item?.groupId !== '0'));
if (selectedGroupRef.current && groupSectionRef.current === "chat") {
window.sendMessage("addTimestampEnterChat", {
@@ -944,7 +950,7 @@ export const Group = ({
!initiatedGetMembers.current &&
selectedGroup?.groupId &&
secretKey &&
- admins.includes(myAddress)
+ admins.includes(myAddress) && selectedGroup?.groupId !== '0'
) {
// getAdmins(selectedGroup?.groupId);
getMembers(selectedGroup?.groupId);
@@ -1998,9 +2004,11 @@ export const Group = ({
width: "100%",
justifyContent: "center",
padding: "10px",
+ gap: '10px'
}}
>
{chatMode === "groups" && (
+ <>
{
setOpenAddGroup(true);
@@ -2013,6 +2021,22 @@ export const Group = ({
/>
Group Mgmt
+ {
+ setIsOpenBlockedUserModal(true);
+ }}
+ sx={{
+ minWidth: 'unset',
+ padding: '10px'
+ }}
+ >
+
+
+ >
)}
{chatMode === "directs" && (
)}
-
+ {isOpenBlockedUserModal && (
+ {
+ setIsOpenBlockedUserModal(false)
+ }} />
+ )}
{selectedDirect && !newChat && (
<>
diff --git a/src/components/Group/WebsocketActive.tsx b/src/components/Group/WebsocketActive.tsx
index 63359a4..b9da4d2 100644
--- a/src/components/Group/WebsocketActive.tsx
+++ b/src/components/Group/WebsocketActive.tsx
@@ -91,7 +91,7 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => {
const sortedDirects = (data?.direct || []).filter(item =>
item?.name !== 'extension-proxy' && item?.address !== 'QSMMGSgysEuqDCuLw3S4cHrQkBrh3vP3VH'
).sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
-
+
window.sendMessage("handleActiveGroupDataFromSocket", {
groups: sortedGroups,
directs: sortedDirects,
diff --git a/src/components/Group/useBlockUsers.tsx b/src/components/Group/useBlockUsers.tsx
new file mode 100644
index 0000000..05cbe90
--- /dev/null
+++ b/src/components/Group/useBlockUsers.tsx
@@ -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
+ };
+};
diff --git a/src/components/WrapperUserAction.tsx b/src/components/WrapperUserAction.tsx
index f7af63e..8bcc03a 100644
--- a/src/components/WrapperUserAction.tsx
+++ b/src/components/WrapperUserAction.tsx
@@ -1,6 +1,7 @@
-import React, { useState } from 'react';
-import { Popover, Button, Box } from '@mui/material';
+import React, { useCallback, useContext, useEffect, useState } from 'react';
+import { Popover, Button, Box, CircularProgress } from '@mui/material';
import { executeEvent } from '../utils/events';
+import { MyContext } from '../App';
export const WrapperUserAction = ({ children, address, name, disabled }) => {
const [anchorEl, setAnchorEl] = useState(null);
@@ -12,9 +13,9 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
};
// Handle closing the Popover
- const handleClose = () => {
+ const handleClose = useCallback(() => {
setAnchorEl(null);
- };
+ }, []);
// Determine if the popover is open
const open = Boolean(anchorEl);
@@ -120,9 +121,63 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
>
Copy address
+
)}
>
);
};
+
+
+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 (
+
+ )
+}
\ No newline at end of file