Add theme

This commit is contained in:
Nicola Benaglia 2025-04-19 07:56:09 +02:00
parent 3a302cf5b2
commit 18ec6126b7
3 changed files with 748 additions and 673 deletions

View File

@ -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,7 +94,7 @@ 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;
@ -90,52 +104,56 @@ export const BlockedUsersModal = () => {
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(() => { useEffect(() => {
subscribeToEvent("blockUserFromOutside", blockUserFromOutsideModalFunc); subscribeToEvent('blockUserFromOutside', blockUserFromOutsideModalFunc);
return () => { return () => {
unsubscribeFromEvent("blockUserFromOutside", blockUserFromOutsideModalFunc); 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',
display: 'flex',
gap: '10px', gap: '10px',
marginTop: '20px' marginTop: '20px',
}}> }}
<InfoIcon sx={{ >
color: 'fff' <InfoIcon
}}/> <Typography>Choose "block txs" or "all" to block chat messages </Typography> 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

View File

@ -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,101 +42,108 @@ 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(
async (messageAddressOrName) => {
try { try {
setErrorMessage('') setErrorMessage('');
setIsLoadingUser(true) setIsLoadingUser(true);
setPayments([]) setPayments([]);
setAddressInfo(null) setAddressInfo(null);
const inputAddressOrName = messageAddressOrName || nameOrAddress const inputAddressOrName = messageAddressOrName || nameOrAddress;
if (!inputAddressOrName?.trim()) if (!inputAddressOrName?.trim())
throw new Error("Please insert a name or address"); throw new Error('Please insert a name or address');
const owner = await getNameOrAddress(inputAddressOrName); const owner = await getNameOrAddress(inputAddressOrName);
if (!owner) throw new Error("Name does not exist"); if (!owner) throw new Error('Name does not exist');
const addressInfoRes = await getAddressInfo(owner); const addressInfoRes = await getAddressInfo(owner);
if (!addressInfoRes?.publicKey) { if (!addressInfoRes?.publicKey) {
throw new Error("Address does not exist on blockchain"); throw new Error('Address does not exist on blockchain');
} }
const name = await getNameInfo(owner); const name = await getNameInfo(owner);
const balanceRes = await fetch( const balanceRes = await fetch(
`${getBaseApiReact()}/addresses/balance/${owner}` `${getBaseApiReact()}/addresses/balance/${owner}`
); );
const balanceData = await balanceRes.json(); const balanceData = await balanceRes.json();
setAddressInfo({ setAddressInfo({
...addressInfoRes, ...addressInfoRes,
balance: balanceData, balance: balanceData,
name, name,
}); });
setIsLoadingUser(false) setIsLoadingUser(false);
setIsLoadingPayments(true) setIsLoadingPayments(true);
const getPayments = await fetch( const getPayments = await fetch(
`${getBaseApiReact()}/transactions/search?txType=PAYMENT&address=${owner}&confirmationStatus=CONFIRMED&limit=20&reverse=true` `${getBaseApiReact()}/transactions/search?txType=PAYMENT&address=${owner}&confirmationStatus=CONFIRMED&limit=20&reverse=true`
); );
const paymentsData = await getPayments.json(); const paymentsData = await getPayments.json();
setPayments(paymentsData); setPayments(paymentsData);
} catch (error) { } catch (error) {
setErrorMessage(error?.message) setErrorMessage(error?.message);
console.error(error); console.error(error);
} finally { } finally {
setIsLoadingUser(false) setIsLoadingUser(false);
setIsLoadingPayments(false) setIsLoadingPayments(false);
} }
}, [nameOrAddress]); },
[nameOrAddress]
);
const openUserLookupDrawerFunc = useCallback((e) => { const openUserLookupDrawerFunc = useCallback(
setIsOpenDrawerLookup(true) (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(() => { useEffect(() => {
subscribeToEvent("openUserLookupDrawer", openUserLookupDrawerFunc); subscribeToEvent('openUserLookupDrawer', openUserLookupDrawerFunc);
return () => { return () => {
unsubscribeFromEvent("openUserLookupDrawer", openUserLookupDrawerFunc); unsubscribeFromEvent('openUserLookupDrawer', openUserLookupDrawerFunc);
}; };
}, [openUserLookupDrawerFunc]); }, [openUserLookupDrawerFunc]);
const onClose = () => { const onClose = () => {
setIsOpenDrawerLookup(false) setIsOpenDrawerLookup(false);
setNameOrAddress('') setNameOrAddress('');
setErrorMessage('') setErrorMessage('');
setPayments([]) setPayments([]);
setIsLoadingUser(false) setIsLoadingUser(false);
setIsLoadingPayments(false) setIsLoadingPayments(false);
setAddressInfo(null) 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
@ -142,101 +154,116 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
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
onClick={() => {
lookupFunc(); lookupFunc();
}} > }}
<SearchIcon sx={{ >
color: 'white', <SearchIcon
marginRight: '20px' sx={{
}} /> color: theme.palette.text.primary,
marginRight: '20px',
}}
/>
</ButtonBase> </ButtonBase>
<ButtonBase sx={{ <ButtonBase
sx={{
marginLeft: 'auto', marginLeft: 'auto',
}}
}} onClick={()=> { onClick={() => {
onClose() 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 <Box
sx={{ sx={{
display: "flex", display: 'flex',
gap: "20px", flexDirection: 'row',
flexWrap: "wrap", flexWrap: 'wrap',
flexDirection: "row", gap: '20px',
width: "100%", justifyContent: 'center',
justifyContent: "center", width: '100%',
}} }}
> >
<Card <Card
sx={{ sx={{
padding: "15px", alignItems: 'center',
minWidth: "320px", background: theme.palette.background.default,
alignItems: "center", display: 'flex',
minHeight: "200px", flexDirection: 'column',
background: "var(--bg-primary)", minHeight: '200px',
display: "flex", minWidth: '320px',
flexDirection: "column", padding: '15px',
}} }}
> >
<Typography <Typography
sx={{ sx={{
textAlign: "center", textAlign: 'center',
}} }}
> >
{addressInfo?.name ?? "Name not registered"} {addressInfo?.name ?? 'Name not registered'}
</Typography> </Typography>
<Spacer height="20px" /> <Spacer height="20px" />
<Divider> <Divider>
{addressInfo?.name ? ( {addressInfo?.name ? (
<Avatar <Avatar
sx={{ sx={{
height: "50px", height: '50px',
width: "50px", width: '50px',
"& img": { '& img': {
objectFit: "fill", objectFit: 'fill',
}, },
}} }}
alt={addressInfo?.name} alt={addressInfo?.name}
@ -246,49 +273,52 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
> >
<AccountCircleIcon <AccountCircleIcon
sx={{ sx={{
fontSize: "50px", fontSize: '50px',
}} }}
/> />
</Avatar> </Avatar>
) : ( ) : (
<AccountCircleIcon <AccountCircleIcon
sx={{ sx={{
fontSize: "50px", fontSize: '50px',
}} }}
/> />
)} )}
</Divider> </Divider>
<Spacer height="20px" /> <Spacer height="20px" />
<Typography <Typography
sx={{ sx={{
textAlign: "center", textAlign: 'center',
}} }}
> >
Level {addressInfo?.level} Level {addressInfo?.level}
</Typography> </Typography>
</Card> </Card>
<Card <Card
sx={{ sx={{
padding: "15px", background: theme.palette.background.default,
minWidth: "320px", display: 'flex',
minHeight: "200px", flexDirection: 'column',
gap: "20px", gap: '20px',
display: "flex", minHeight: '200px',
flexDirection: "column", minWidth: '320px',
background: "var(--bg-primary)", padding: '15px',
}} }}
> >
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
gap: "20px", gap: '20px',
justifyContent: "space-between", justifyContent: 'space-between',
width: "100%", width: '100%',
}} }}
> >
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
flexShrink: 0, flexShrink: 0,
}} }}
> >
@ -298,8 +328,8 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
title={ title={
<span <span
style={{ style={{
color: "white", color: theme.palette.text.primary,
fontSize: "14px", fontSize: '14px',
fontWeight: 700, fontWeight: 700,
}} }}
> >
@ -308,17 +338,17 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
} }
placement="bottom" placement="bottom"
arrow arrow
sx={{ fontSize: "24" }} sx={{ fontSize: '24' }}
slotProps={{ slotProps={{
tooltip: { tooltip: {
sx: { sx: {
color: "#ffffff", color: theme.palette.text.primary,
backgroundColor: "#444444", backgroundColor: theme.palette.background.default,
}, },
}, },
arrow: { arrow: {
sx: { sx: {
color: "#444444", color: theme.palette.text.primary,
}, },
}, },
}} }}
@ -330,7 +360,7 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
> >
<Typography <Typography
sx={{ sx={{
textAlign: "end", textAlign: 'end',
}} }}
> >
{addressInfo?.address} {addressInfo?.address}
@ -338,64 +368,80 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
</ButtonBase> </ButtonBase>
</Tooltip> </Tooltip>
</Box> </Box>
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
gap: "20px", gap: '20px',
justifyContent: "space-between", justifyContent: 'space-between',
width: "100%", width: '100%',
}} }}
> >
<Typography>Balance</Typography> <Typography>Balance</Typography>
<Typography>{addressInfo?.balance}</Typography> <Typography>{addressInfo?.balance}</Typography>
</Box> </Box>
<Spacer height="20px" /> <Spacer height="20px" />
<Button variant="contained" onClick={()=> {
<Button
variant="contained"
onClick={() => {
executeEvent('openPaymentInternal', { executeEvent('openPaymentInternal', {
address: addressInfo?.address, address: addressInfo?.address,
name: addressInfo?.name, name: addressInfo?.name,
}); });
}}>Send QORT</Button> }}
>
Send QORT
</Button>
</Card> </Card>
</Box> </Box>
</> </>
)} )}
<Spacer height="40px" /> <Spacer height="40px" />
{isLoadingPayments && ( {isLoadingPayments && (
<Box sx={{ <Box
sx={{
display: 'flex', display: 'flex',
justifyContent: 'center',
width: '100%', width: '100%',
justifyContent: 'center' }}
}}> >
<CircularProgress sx={{ <CircularProgress
color: 'white' sx={{
}} /> color: theme.palette.text.primary,
}}
/>
</Box> </Box>
)} )}
{!isLoadingPayments && addressInfo && ( {!isLoadingPayments && addressInfo && (
<Card <Card
sx={{ sx={{
padding: "15px", background: theme.palette.background.default,
overflow: "auto", display: 'flex',
flexDirection: 'column',
display: "flex", overflow: 'auto',
flexDirection: "column", padding: '15px',
background: "var(--bg-primary)",
}} }}
> >
<Typography>20 most recent payments</Typography> <Typography>20 most recent payments</Typography>
<Spacer height="20px" /> <Spacer height="20px" />
{!isLoadingPayments && payments?.length === 0 && ( {!isLoadingPayments && payments?.length === 0 && (
<Box sx={{ <Box
sx={{
display: 'flex', display: 'flex',
justifyContent: 'center',
width: '100%', width: '100%',
justifyContent: 'center' }}
}}> >
<Typography>No payments</Typography> <Typography>No payments</Typography>
</Box> </Box>
)} )}
<Table> <Table>
<TableHead> <TableHead>
<TableRow> <TableRow>
@ -405,6 +451,7 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
<TableCell>Time</TableCell> <TableCell>Time</TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
{payments.map((payment, index) => ( {payments.map((payment, index) => (
<TableRow key={payment?.signature}> <TableRow key={payment?.signature}>
@ -413,8 +460,8 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
title={ title={
<span <span
style={{ style={{
color: "white", color: theme.palette.text.primary,
fontSize: "14px", fontSize: '14px',
fontWeight: 700, fontWeight: 700,
}} }}
> >
@ -423,17 +470,18 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
} }
placement="bottom" placement="bottom"
arrow arrow
sx={{ fontSize: "24" }} sx={{ fontSize: '24' }}
slotProps={{ slotProps={{
tooltip: { tooltip: {
sx: { sx: {
color: "#ffffff", color: theme.palette.text.primary,
backgroundColor: "#444444", backgroundColor:
theme.palette.background.default,
}, },
}, },
arrow: { arrow: {
sx: { sx: {
color: "#444444", color: theme.palette.text.primary,
}, },
}, },
}} }}
@ -454,8 +502,8 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
title={ title={
<span <span
style={{ style={{
color: "white", color: theme.palette.text.primary,
fontSize: "14px", fontSize: '14px',
fontWeight: 700, fontWeight: 700,
}} }}
> >
@ -464,17 +512,18 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
} }
placement="bottom" placement="bottom"
arrow arrow
sx={{ fontSize: "24" }} sx={{ fontSize: '24' }}
slotProps={{ slotProps={{
tooltip: { tooltip: {
sx: { sx: {
color: "#ffffff", color: theme.palette.text.primary,
backgroundColor: "#444444", backgroundColor:
theme.palette.background.default,
}, },
}, },
arrow: { arrow: {
sx: { sx: {
color: "#444444", color: theme.palette.text.primary,
}, },
}, },
}} }}
@ -485,21 +534,19 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
}} }}
> >
{formatAddress(payment?.recipient)} {formatAddress(payment?.recipient)}
</ButtonBase> </ButtonBase>
</Tooltip> </Tooltip>
</TableCell> </TableCell>
<TableCell>{payment?.amount}</TableCell>
<TableCell> <TableCell>
{payment?.amount} {formatTimestamp(payment?.timestamp)}
</TableCell> </TableCell>
<TableCell>{formatTimestamp(payment?.timestamp)}</TableCell>
</TableRow> </TableRow>
))} ))}
</TableBody> </TableBody>
</Table> </Table>
</Card> </Card>
)} )}
</Box> </Box>
</Box> </Box>
</DrawerUserLookup> </DrawerUserLookup>

View File

@ -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
@ -23,7 +30,7 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
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 */}
@ -73,7 +80,6 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
<Button <Button
variant="text" variant="text"
onClick={() => { onClick={() => {
handleClose(); handleClose();
setTimeout(() => { setTimeout(() => {
executeEvent('openDirectMessageInternal', { executeEvent('openDirectMessageInternal', {
@ -83,8 +89,8 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
}, 200); }, 200);
}} }}
sx={{ sx={{
color: 'white', color: theme.palette.text.primary,
justifyContent: 'flex-start' justifyContent: 'flex-start',
}} }}
> >
Message Message
@ -99,11 +105,10 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
name, name,
}); });
handleClose(); handleClose();
}} }}
sx={{ sx={{
color: 'white', color: theme.palette.text.primary,
justifyContent: 'flex-start' justifyContent: 'flex-start',
}} }}
> >
Send QORT Send QORT
@ -111,13 +116,12 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
<Button <Button
variant="text" variant="text"
onClick={() => { onClick={() => {
navigator.clipboard.writeText(address|| ""); navigator.clipboard.writeText(address || '');
handleClose(); handleClose();
}} }}
sx={{ sx={{
color: 'white', color: theme.palette.text.primary,
justifyContent: 'flex-start' justifyContent: 'flex-start',
}} }}
> >
Copy address Copy address
@ -126,22 +130,24 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
variant="text" variant="text"
onClick={() => { onClick={() => {
executeEvent('openUserLookupDrawer', { executeEvent('openUserLookupDrawer', {
addressOrName: name || address addressOrName: name || address,
}) });
handleClose(); handleClose();
}} }}
sx={{ sx={{
color: 'white', color: theme.palette.text.primary,
justifyContent: 'flex-start' justifyContent: 'flex-start',
}} }}
> >
User lookup User lookup
</Button> </Button>
{!isRunningPublicNode && ( {!isRunningPublicNode && (
<BlockUser handleClose={handleClose} address={address} name={name} /> <BlockUser
handleClose={handleClose}
address={address}
name={name}
/>
)} )}
</Box> </Box>
</Popover> </Popover>
@ -150,28 +156,27 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
); );
}; };
const BlockUser = ({ address, name, handleClose }) => { const BlockUser = ({ address, name, handleClose }) => {
const [isAlreadyBlocked, setIsAlreadyBlocked] = useState(null) const [isAlreadyBlocked, setIsAlreadyBlocked] = useState(null);
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false);
const {isUserBlocked, const { isUserBlocked, addToBlockList, removeBlockFromList } =
addToBlockList, useContext(MyContext);
removeBlockFromList} = useContext(MyContext) const theme = useTheme();
useEffect(() => { useEffect(() => {
if(!address) return if (!address) return;
setIsAlreadyBlocked(isUserBlocked(address, name)) setIsAlreadyBlocked(isUserBlocked(address, name));
}, [address, setIsAlreadyBlocked, isUserBlocked, 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) {
@ -179,29 +184,23 @@ useEffect(()=> {
// } // }
// 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={{ sx={{
color: 'white', color: theme.palette.text.primary,
gap: '10px',
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 && ( {isAlreadyBlocked && 'Unblock name'}
'Unblock name' {isAlreadyBlocked === false && 'Block name'}
)}
{isAlreadyBlocked === false && (
'Block name'
)}
</Button> </Button>
) );
} };