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