From 534301294e4de307637981154718b15ce08d191c Mon Sep 17 00:00:00 2001 From: PhilReact Date: Fri, 11 Apr 2025 15:56:46 +0300 Subject: [PATCH 1/3] give more info on block users --- src/atoms/global.ts | 5 + src/components/Group/BlockedUsersModal.tsx | 284 ++++++++++++++++----- src/components/Group/Group.tsx | 15 +- src/components/Group/useBlockUsers.tsx | 160 +++++++----- src/components/WrapperUserAction.tsx | 15 +- 5 files changed, 338 insertions(+), 141 deletions(-) diff --git a/src/atoms/global.ts b/src/atoms/global.ts index f05da75..443d833 100644 --- a/src/atoms/global.ts +++ b/src/atoms/global.ts @@ -177,4 +177,9 @@ export const mailsAtom = atom({ export const groupsPropertiesAtom = atom({ key: 'groupsPropertiesAtom', default: {}, +}); + +export const isOpenBlockedModalAtom = atom({ + key: 'isOpenBlockedModalAtom', + default: false, }); \ No newline at end of file diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index 84fa3fa..e81d207 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -10,15 +10,23 @@ import { Typography, } from "@mui/material"; import React, { useContext, useEffect, useState } from "react"; -import { MyContext } from "../../App"; +import { getBaseApiReact, MyContext } from "../../App"; import { Spacer } from "../../common/Spacer"; -import { executeEvent } from "../../utils/events"; - -export const BlockedUsersModal = ({ close }) => { +import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "../../utils/events"; +import { validateAddress } from "../../utils/validateAddress"; +import { getNameInfo, requestQueueMemberNames } from "./Group"; +import { useModal } from "../../common/useModal"; +import { useRecoilState } from "recoil"; +import { isOpenBlockedModalAtom } from "../../atoms/global"; +import InfoIcon from '@mui/icons-material/Info'; +export const BlockedUsersModal = () => { + const [isOpenBlockedModal, setIsOpenBlockedModal] = useRecoilState(isOpenBlockedModalAtom) const [hasChanged, setHasChanged] = useState(false); const [value, setValue] = useState(""); - - const { getAllBlockedUsers, removeBlockFromList, addToBlockList } = useContext(MyContext); + const [addressesWithNames, setAddressesWithNames] = useState({}) + const { isShow, onCancel, onOk, show, message } = useModal(); + const { getAllBlockedUsers, removeBlockFromList, addToBlockList } = + useContext(MyContext); const [blockedUsers, setBlockedUsers] = useState({ addresses: {}, names: {}, @@ -28,60 +36,157 @@ export const BlockedUsersModal = ({ close }) => { }; useEffect(() => { + if(!isOpenBlockedModal) return fetchBlockedUsers(); - }, []); + }, [isOpenBlockedModal]); + + const getNames = async () => { + // const validApi = await findUsableApi(); + const addresses = Object.keys(blockedUsers?.addresses) + const addressNames = {} + + + const getMemNames = addresses.map(async (address) => { + const name = await requestQueueMemberNames.enqueue(() => { + return getNameInfo(address); + }); + if (name) { + addressNames[address] = name + } + + + return true; + }); + + await Promise.all(getMemNames); + + setAddressesWithNames(addressNames) + }; + + const blockUser = async (e, user?: string) => { + try { + const valUser = user || value + if (!valUser) return; + const isAddress = validateAddress(valUser); + let userName = null; + let userAddress = null; + if (isAddress) { + userAddress = valUser; + const name = await getNameInfo(valUser); + if (name) { + userName = name; + } + } + if (!isAddress) { + const response = await fetch(`${getBaseApiReact()}/names/${valUser}`); + const data = await response.json(); + if (!data?.owner) throw new Error("Name does not exist"); + if (data?.owner) { + userAddress = data.owner; + userName = valUser; + } + } + if(!userName){ + await addToBlockList(userAddress, null); + fetchBlockedUsers(); + setHasChanged(true); + executeEvent('updateChatMessagesWithBlocks', true) + setValue('') + return + } + const responseModal = await show({ + userName, + userAddress, + }); + if (responseModal === "both") { + await addToBlockList(userAddress, userName); + } else if (responseModal === "address") { + await addToBlockList(userAddress, null); + } else if (responseModal === "name") { + await addToBlockList(null, userName); + } + fetchBlockedUsers(); + setHasChanged(true); + setValue('') + if(user){ + setIsOpenBlockedModal(false) + } + if(responseModal === 'both' || responseModal === 'address'){ + executeEvent('updateChatMessagesWithBlocks', true) + } + } catch (error) { + console.error(error); + } + }; + const blockUserFromOutsideModalFunc = (e) => { + const user = e.detail?.user; + setIsOpenBlockedModal(true) + blockUser(null, user) + }; + + useEffect(() => { + subscribeToEvent("blockUserFromOutside", blockUserFromOutsideModalFunc); + + return () => { + unsubscribeFromEvent("blockUserFromOutside", blockUserFromOutsideModalFunc); + }; + }, []); return ( - Blocked Users - - Blocked Users + - { - setValue(e.target.value); + > + - - - + > + { + setValue(e.target.value); + }} + /> + + + {Object.entries(blockedUsers?.addresses).length > 0 && ( <> - Blocked Users for Chat ( addresses ) + Blocked addresses- blocks processing of txs + + )} - + {Object.entries(blockedUsers?.addresses || {})?.map( ([key, value]) => { return ( @@ -90,18 +195,22 @@ export const BlockedUsersModal = ({ close }) => { display: "flex", alignItems: "center", gap: "10px", - width: '100%', - justifyContent: 'space-between' + width: "100%", + justifyContent: "space-between", }} > - {key} + {addressesWithNames[key] || key} + + + + {"Decide what to block"} + + + + Blocking {message?.userName || message?.userAddress} + + + Choose "block txs" or "all" to block chat messages + + + + + + + + ); }; diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 64dc541..9f95a3a 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -75,9 +75,9 @@ import { MessagingIcon } from "../../assets/Icons/MessagingIcon"; import { formatEmailDate } from "./QMailMessages"; import { AdminSpace } from "../Chat/AdminSpace"; import { useRecoilState, useSetRecoilState } from "recoil"; -import { addressInfoControllerAtom, groupsPropertiesAtom, selectedGroupIdAtom } from "../../atoms/global"; +import { addressInfoControllerAtom, groupsPropertiesAtom, isOpenBlockedModalAtom, selectedGroupIdAtom } from "../../atoms/global"; import { sortArrayByTimestampAndGroupName } from "../../utils/time"; -import BlockIcon from '@mui/icons-material/Block'; +import PersonOffIcon from '@mui/icons-material/PersonOff'; import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import { BlockedUsersModal } from "./BlockedUsersModal"; @@ -421,7 +421,7 @@ 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 setIsOpenBlockedUserModal = useSetRecoilState(isOpenBlockedModalAtom) const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false); const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState(""); @@ -2035,7 +2035,7 @@ export const Group = ({ padding: '10px' }} > - )} - {isOpenBlockedUserModal && ( - { - setIsOpenBlockedUserModal(false) - }} /> - )} + + {selectedDirect && !newChat && ( <> diff --git a/src/components/Group/useBlockUsers.tsx b/src/components/Group/useBlockUsers.tsx index 05cbe90..eeb5361 100644 --- a/src/components/Group/useBlockUsers.tsx +++ b/src/components/Group/useBlockUsers.tsx @@ -19,7 +19,7 @@ export const useBlockedAddresses = () => { const isUserBlocked = useCallback((address, name)=> { try { if(!address) return false - if(userBlockedRef.current[address] || userNamesBlockedRef.current[name]) return true + if(userBlockedRef.current[address]) return true return false @@ -90,43 +90,13 @@ export const useBlockedAddresses = () => { }, []) 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]){ + if(name){ await new Promise((res, rej) => { window.sendMessage("listActions", { type: 'remove', - items: !name ? [name] : [address], - listName: !name ? 'blockedNames' : 'blockedAddresses' + items: [name] , + listName: 'blockedNames' }) .then((response) => { @@ -134,9 +104,12 @@ export const useBlockedAddresses = () => { rej(response?.message); return; } else { - const copyObject = {...userBlockedRef.current} - delete copyObject[address] - userBlockedRef.current = copyObject + + const copyObject = {...userNamesBlockedRef.current} + delete copyObject[name] + userNamesBlockedRef.current = copyObject + + res(response); } }) @@ -145,42 +118,95 @@ export const useBlockedAddresses = () => { }); }) } + + if(address){ + await new Promise((res, rej) => { + window.sendMessage("listActions", { + + type: 'remove', + items: [address], + listName: '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 - - } + if(name){ + await new Promise((res, rej) => { + window.sendMessage("listActions", { - res(response); - } + type: 'add', + items: [name], + listName: 'blockedNames' + + }) + .then((response) => { + if (response.error) { + rej(response?.message); + return; + } else { + const copyObject = {...userNamesBlockedRef.current} + copyObject[name] = true + userNamesBlockedRef.current = copyObject + + + res(response); + } + }) + .catch((error) => { + console.error("Failed qortalRequest", error); + }); }) - .catch((error) => { - console.error("Failed qortalRequest", error); - }); - }) + } + if(address){ + await new Promise((res, rej) => { + window.sendMessage("listActions", { + + type: 'add', + items: [address], + listName: 'blockedAddresses' + + }) + .then((response) => { + if (response.error) { + rej(response?.message); + return; + } else { + + const copyObject = {...userBlockedRef.current} + copyObject[address] = true + userBlockedRef.current = copyObject + + res(response); + } + }) + .catch((error) => { + console.error("Failed qortalRequest", error); + }); + }) + } + }, []) return { diff --git a/src/components/WrapperUserAction.tsx b/src/components/WrapperUserAction.tsx index a3bcc3f..c074c16 100644 --- a/src/components/WrapperUserAction.tsx +++ b/src/components/WrapperUserAction.tsx @@ -169,12 +169,15 @@ useEffect(()=> { onClick={async () => { try { setIsLoading(true) - if(isAlreadyBlocked === true){ - await removeBlockFromList(address, name) - } else if(isAlreadyBlocked === false) { - await addToBlockList(address, name) - } - executeEvent('updateChatMessagesWithBlocks', true) + executeEvent("blockUserFromOutside", { + user: address + }) + // if(isAlreadyBlocked === true){ + // await removeBlockFromList(address, name) + // } else if(isAlreadyBlocked === false) { + // await addToBlockList(address, name) + // } + // executeEvent('updateChatMessagesWithBlocks', true) } catch (error) { console.error(error) } finally { From 8f8b7fc0cdc4825705fdceb5c1581a95b1d086be Mon Sep 17 00:00:00 2001 From: PhilReact Date: Fri, 11 Apr 2025 20:19:03 +0300 Subject: [PATCH 2/3] error snack for blocked user --- src/components/Group/BlockedUsersModal.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index e81d207..24b3f01 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -25,7 +25,7 @@ export const BlockedUsersModal = () => { const [value, setValue] = useState(""); const [addressesWithNames, setAddressesWithNames] = useState({}) const { isShow, onCancel, onOk, show, message } = useModal(); - const { getAllBlockedUsers, removeBlockFromList, addToBlockList } = + const { getAllBlockedUsers, removeBlockFromList, addToBlockList, setOpenSnackGlobal, setInfoSnackCustom } = useContext(MyContext); const [blockedUsers, setBlockedUsers] = useState({ addresses: {}, @@ -115,7 +115,12 @@ export const BlockedUsersModal = () => { executeEvent('updateChatMessagesWithBlocks', true) } } catch (error) { - console.error(error); + setOpenSnackGlobal(true); + + setInfoSnackCustom({ + type: "error", + message: error?.message || "Unable to block user", + }); } }; const blockUserFromOutsideModalFunc = (e) => { From eb822cbcb470df9b861a4d63e0e5617308f597a1 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sun, 13 Apr 2025 18:10:09 +0300 Subject: [PATCH 3/3] added q-nodecontrol as default --- src/atoms/global.ts | 101 +++++++++++--------- src/components/Apps/AppsCategory.tsx | 3 +- src/components/Apps/AppsCategoryDesktop.tsx | 3 +- src/components/Apps/AppsLibrary.tsx | 25 ++--- src/components/Apps/AppsLibraryDesktop.tsx | 27 +++--- 5 files changed, 86 insertions(+), 73 deletions(-) diff --git a/src/atoms/global.ts b/src/atoms/global.ts index 443d833..7d0b3fc 100644 --- a/src/atoms/global.ts +++ b/src/atoms/global.ts @@ -2,52 +2,61 @@ import { atom, selectorFamily } from 'recoil'; export const sortablePinnedAppsAtom = atom({ - key: 'sortablePinnedAppsFromAtom', - default: [{ - name: 'Q-Tube', - service: 'APP' - }, { - name: 'Q-Mail', - service: 'APP' - }, { - name: 'Q-Share', - service: 'APP' - }, { - name: 'Q-Fund', - service: 'APP' - }, { - name: 'Q-Shop', - service: 'APP' - }, - { - name: 'Q-Trade', - service: 'APP' - }, - { - name: 'Q-Support', - service: 'APP' - }, - { - name: 'Q-Manager', - service: 'APP' - }, - { - name: 'Q-Blog', - service: 'APP' - }, - { - name: 'Q-Mintership', - service: 'APP' - }, - { - name: 'Q-Wallets', - service: 'APP' - }, - { - name: 'Q-Search', - service: 'APP' - }, -], + key: 'sortablePinnedAppsFromAtom', + default: [ + { + name: 'Q-Tube', + service: 'APP', + }, + { + name: 'Q-Mail', + service: 'APP', + }, + { + name: 'Q-Share', + service: 'APP', + }, + { + name: 'Q-Fund', + service: 'APP', + }, + { + name: 'Q-Shop', + service: 'APP', + }, + { + name: 'Q-Trade', + service: 'APP', + }, + { + name: 'Q-Support', + service: 'APP', + }, + { + name: 'Q-Manager', + service: 'APP', + }, + { + name: 'Q-Blog', + service: 'APP', + }, + { + name: 'Q-Mintership', + service: 'APP', + }, + { + name: 'Q-Wallets', + service: 'APP', + }, + { + name: 'Q-Search', + service: 'APP', + }, + { + name: 'Q-Nodecontrol', + service: 'APP' + } + ], }); export const canSaveSettingToQdnAtom = atom({ diff --git a/src/components/Apps/AppsCategory.tsx b/src/components/Apps/AppsCategory.tsx index c1fa0a0..e96391f 100644 --- a/src/components/Apps/AppsCategory.tsx +++ b/src/components/Apps/AppsCategory.tsx @@ -42,7 +42,8 @@ const officialAppList = [ "q-support", "q-manager", "q-wallets", - "q-search" + "q-search", + "q-nodecontrol" ]; const ScrollerStyled = styled('div')({ diff --git a/src/components/Apps/AppsCategoryDesktop.tsx b/src/components/Apps/AppsCategoryDesktop.tsx index 55cdc4f..ea08998 100644 --- a/src/components/Apps/AppsCategoryDesktop.tsx +++ b/src/components/Apps/AppsCategoryDesktop.tsx @@ -50,7 +50,8 @@ const officialAppList = [ "q-support", "q-manager", "q-wallets", - "q-search" + "q-search", + "q-nodecontrol" ]; const ScrollerStyled = styled("div")({ diff --git a/src/components/Apps/AppsLibrary.tsx b/src/components/Apps/AppsLibrary.tsx index 5601626..0902d9a 100644 --- a/src/components/Apps/AppsLibrary.tsx +++ b/src/components/Apps/AppsLibrary.tsx @@ -33,18 +33,19 @@ import { Virtuoso } from "react-virtuoso"; import { executeEvent } from "../../utils/events"; import { ComposeP, MailIconImg, ShowMessageReturnButton } from "../Group/Forum/Mail-styles"; const officialAppList = [ - "q-tube", - "q-blog", - "q-share", - "q-support", - "q-mail", - "q-fund", - "q-shop", - "q-trade", - "q-support", - "q-manager", - "q-wallets", - "q-search" + 'q-tube', + 'q-blog', + 'q-share', + 'q-support', + 'q-mail', + 'q-fund', + 'q-shop', + 'q-trade', + 'q-support', + 'q-manager', + 'q-wallets', + 'q-search', + "q-nodecontrol" ]; const ScrollerStyled = styled('div')({ diff --git a/src/components/Apps/AppsLibraryDesktop.tsx b/src/components/Apps/AppsLibraryDesktop.tsx index 851267b..a0a15e1 100644 --- a/src/components/Apps/AppsLibraryDesktop.tsx +++ b/src/components/Apps/AppsLibraryDesktop.tsx @@ -48,19 +48,20 @@ import { AppsNavBarDesktop } from "./AppsNavBarDesktop"; import ReturnSVG from '../../assets/svgs/Return.svg' import { ComposeP, MailIconImg, ShowMessageReturnButton } from "../Group/Forum/Mail-styles"; const officialAppList = [ - "q-tube", - "q-blog", - "q-share", - "q-support", - "q-mail", - "q-fund", - "q-shop", - "q-trade", - "q-support", - "q-manager", - "q-mintership", - "q-wallets", - "q-search" + 'q-tube', + 'q-blog', + 'q-share', + 'q-support', + 'q-mail', + 'q-fund', + 'q-shop', + 'q-trade', + 'q-support', + 'q-manager', + 'q-mintership', + 'q-wallets', + 'q-search', + "q-nodecontrol" ]; const ScrollerStyled = styled("div")({