mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-06-13 11:31:22 +00:00
Add theme
This commit is contained in:
parent
3a302cf5b2
commit
18ec6126b7
@ -8,64 +8,78 @@ import {
|
||||
DialogTitle,
|
||||
TextField,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { getBaseApiReact, MyContext } from "../../App";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
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";
|
||||
useTheme,
|
||||
} from '@mui/material';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { getBaseApiReact, MyContext } from '../../App';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
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 theme = useTheme();
|
||||
const [isOpenBlockedModal, setIsOpenBlockedModal] = useRecoilState(
|
||||
isOpenBlockedModalAtom
|
||||
);
|
||||
const [hasChanged, setHasChanged] = useState(false);
|
||||
const [value, setValue] = useState("");
|
||||
const [addressesWithNames, setAddressesWithNames] = useState({})
|
||||
const [value, setValue] = useState('');
|
||||
const [addressesWithNames, setAddressesWithNames] = useState({});
|
||||
const { isShow, onCancel, onOk, show, message } = useModal();
|
||||
const { getAllBlockedUsers, removeBlockFromList, addToBlockList, setOpenSnackGlobal, setInfoSnackCustom } =
|
||||
useContext(MyContext);
|
||||
const {
|
||||
getAllBlockedUsers,
|
||||
removeBlockFromList,
|
||||
addToBlockList,
|
||||
setOpenSnackGlobal,
|
||||
setInfoSnackCustom,
|
||||
} = useContext(MyContext);
|
||||
|
||||
const [blockedUsers, setBlockedUsers] = useState({
|
||||
addresses: {},
|
||||
names: {},
|
||||
});
|
||||
|
||||
const fetchBlockedUsers = () => {
|
||||
setBlockedUsers(getAllBlockedUsers());
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if(!isOpenBlockedModal) return
|
||||
if (!isOpenBlockedModal) return;
|
||||
fetchBlockedUsers();
|
||||
}, [isOpenBlockedModal]);
|
||||
|
||||
const getNames = async () => {
|
||||
const getNames = async () => {
|
||||
// const validApi = await findUsableApi();
|
||||
const addresses = Object.keys(blockedUsers?.addresses)
|
||||
const addressNames = {}
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
const name = await requestQueueMemberNames.enqueue(() => {
|
||||
return getNameInfo(address);
|
||||
});
|
||||
if (name) {
|
||||
addressNames[address] = name;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
await Promise.all(getMemNames);
|
||||
|
||||
setAddressesWithNames(addressNames)
|
||||
|
||||
setAddressesWithNames(addressNames);
|
||||
};
|
||||
|
||||
const blockUser = async (e, user?: string) => {
|
||||
try {
|
||||
const valUser = user || value
|
||||
const valUser = user || value;
|
||||
if (!valUser) return;
|
||||
const isAddress = validateAddress(valUser);
|
||||
let userName = null;
|
||||
@ -80,62 +94,66 @@ export const BlockedUsersModal = () => {
|
||||
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) throw new Error('Name does not exist');
|
||||
if (data?.owner) {
|
||||
userAddress = data.owner;
|
||||
userName = valUser;
|
||||
}
|
||||
}
|
||||
if(!userName){
|
||||
if (!userName) {
|
||||
await addToBlockList(userAddress, null);
|
||||
fetchBlockedUsers();
|
||||
setHasChanged(true);
|
||||
executeEvent('updateChatMessagesWithBlocks', true)
|
||||
setValue('')
|
||||
return
|
||||
executeEvent('updateChatMessagesWithBlocks', true);
|
||||
setValue('');
|
||||
return;
|
||||
}
|
||||
const responseModal = await show({
|
||||
userName,
|
||||
userAddress,
|
||||
});
|
||||
if (responseModal === "both") {
|
||||
if (responseModal === 'both') {
|
||||
await addToBlockList(userAddress, userName);
|
||||
} else if (responseModal === "address") {
|
||||
} else if (responseModal === 'address') {
|
||||
await addToBlockList(userAddress, null);
|
||||
} else if (responseModal === "name") {
|
||||
} else if (responseModal === 'name') {
|
||||
await addToBlockList(null, userName);
|
||||
}
|
||||
fetchBlockedUsers();
|
||||
setHasChanged(true);
|
||||
setValue('')
|
||||
if(user){
|
||||
setIsOpenBlockedModal(false)
|
||||
setValue('');
|
||||
if (user) {
|
||||
setIsOpenBlockedModal(false);
|
||||
}
|
||||
if(responseModal === 'both' || responseModal === 'address'){
|
||||
executeEvent('updateChatMessagesWithBlocks', true)
|
||||
if (responseModal === 'both' || responseModal === 'address') {
|
||||
executeEvent('updateChatMessagesWithBlocks', true);
|
||||
}
|
||||
} catch (error) {
|
||||
setOpenSnackGlobal(true);
|
||||
|
||||
setInfoSnackCustom({
|
||||
type: "error",
|
||||
message: error?.message || "Unable to block user",
|
||||
});
|
||||
setInfoSnackCustom({
|
||||
type: 'error',
|
||||
message: error?.message || 'Unable to block user',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const blockUserFromOutsideModalFunc = (e) => {
|
||||
const user = e.detail?.user;
|
||||
setIsOpenBlockedModal(true)
|
||||
blockUser(null, user)
|
||||
const user = e.detail?.user;
|
||||
setIsOpenBlockedModal(true);
|
||||
blockUser(null, user);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent('blockUserFromOutside', blockUserFromOutsideModalFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent(
|
||||
'blockUserFromOutside',
|
||||
blockUserFromOutsideModalFunc
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("blockUserFromOutside", blockUserFromOutsideModalFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("blockUserFromOutside", blockUserFromOutsideModalFunc);
|
||||
};
|
||||
}, []);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={isOpenBlockedModal}
|
||||
@ -145,14 +163,14 @@ export const BlockedUsersModal = () => {
|
||||
<DialogTitle>Blocked Users</DialogTitle>
|
||||
<DialogContent
|
||||
sx={{
|
||||
padding: "20px",
|
||||
padding: '20px',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
@ -180,16 +198,18 @@ export const BlockedUsersModal = () => {
|
||||
Blocked addresses- blocks processing of txs
|
||||
</DialogContentText>
|
||||
<Spacer height="10px" />
|
||||
<Button variant="contained" size="small" onClick={getNames}>Fetch names</Button>
|
||||
<Button variant="contained" size="small" onClick={getNames}>
|
||||
Fetch names
|
||||
</Button>
|
||||
<Spacer height="10px" />
|
||||
</>
|
||||
)}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "10px",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
{Object.entries(blockedUsers?.addresses || {})?.map(
|
||||
@ -197,11 +217,11 @@ export const BlockedUsersModal = () => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography>{addressesWithNames[key] || key}</Typography>
|
||||
@ -215,7 +235,7 @@ export const BlockedUsersModal = () => {
|
||||
try {
|
||||
await removeBlockFromList(key, undefined);
|
||||
setHasChanged(true);
|
||||
setValue("");
|
||||
setValue('');
|
||||
fetchBlockedUsers();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@ -241,20 +261,20 @@ export const BlockedUsersModal = () => {
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "10px",
|
||||
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",
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography>{key}</Typography>
|
||||
@ -284,20 +304,20 @@ export const BlockedUsersModal = () => {
|
||||
<DialogActions>
|
||||
<Button
|
||||
sx={{
|
||||
backgroundColor: "var(--green)",
|
||||
color: "black",
|
||||
fontWeight: "bold",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
fontWeight: 'bold',
|
||||
opacity: 0.7,
|
||||
"&:hover": {
|
||||
backgroundColor: "var(--green)",
|
||||
color: "black",
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
color: theme.palette.text.primary,
|
||||
opacity: 1,
|
||||
},
|
||||
}}
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
if (hasChanged) {
|
||||
executeEvent("updateChatMessagesWithBlocks", true);
|
||||
executeEvent('updateChatMessagesWithBlocks', true);
|
||||
}
|
||||
setIsOpenBlockedModal(false);
|
||||
}}
|
||||
@ -312,28 +332,37 @@ export const BlockedUsersModal = () => {
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
{"Decide what to block"}
|
||||
{'Decide what to block'}
|
||||
</DialogTitle>
|
||||
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
Blocking {message?.userName || message?.userAddress}
|
||||
</DialogContentText>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '10px',
|
||||
marginTop: '20px'
|
||||
}}>
|
||||
<InfoIcon sx={{
|
||||
color: 'fff'
|
||||
}}/> <Typography>Choose "block txs" or "all" to block chat messages </Typography>
|
||||
<Box
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
marginTop: '20px',
|
||||
}}
|
||||
>
|
||||
<InfoIcon
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>{' '}
|
||||
<Typography>
|
||||
Choose "block txs" or "all" to block chat messages{' '}
|
||||
</Typography>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
onOk("address");
|
||||
onOk('address');
|
||||
}}
|
||||
>
|
||||
Block txs
|
||||
@ -341,7 +370,7 @@ export const BlockedUsersModal = () => {
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
onOk("name");
|
||||
onOk('name');
|
||||
}}
|
||||
>
|
||||
Block QDN data
|
||||
@ -349,7 +378,7 @@ export const BlockedUsersModal = () => {
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
onOk("both");
|
||||
onOk('both');
|
||||
}}
|
||||
>
|
||||
Block All
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { DrawerUserLookup } from "../Drawer/DrawerUserLookup";
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { DrawerUserLookup } from '../Drawer/DrawerUserLookup';
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
@ -16,16 +16,21 @@ import {
|
||||
Typography,
|
||||
Table,
|
||||
CircularProgress,
|
||||
} from "@mui/material";
|
||||
import { getAddressInfo, getNameOrAddress } from "../../background";
|
||||
import { getBaseApiReact } from "../../App";
|
||||
import { getNameInfo } from "../Group/Group";
|
||||
import AccountCircleIcon from "@mui/icons-material/AccountCircle";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { formatTimestamp } from "../../utils/time";
|
||||
useTheme,
|
||||
} from '@mui/material';
|
||||
import { getAddressInfo, getNameOrAddress } from '../../background';
|
||||
import { getBaseApiReact } from '../../App';
|
||||
import { getNameInfo } from '../Group/Group';
|
||||
import AccountCircleIcon from '@mui/icons-material/AccountCircle';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { formatTimestamp } from '../../utils/time';
|
||||
import CloseFullscreenIcon from '@mui/icons-material/CloseFullscreen';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "../../utils/events";
|
||||
import {
|
||||
executeEvent,
|
||||
subscribeToEvent,
|
||||
unsubscribeFromEvent,
|
||||
} from '../../utils/events';
|
||||
|
||||
function formatAddress(str) {
|
||||
if (str.length <= 12) return str;
|
||||
@ -37,469 +42,511 @@ function formatAddress(str) {
|
||||
}
|
||||
|
||||
export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
|
||||
const [nameOrAddress, setNameOrAddress] = useState("");
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
const theme = useTheme();
|
||||
const [nameOrAddress, setNameOrAddress] = useState('');
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
const [addressInfo, setAddressInfo] = useState(null);
|
||||
const [isLoadingUser, setIsLoadingUser] = useState(false);
|
||||
const [isLoadingPayments, setIsLoadingPayments] = useState(false);
|
||||
const [payments, setPayments] = useState([]);
|
||||
const lookupFunc = useCallback(async (messageAddressOrName) => {
|
||||
try {
|
||||
setErrorMessage('')
|
||||
setIsLoadingUser(true)
|
||||
setPayments([])
|
||||
setAddressInfo(null)
|
||||
const inputAddressOrName = messageAddressOrName || nameOrAddress
|
||||
if (!inputAddressOrName?.trim())
|
||||
throw new Error("Please insert a name or address");
|
||||
const owner = await getNameOrAddress(inputAddressOrName);
|
||||
if (!owner) throw new Error("Name does not exist");
|
||||
const addressInfoRes = await getAddressInfo(owner);
|
||||
if (!addressInfoRes?.publicKey) {
|
||||
throw new Error("Address does not exist on blockchain");
|
||||
const lookupFunc = useCallback(
|
||||
async (messageAddressOrName) => {
|
||||
try {
|
||||
setErrorMessage('');
|
||||
setIsLoadingUser(true);
|
||||
setPayments([]);
|
||||
setAddressInfo(null);
|
||||
const inputAddressOrName = messageAddressOrName || nameOrAddress;
|
||||
|
||||
if (!inputAddressOrName?.trim())
|
||||
throw new Error('Please insert a name or address');
|
||||
const owner = await getNameOrAddress(inputAddressOrName);
|
||||
if (!owner) throw new Error('Name does not exist');
|
||||
|
||||
const addressInfoRes = await getAddressInfo(owner);
|
||||
if (!addressInfoRes?.publicKey) {
|
||||
throw new Error('Address does not exist on blockchain');
|
||||
}
|
||||
|
||||
const name = await getNameInfo(owner);
|
||||
const balanceRes = await fetch(
|
||||
`${getBaseApiReact()}/addresses/balance/${owner}`
|
||||
);
|
||||
|
||||
const balanceData = await balanceRes.json();
|
||||
setAddressInfo({
|
||||
...addressInfoRes,
|
||||
balance: balanceData,
|
||||
name,
|
||||
});
|
||||
setIsLoadingUser(false);
|
||||
setIsLoadingPayments(true);
|
||||
|
||||
const getPayments = await fetch(
|
||||
`${getBaseApiReact()}/transactions/search?txType=PAYMENT&address=${owner}&confirmationStatus=CONFIRMED&limit=20&reverse=true`
|
||||
);
|
||||
const paymentsData = await getPayments.json();
|
||||
setPayments(paymentsData);
|
||||
} catch (error) {
|
||||
setErrorMessage(error?.message);
|
||||
console.error(error);
|
||||
} finally {
|
||||
setIsLoadingUser(false);
|
||||
setIsLoadingPayments(false);
|
||||
}
|
||||
const name = await getNameInfo(owner);
|
||||
const balanceRes = await fetch(
|
||||
`${getBaseApiReact()}/addresses/balance/${owner}`
|
||||
);
|
||||
const balanceData = await balanceRes.json();
|
||||
setAddressInfo({
|
||||
...addressInfoRes,
|
||||
balance: balanceData,
|
||||
name,
|
||||
});
|
||||
setIsLoadingUser(false)
|
||||
setIsLoadingPayments(true)
|
||||
},
|
||||
[nameOrAddress]
|
||||
);
|
||||
|
||||
const getPayments = await fetch(
|
||||
`${getBaseApiReact()}/transactions/search?txType=PAYMENT&address=${owner}&confirmationStatus=CONFIRMED&limit=20&reverse=true`
|
||||
);
|
||||
const paymentsData = await getPayments.json();
|
||||
setPayments(paymentsData);
|
||||
|
||||
} catch (error) {
|
||||
setErrorMessage(error?.message)
|
||||
console.error(error);
|
||||
} finally {
|
||||
setIsLoadingUser(false)
|
||||
setIsLoadingPayments(false)
|
||||
}
|
||||
}, [nameOrAddress]);
|
||||
|
||||
const openUserLookupDrawerFunc = useCallback((e) => {
|
||||
setIsOpenDrawerLookup(true)
|
||||
const openUserLookupDrawerFunc = useCallback(
|
||||
(e) => {
|
||||
setIsOpenDrawerLookup(true);
|
||||
const message = e.detail?.addressOrName;
|
||||
if(message){
|
||||
lookupFunc(message)
|
||||
if (message) {
|
||||
lookupFunc(message);
|
||||
}
|
||||
}, [lookupFunc, setIsOpenDrawerLookup]);
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("openUserLookupDrawer", openUserLookupDrawerFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("openUserLookupDrawer", openUserLookupDrawerFunc);
|
||||
};
|
||||
}, [openUserLookupDrawerFunc]);
|
||||
},
|
||||
[lookupFunc, setIsOpenDrawerLookup]
|
||||
);
|
||||
|
||||
const onClose = ()=> {
|
||||
setIsOpenDrawerLookup(false)
|
||||
setNameOrAddress('')
|
||||
setErrorMessage('')
|
||||
setPayments([])
|
||||
setIsLoadingUser(false)
|
||||
setIsLoadingPayments(false)
|
||||
setAddressInfo(null)
|
||||
}
|
||||
useEffect(() => {
|
||||
subscribeToEvent('openUserLookupDrawer', openUserLookupDrawerFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent('openUserLookupDrawer', openUserLookupDrawerFunc);
|
||||
};
|
||||
}, [openUserLookupDrawerFunc]);
|
||||
|
||||
const onClose = () => {
|
||||
setIsOpenDrawerLookup(false);
|
||||
setNameOrAddress('');
|
||||
setErrorMessage('');
|
||||
setPayments([]);
|
||||
setIsLoadingUser(false);
|
||||
setIsLoadingPayments(false);
|
||||
setAddressInfo(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<DrawerUserLookup open={isOpenDrawerLookup} setOpen={setIsOpenDrawerLookup}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
padding: "15px",
|
||||
height: "100vh",
|
||||
overflow: "hidden",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100vh',
|
||||
overflow: 'hidden',
|
||||
padding: '15px',
|
||||
}}
|
||||
>
|
||||
|
||||
|
||||
<Box
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "5px",
|
||||
alignItems: "center",
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flexShrink: 0,
|
||||
gap: '5px',
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
autoFocus
|
||||
autoFocus
|
||||
value={nameOrAddress}
|
||||
onChange={(e) => setNameOrAddress(e.target.value)}
|
||||
size="small"
|
||||
placeholder="Address or Name"
|
||||
autoComplete="off"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" && nameOrAddress) {
|
||||
if (e.key === 'Enter' && nameOrAddress) {
|
||||
lookupFunc();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<ButtonBase onClick={()=> {
|
||||
lookupFunc();
|
||||
}} >
|
||||
<SearchIcon sx={{
|
||||
color: 'white',
|
||||
marginRight: '20px'
|
||||
}} />
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
lookupFunc();
|
||||
}}
|
||||
>
|
||||
<SearchIcon
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
marginRight: '20px',
|
||||
}}
|
||||
/>
|
||||
</ButtonBase>
|
||||
<ButtonBase sx={{
|
||||
marginLeft: 'auto',
|
||||
|
||||
}} onClick={()=> {
|
||||
onClose()
|
||||
}}>
|
||||
<CloseFullscreenIcon sx={{
|
||||
color: 'white'
|
||||
}} />
|
||||
<ButtonBase
|
||||
sx={{
|
||||
marginLeft: 'auto',
|
||||
}}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<CloseFullscreenIcon
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
overflow: "auto",
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
{!isLoadingUser && errorMessage && (
|
||||
<Box sx={{
|
||||
{!isLoadingUser && errorMessage && (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
justifyContent: 'center',
|
||||
marginTop: '40px'
|
||||
}}>
|
||||
<Typography>{errorMessage}</Typography>
|
||||
marginTop: '40px',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography>{errorMessage}</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{isLoadingUser && (
|
||||
<Box sx={{
|
||||
)}
|
||||
{isLoadingUser && (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
justifyContent: 'center',
|
||||
marginTop: '40px'
|
||||
}}>
|
||||
<CircularProgress sx={{
|
||||
color: 'white'
|
||||
}} />
|
||||
marginTop: '40px',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<CircularProgress
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{!isLoadingUser && addressInfo && (
|
||||
)}
|
||||
{!isLoadingUser && addressInfo && (
|
||||
<>
|
||||
|
||||
<Spacer height="30px" />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
flexWrap: "wrap",
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
sx={{
|
||||
padding: "15px",
|
||||
minWidth: "320px",
|
||||
alignItems: "center",
|
||||
minHeight: "200px",
|
||||
background: "var(--bg-primary)",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{addressInfo?.name ?? "Name not registered"}
|
||||
</Typography>
|
||||
<Spacer height="20px" />
|
||||
<Divider>
|
||||
{addressInfo?.name ? (
|
||||
<Avatar
|
||||
sx={{
|
||||
height: "50px",
|
||||
width: "50px",
|
||||
"& img": {
|
||||
objectFit: "fill",
|
||||
},
|
||||
}}
|
||||
alt={addressInfo?.name}
|
||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||
addressInfo?.name
|
||||
}/qortal_avatar?async=true`}
|
||||
>
|
||||
<AccountCircleIcon
|
||||
sx={{
|
||||
fontSize: "50px",
|
||||
}}
|
||||
/>
|
||||
</Avatar>
|
||||
) : (
|
||||
<AccountCircleIcon
|
||||
sx={{
|
||||
fontSize: "50px",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Divider>
|
||||
<Spacer height="20px" />
|
||||
<Typography
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
Level {addressInfo?.level}
|
||||
</Typography>
|
||||
</Card>
|
||||
<Card
|
||||
sx={{
|
||||
padding: "15px",
|
||||
minWidth: "320px",
|
||||
minHeight: "200px",
|
||||
gap: "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
background: "var(--bg-primary)",
|
||||
}}
|
||||
>
|
||||
<Spacer height="30px" />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
gap: '20px',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
background: theme.palette.background.default,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
minHeight: '200px',
|
||||
minWidth: '320px',
|
||||
padding: '15px',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
{addressInfo?.name ?? 'Name not registered'}
|
||||
</Typography>
|
||||
|
||||
<Spacer height="20px" />
|
||||
|
||||
<Divider>
|
||||
{addressInfo?.name ? (
|
||||
<Avatar
|
||||
sx={{
|
||||
height: '50px',
|
||||
width: '50px',
|
||||
'& img': {
|
||||
objectFit: 'fill',
|
||||
},
|
||||
}}
|
||||
alt={addressInfo?.name}
|
||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||
addressInfo?.name
|
||||
}/qortal_avatar?async=true`}
|
||||
>
|
||||
<AccountCircleIcon
|
||||
sx={{
|
||||
fontSize: '50px',
|
||||
}}
|
||||
/>
|
||||
</Avatar>
|
||||
) : (
|
||||
<AccountCircleIcon
|
||||
sx={{
|
||||
fontSize: '50px',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Divider>
|
||||
|
||||
<Spacer height="20px" />
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
Level {addressInfo?.level}
|
||||
</Typography>
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
sx={{
|
||||
background: theme.palette.background.default,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '20px',
|
||||
minHeight: '200px',
|
||||
minWidth: '320px',
|
||||
padding: '15px',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
gap: '20px',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<Typography>Address</Typography>
|
||||
</Box>
|
||||
<Tooltip
|
||||
title={
|
||||
<span
|
||||
style={{
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: '14px',
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
copy address
|
||||
</span>
|
||||
}
|
||||
placement="bottom"
|
||||
arrow
|
||||
sx={{ fontSize: '24' }}
|
||||
slotProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
},
|
||||
},
|
||||
arrow: {
|
||||
sx: {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(addressInfo?.address);
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
textAlign: 'end',
|
||||
}}
|
||||
>
|
||||
{addressInfo?.address}
|
||||
</Typography>
|
||||
</ButtonBase>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
gap: '20px',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography>Balance</Typography>
|
||||
<Typography>{addressInfo?.balance}</Typography>
|
||||
</Box>
|
||||
|
||||
<Spacer height="20px" />
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
executeEvent('openPaymentInternal', {
|
||||
address: addressInfo?.address,
|
||||
name: addressInfo?.name,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Send QORT
|
||||
</Button>
|
||||
</Card>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Spacer height="40px" />
|
||||
|
||||
{isLoadingPayments && (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<CircularProgress
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{!isLoadingPayments && addressInfo && (
|
||||
<Card
|
||||
sx={{
|
||||
background: theme.palette.background.default,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'auto',
|
||||
padding: '15px',
|
||||
}}
|
||||
>
|
||||
<Typography>20 most recent payments</Typography>
|
||||
|
||||
<Spacer height="20px" />
|
||||
|
||||
{!isLoadingPayments && payments?.length === 0 && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexShrink: 0,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography>Address</Typography>
|
||||
<Typography>No payments</Typography>
|
||||
</Box>
|
||||
<Tooltip
|
||||
title={
|
||||
<span
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "14px",
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
copy address
|
||||
</span>
|
||||
}
|
||||
placement="bottom"
|
||||
arrow
|
||||
sx={{ fontSize: "24" }}
|
||||
slotProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
color: "#ffffff",
|
||||
backgroundColor: "#444444",
|
||||
},
|
||||
},
|
||||
arrow: {
|
||||
sx: {
|
||||
color: "#444444",
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(addressInfo?.address);
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
textAlign: "end",
|
||||
}}
|
||||
>
|
||||
{addressInfo?.address}
|
||||
</Typography>
|
||||
</ButtonBase>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Typography>Balance</Typography>
|
||||
<Typography>{addressInfo?.balance}</Typography>
|
||||
</Box>
|
||||
<Spacer height="20px" />
|
||||
<Button variant="contained" onClick={()=> {
|
||||
executeEvent('openPaymentInternal', {
|
||||
address: addressInfo?.address,
|
||||
name: addressInfo?.name,
|
||||
});
|
||||
}}>Send QORT</Button>
|
||||
</Card>
|
||||
</Box>
|
||||
|
||||
)}
|
||||
|
||||
</>
|
||||
)}
|
||||
<Spacer height="40px" />
|
||||
{isLoadingPayments && (
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<CircularProgress sx={{
|
||||
color: 'white'
|
||||
}} />
|
||||
</Box>
|
||||
)}
|
||||
{!isLoadingPayments && addressInfo && (
|
||||
<Card
|
||||
sx={{
|
||||
padding: "15px",
|
||||
overflow: "auto",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
background: "var(--bg-primary)",
|
||||
}}
|
||||
>
|
||||
<Typography>20 most recent payments</Typography>
|
||||
<Spacer height="20px" />
|
||||
{!isLoadingPayments && payments?.length === 0 && (
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<Typography>No payments</Typography>
|
||||
</Box>
|
||||
)}
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Sender</TableCell>
|
||||
<TableCell>Reciver</TableCell>
|
||||
<TableCell>Amount</TableCell>
|
||||
<TableCell>Time</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{payments.map((payment, index) => (
|
||||
<TableRow key={payment?.signature}>
|
||||
<TableCell>
|
||||
<Tooltip
|
||||
title={
|
||||
<span
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "14px",
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
copy address
|
||||
</span>
|
||||
}
|
||||
placement="bottom"
|
||||
arrow
|
||||
sx={{ fontSize: "24" }}
|
||||
slotProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
color: "#ffffff",
|
||||
backgroundColor: "#444444",
|
||||
},
|
||||
},
|
||||
arrow: {
|
||||
sx: {
|
||||
color: "#444444",
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(
|
||||
payment?.creatorAddress
|
||||
);
|
||||
}}
|
||||
>
|
||||
{formatAddress(payment?.creatorAddress)}
|
||||
</ButtonBase>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Tooltip
|
||||
title={
|
||||
<span
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "14px",
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
copy address
|
||||
</span>
|
||||
}
|
||||
placement="bottom"
|
||||
arrow
|
||||
sx={{ fontSize: "24" }}
|
||||
slotProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
color: "#ffffff",
|
||||
backgroundColor: "#444444",
|
||||
},
|
||||
},
|
||||
arrow: {
|
||||
sx: {
|
||||
color: "#444444",
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(payment?.recipient);
|
||||
}}
|
||||
>
|
||||
{formatAddress(payment?.recipient)}
|
||||
|
||||
</ButtonBase>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{payment?.amount}
|
||||
</TableCell>
|
||||
<TableCell>{formatTimestamp(payment?.timestamp)}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Sender</TableCell>
|
||||
<TableCell>Reciver</TableCell>
|
||||
<TableCell>Amount</TableCell>
|
||||
<TableCell>Time</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
<TableBody>
|
||||
{payments.map((payment, index) => (
|
||||
<TableRow key={payment?.signature}>
|
||||
<TableCell>
|
||||
<Tooltip
|
||||
title={
|
||||
<span
|
||||
style={{
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: '14px',
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
copy address
|
||||
</span>
|
||||
}
|
||||
placement="bottom"
|
||||
arrow
|
||||
sx={{ fontSize: '24' }}
|
||||
slotProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor:
|
||||
theme.palette.background.default,
|
||||
},
|
||||
},
|
||||
arrow: {
|
||||
sx: {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(
|
||||
payment?.creatorAddress
|
||||
);
|
||||
}}
|
||||
>
|
||||
{formatAddress(payment?.creatorAddress)}
|
||||
</ButtonBase>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Tooltip
|
||||
title={
|
||||
<span
|
||||
style={{
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: '14px',
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
copy address
|
||||
</span>
|
||||
}
|
||||
placement="bottom"
|
||||
arrow
|
||||
sx={{ fontSize: '24' }}
|
||||
slotProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor:
|
||||
theme.palette.background.default,
|
||||
},
|
||||
},
|
||||
arrow: {
|
||||
sx: {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(payment?.recipient);
|
||||
}}
|
||||
>
|
||||
{formatAddress(payment?.recipient)}
|
||||
</ButtonBase>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell>{payment?.amount}</TableCell>
|
||||
<TableCell>
|
||||
{formatTimestamp(payment?.timestamp)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
</Box>
|
||||
</Box>
|
||||
</DrawerUserLookup>
|
||||
|
@ -1,10 +1,17 @@
|
||||
import React, { useCallback, useContext, useEffect, useState } from 'react';
|
||||
import { Popover, Button, Box, CircularProgress } from '@mui/material';
|
||||
import { useCallback, useContext, useEffect, useState } from 'react';
|
||||
import {
|
||||
Popover,
|
||||
Button,
|
||||
Box,
|
||||
CircularProgress,
|
||||
useTheme,
|
||||
} from '@mui/material';
|
||||
import { executeEvent } from '../utils/events';
|
||||
import { MyContext } from '../App';
|
||||
|
||||
export const WrapperUserAction = ({ children, address, name, disabled }) => {
|
||||
const {isRunningPublicNode} = useContext(MyContext)
|
||||
const theme = useTheme();
|
||||
const { isRunningPublicNode } = useContext(MyContext);
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
|
||||
// Handle child element click to open Popover
|
||||
@ -22,8 +29,8 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
|
||||
const open = Boolean(anchorEl);
|
||||
const id = open ? address || name : undefined;
|
||||
|
||||
if(disabled){
|
||||
return children
|
||||
if (disabled) {
|
||||
return children;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -31,16 +38,16 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
|
||||
<Box
|
||||
onClick={handleChildClick} // Open popover on click
|
||||
sx={{
|
||||
display: 'inline-flex', // Keep inline behavior
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
alignSelf: 'flex-start', // Prevent stretching to parent height
|
||||
cursor: 'pointer',
|
||||
display: 'inline-flex', // Keep inline behavior
|
||||
height: 'fit-content', // Limit height to content size
|
||||
justifyContent: 'center',
|
||||
maxHeight: '100%', // Prevent flex shrink behavior in a flex container
|
||||
maxWidth: '100%', // Optional: Limit the width to avoid overflow
|
||||
padding: 0,
|
||||
width: 'fit-content', // Limit width to content size
|
||||
height: 'fit-content', // Limit height to content size
|
||||
alignSelf: 'flex-start', // Prevent stretching to parent height
|
||||
maxWidth: '100%', // Optional: Limit the width to avoid overflow
|
||||
maxHeight: '100%', // Prevent flex shrink behavior in a flex container
|
||||
}}
|
||||
>
|
||||
{/* Render the child without altering dimensions */}
|
||||
@ -49,159 +56,151 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
|
||||
|
||||
{/* Popover */}
|
||||
{open && (
|
||||
<Popover
|
||||
id={id}
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
onClose={handleClose} // Close popover on click outside
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
componentsProps={{
|
||||
paper: {
|
||||
onClick: (event) => event.stopPropagation(), // Stop propagation inside popover
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box sx={{ p: 2, display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||
{/* Option 1: Message */}
|
||||
<Button
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
|
||||
handleClose();
|
||||
setTimeout(() => {
|
||||
executeEvent('openDirectMessageInternal', {
|
||||
address,
|
||||
name,
|
||||
});
|
||||
}, 200);
|
||||
}}
|
||||
sx={{
|
||||
color: 'white',
|
||||
justifyContent: 'flex-start'
|
||||
}}
|
||||
>
|
||||
Message
|
||||
</Button>
|
||||
|
||||
{/* Option 2: Send QORT */}
|
||||
<Button
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
executeEvent('openPaymentInternal', {
|
||||
address,
|
||||
name,
|
||||
});
|
||||
handleClose();
|
||||
|
||||
}}
|
||||
sx={{
|
||||
color: 'white',
|
||||
justifyContent: 'flex-start'
|
||||
}}
|
||||
>
|
||||
Send QORT
|
||||
</Button>
|
||||
<Button
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(address|| "");
|
||||
handleClose();
|
||||
|
||||
}}
|
||||
sx={{
|
||||
color: 'white',
|
||||
justifyContent: 'flex-start'
|
||||
}}
|
||||
>
|
||||
Copy address
|
||||
</Button>
|
||||
<Button
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
executeEvent('openUserLookupDrawer', {
|
||||
addressOrName: name || address
|
||||
})
|
||||
handleClose();
|
||||
|
||||
}}
|
||||
sx={{
|
||||
color: 'white',
|
||||
justifyContent: 'flex-start'
|
||||
}}
|
||||
>
|
||||
<Popover
|
||||
id={id}
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
onClose={handleClose} // Close popover on click outside
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
componentsProps={{
|
||||
paper: {
|
||||
onClick: (event) => event.stopPropagation(), // Stop propagation inside popover
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box sx={{ p: 2, display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||
{/* Option 1: Message */}
|
||||
<Button
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
handleClose();
|
||||
setTimeout(() => {
|
||||
executeEvent('openDirectMessageInternal', {
|
||||
address,
|
||||
name,
|
||||
});
|
||||
}, 200);
|
||||
}}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
justifyContent: 'flex-start',
|
||||
}}
|
||||
>
|
||||
Message
|
||||
</Button>
|
||||
|
||||
{/* Option 2: Send QORT */}
|
||||
<Button
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
executeEvent('openPaymentInternal', {
|
||||
address,
|
||||
name,
|
||||
});
|
||||
handleClose();
|
||||
}}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
justifyContent: 'flex-start',
|
||||
}}
|
||||
>
|
||||
Send QORT
|
||||
</Button>
|
||||
<Button
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(address || '');
|
||||
handleClose();
|
||||
}}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
justifyContent: 'flex-start',
|
||||
}}
|
||||
>
|
||||
Copy address
|
||||
</Button>
|
||||
<Button
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
executeEvent('openUserLookupDrawer', {
|
||||
addressOrName: name || address,
|
||||
});
|
||||
handleClose();
|
||||
}}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
justifyContent: 'flex-start',
|
||||
}}
|
||||
>
|
||||
User lookup
|
||||
</Button>
|
||||
</Button>
|
||||
|
||||
{!isRunningPublicNode && (
|
||||
<BlockUser handleClose={handleClose} address={address} name={name} />
|
||||
|
||||
)}
|
||||
</Box>
|
||||
</Popover>
|
||||
{!isRunningPublicNode && (
|
||||
<BlockUser
|
||||
handleClose={handleClose}
|
||||
address={address}
|
||||
name={name}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Popover>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const BlockUser = ({ address, name, handleClose }) => {
|
||||
const [isAlreadyBlocked, setIsAlreadyBlocked] = useState(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { isUserBlocked, addToBlockList, removeBlockFromList } =
|
||||
useContext(MyContext);
|
||||
const theme = useTheme();
|
||||
|
||||
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])
|
||||
useEffect(() => {
|
||||
if (!address) return;
|
||||
setIsAlreadyBlocked(isUserBlocked(address, name));
|
||||
}, [address, setIsAlreadyBlocked, isUserBlocked, name]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="text"
|
||||
onClick={async () => {
|
||||
try {
|
||||
setIsLoading(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 {
|
||||
setIsLoading(false)
|
||||
handleClose();
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
sx={{
|
||||
color: 'white',
|
||||
variant="text"
|
||||
onClick={async () => {
|
||||
try {
|
||||
setIsLoading(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 {
|
||||
setIsLoading(false);
|
||||
handleClose();
|
||||
}
|
||||
}}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
gap: '10px',
|
||||
justifyContent: 'flex-start',
|
||||
gap: '10px'
|
||||
}}
|
||||
>
|
||||
{(isAlreadyBlocked === null || isLoading) && (
|
||||
<CircularProgress color="secondary" size={24} />
|
||||
)}
|
||||
{isAlreadyBlocked && (
|
||||
'Unblock name'
|
||||
)}
|
||||
{isAlreadyBlocked === false && (
|
||||
'Block name'
|
||||
)}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{(isAlreadyBlocked === null || isLoading) && (
|
||||
<CircularProgress color="secondary" size={24} />
|
||||
)}
|
||||
{isAlreadyBlocked && 'Unblock name'}
|
||||
{isAlreadyBlocked === false && 'Block name'}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user