added conditions for actions

This commit is contained in:
PhilReact 2025-05-20 15:47:14 +03:00
parent d1233cc1ad
commit 18151bfa9b
5 changed files with 124 additions and 17 deletions

View File

@ -18,12 +18,17 @@ import {
Spacer, Spacer,
useGlobal, useGlobal,
} from 'qapp-core'; } from 'qapp-core';
import { useEffect, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { BarSpinner } from '../common/Spinners/BarSpinner/BarSpinner'; import { BarSpinner } from '../common/Spinners/BarSpinner/BarSpinner';
import CheckIcon from '@mui/icons-material/Check'; import CheckIcon from '@mui/icons-material/Check';
import ErrorIcon from '@mui/icons-material/Error'; import ErrorIcon from '@mui/icons-material/Error';
import { useSetAtom } from 'jotai'; import { useAtom, useSetAtom } from 'jotai';
import { namesAtom, pendingTxsAtom } from '../state/global/names'; import {
forSaleAtom,
namesAtom,
pendingTxsAtom,
primaryNameAtom,
} from '../state/global/names';
import { Availability } from '../interfaces'; import { Availability } from '../interfaces';
const Label = styled('label')` const Label = styled('label')`
@ -38,7 +43,9 @@ const RegisterName = () => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const balance = useGlobal().auth.balance; const balance = useGlobal().auth.balance;
const setNames = useSetAtom(namesAtom); const setNames = useSetAtom(namesAtom);
const [namesForSale] = useAtom(forSaleAtom);
const [primaryName] = useAtom(primaryNameAtom);
const [pendingTxs] = useAtom(pendingTxsAtom);
const address = useGlobal().auth.address; const address = useGlobal().auth.address;
const [nameValue, setNameValue] = useState(''); const [nameValue, setNameValue] = useState('');
const [isNameAvailable, setIsNameAvailable] = useState<Availability>( const [isNameAvailable, setIsNameAvailable] = useState<Availability>(
@ -49,6 +56,25 @@ const RegisterName = () => {
const [isLoadingRegisterName, setIsLoadingRegisterName] = useState(false); const [isLoadingRegisterName, setIsLoadingRegisterName] = useState(false);
const theme = useTheme(); const theme = useTheme();
const [nameFee, setNameFee] = useState<number | null>(null); const [nameFee, setNameFee] = useState<number | null>(null);
const isPrimaryNameForSale = useMemo(() => {
if (!primaryName) return false;
const findPendingNameSellTx = pendingTxs?.['SELL_NAME'];
let isOnSale = false;
if (findPendingNameSellTx) {
Object.values(findPendingNameSellTx).forEach((value) => {
if (value?.name === primaryName) {
isOnSale = true;
return;
}
});
}
if (isOnSale) return true;
const findNameIndex = namesForSale?.findIndex(
(item) => item?.name === primaryName
);
if (findNameIndex === -1) return false;
return true;
}, [namesForSale, primaryName, pendingTxs]);
const registerNameFunc = async () => { const registerNameFunc = async () => {
if (!address) return; if (!address) return;
const loadId = showLoading('Registering name...please wait'); const loadId = showLoading('Registering name...please wait');
@ -142,7 +168,7 @@ const RegisterName = () => {
return ( return (
<> <>
<Button <Button
// disabled={!nameValue?.trim()} disabled={isPrimaryNameForSale}
onClick={() => setIsOpen(true)} onClick={() => setIsOpen(true)}
variant="outlined" variant="outlined"
sx={{ sx={{

View File

@ -94,7 +94,8 @@ function rowContent(
row: NamesForSale, row: NamesForSale,
setPendingTxs: SetPendingTxs, setPendingTxs: SetPendingTxs,
setNames: SetNames, setNames: SetNames,
setNamesForSale: SetNamesForSale setNamesForSale: SetNamesForSale,
isPrimaryNameForSale: boolean
) { ) {
const handleBuy = async (name: string) => { const handleBuy = async (name: string) => {
const loadId = showLoading('Attempting to purchase name...please wait'); const loadId = showLoading('Attempting to purchase name...please wait');
@ -146,6 +147,7 @@ function rowContent(
<TableCell>{row.salePrice}</TableCell> <TableCell>{row.salePrice}</TableCell>
<TableCell> <TableCell>
<Button <Button
disabled={isPrimaryNameForSale}
variant="contained" variant="contained"
size="small" size="small"
onClick={() => handleBuy(row.name)} onClick={() => handleBuy(row.name)}
@ -162,6 +164,7 @@ interface ForSaleTable {
sortDirection: SortDirection; sortDirection: SortDirection;
sortBy: SortBy; sortBy: SortBy;
handleSort: (sortBy: SortBy) => void; handleSort: (sortBy: SortBy) => void;
isPrimaryNameForSale: boolean;
} }
export const ForSaleTable = ({ export const ForSaleTable = ({
@ -169,6 +172,7 @@ export const ForSaleTable = ({
sortDirection, sortDirection,
sortBy, sortBy,
handleSort, handleSort,
isPrimaryNameForSale,
}: ForSaleTable) => { }: ForSaleTable) => {
const setNames = useSetAtom(namesAtom); const setNames = useSetAtom(namesAtom);
const setNamesForSale = useSetAtom(forSaleAtom); const setNamesForSale = useSetAtom(forSaleAtom);
@ -188,7 +192,14 @@ export const ForSaleTable = ({
fixedHeaderContent(sortBy, sortDirection, handleSort) fixedHeaderContent(sortBy, sortDirection, handleSort)
} }
itemContent={(index, row: NamesForSale) => itemContent={(index, row: NamesForSale) =>
rowContent(index, row, setPendingTxs, setNames, setNamesForSale) rowContent(
index,
row,
setPendingTxs,
setNames,
setNamesForSale,
isPrimaryNameForSale
)
} }
/> />
</Paper> </Paper>

View File

@ -26,6 +26,7 @@ import { TableVirtuoso, TableComponents } from 'react-virtuoso';
import { import {
forceRefreshAtom, forceRefreshAtom,
forSaleAtom, forSaleAtom,
isNamePendingTx,
Names, Names,
namesAtom, namesAtom,
NamesForSale, NamesForSale,
@ -99,9 +100,14 @@ function fixedHeaderContent() {
interface ManageAvatarProps { interface ManageAvatarProps {
name: string; name: string;
modalFunctionsAvatar: ModalFunctionsAvatar; modalFunctionsAvatar: ModalFunctionsAvatar;
isNameCurrentlyDoingATx?: boolean;
} }
const ManageAvatar = ({ name, modalFunctionsAvatar }: ManageAvatarProps) => { const ManageAvatar = ({
name,
modalFunctionsAvatar,
isNameCurrentlyDoingATx,
}: ManageAvatarProps) => {
const { setHasAvatar, getHasAvatar } = usePendingTxs(); const { setHasAvatar, getHasAvatar } = usePendingTxs();
const [refresh] = useAtom(refreshAtom); // just to subscribe const [refresh] = useAtom(refreshAtom); // just to subscribe
const [hasAvatarState, setHasAvatarState] = useState<boolean | null>(null); const [hasAvatarState, setHasAvatarState] = useState<boolean | null>(null);
@ -146,7 +152,7 @@ const ManageAvatar = ({ name, modalFunctionsAvatar }: ManageAvatarProps) => {
<Button <Button
variant="outlined" variant="outlined"
size="small" size="small"
disabled={hasAvatarState === null} disabled={hasAvatarState === null || isNameCurrentlyDoingATx}
onClick={() => onClick={() =>
modalFunctionsAvatar.show({ name, hasAvatar: Boolean(hasAvatarState) }) modalFunctionsAvatar.show({ name, hasAvatar: Boolean(hasAvatarState) })
} }
@ -179,7 +185,8 @@ function rowContent(
modalFunctionsSellName: ReturnType<typeof useModal>, modalFunctionsSellName: ReturnType<typeof useModal>,
setPendingTxs: SetPendingTxs, setPendingTxs: SetPendingTxs,
setNames: SetNames, setNames: SetNames,
setNamesForSale: SetNamesForSale setNamesForSale: SetNamesForSale,
isNameCurrentlyDoingATx: boolean
) { ) {
const handleUpdate = async (name: string) => { const handleUpdate = async (name: string) => {
if (name === primaryName && numberOfNames > 1) { if (name === primaryName && numberOfNames > 1) {
@ -366,6 +373,10 @@ function rowContent(
color={primaryName === row.name ? 'warning' : 'primary'} color={primaryName === row.name ? 'warning' : 'primary'}
variant="outlined" variant="outlined"
size="small" size="small"
disabled={
(row?.name === primaryName && numberOfNames > 1) ||
isNameCurrentlyDoingATx
}
onClick={() => handleUpdate(row.name)} onClick={() => handleUpdate(row.name)}
> >
Update Update
@ -376,6 +387,10 @@ function rowContent(
size="small" size="small"
variant="outlined" variant="outlined"
onClick={() => handleSell(row.name)} onClick={() => handleSell(row.name)}
disabled={
(row?.name === primaryName && numberOfNames > 1) ||
isNameCurrentlyDoingATx
}
> >
Sell Sell
</Button> </Button>
@ -384,6 +399,7 @@ function rowContent(
color="error" color="error"
size="small" size="small"
onClick={() => handleCancel(row.name)} onClick={() => handleCancel(row.name)}
disabled={isNameCurrentlyDoingATx}
> >
Cancel Sell Cancel Sell
</Button> </Button>
@ -391,6 +407,7 @@ function rowContent(
<ManageAvatar <ManageAvatar
name={row.name} name={row.name}
modalFunctionsAvatar={modalFunctionsAvatar} modalFunctionsAvatar={modalFunctionsAvatar}
isNameCurrentlyDoingATx={isNameCurrentlyDoingATx}
/> />
</Box> </Box>
</TableCell> </TableCell>
@ -406,6 +423,8 @@ export const NameTable = ({ names, primaryName }: NameTableProps) => {
const setNames = useSetAtom(namesAtom); const setNames = useSetAtom(namesAtom);
const { auth } = useGlobal(); const { auth } = useGlobal();
const [namesForSale, setNamesForSale] = useAtom(forSaleAtom); const [namesForSale, setNamesForSale] = useAtom(forSaleAtom);
const [pendingTxs] = useAtom(pendingTxsAtom);
const modalFunctions = useModal<{ name: string }>(); const modalFunctions = useModal<{ name: string }>();
const modalFunctionsUpdateName = useModal(); const modalFunctionsUpdateName = useModal();
const modalFunctionsAvatar = useModal<{ name: string; hasAvatar: boolean }>(); const modalFunctionsAvatar = useModal<{ name: string; hasAvatar: boolean }>();
@ -435,8 +454,12 @@ export const NameTable = ({ names, primaryName }: NameTableProps) => {
data={namesToDisplay} data={namesToDisplay}
components={VirtuosoTableComponents} components={VirtuosoTableComponents}
fixedHeaderContent={fixedHeaderContent} fixedHeaderContent={fixedHeaderContent}
itemContent={(index, row) => itemContent={(index, row) => {
rowContent( const isNameCurrentlyDoingATx = isNamePendingTx(
row?.name,
pendingTxs
);
return rowContent(
index, index,
row, row,
primaryName, primaryName,
@ -449,9 +472,10 @@ export const NameTable = ({ names, primaryName }: NameTableProps) => {
modalFunctionsSellName, modalFunctionsSellName,
setPendingTxs, setPendingTxs,
setNames, setNames,
setNamesForSale setNamesForSale,
) isNameCurrentlyDoingATx
} );
}}
/> />
{modalFunctions?.isShow && ( {modalFunctions?.isShow && (
<Dialog <Dialog

View File

@ -1,12 +1,19 @@
import { Box, TextField } from '@mui/material'; import { Box, TextField } from '@mui/material';
import { ForSaleTable } from '../components/Tables/ForSaleTable'; import { ForSaleTable } from '../components/Tables/ForSaleTable';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { forSaleAtom } from '../state/global/names'; import {
forSaleAtom,
pendingTxsAtom,
primaryNameAtom,
} from '../state/global/names';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import { SortBy, SortDirection } from '../interfaces'; import { SortBy, SortDirection } from '../interfaces';
export const Market = () => { export const Market = () => {
const [namesForSale] = useAtom(forSaleAtom); const [namesForSale] = useAtom(forSaleAtom);
const [pendingTxs] = useAtom(pendingTxsAtom);
const [primaryName] = useAtom(primaryNameAtom);
const [sortBy, setSortBy] = useState<SortBy>('name'); const [sortBy, setSortBy] = useState<SortBy>('name');
const [sortDirection, setSortDirection] = useState<SortDirection>('asc'); const [sortDirection, setSortDirection] = useState<SortDirection>('asc');
const [filterValue, setFilterValue] = useState(''); const [filterValue, setFilterValue] = useState('');
@ -41,6 +48,26 @@ export const Market = () => {
}); });
}, [namesForSale, sortBy, sortDirection, filterValue]); }, [namesForSale, sortBy, sortDirection, filterValue]);
const isPrimaryNameForSale = useMemo(() => {
if (!primaryName) return false;
const findPendingNameSellTx = pendingTxs?.['SELL_NAME'];
let isOnSale = false;
if (findPendingNameSellTx) {
Object.values(findPendingNameSellTx).forEach((value) => {
if (value?.name === primaryName) {
isOnSale = true;
return;
}
});
}
if (isOnSale) return true;
const findNameIndex = namesForSale?.findIndex(
(item) => item?.name === primaryName
);
if (findNameIndex === -1) return false;
return true;
}, [namesForSale, primaryName, pendingTxs]);
useEffect(() => { useEffect(() => {
const handler = setTimeout(() => { const handler = setTimeout(() => {
setFilterValue(value); setFilterValue(value);
@ -90,6 +117,7 @@ export const Market = () => {
sortBy={sortBy} sortBy={sortBy}
sortDirection={sortDirection} sortDirection={sortDirection}
handleSort={handleSort} handleSort={handleSort}
isPrimaryNameForSale={isPrimaryNameForSale}
/> />
</div> </div>
); );

View File

@ -1,5 +1,23 @@
import { atom } from 'jotai'; import { atom } from 'jotai';
export const isNamePendingTx = (
name: string,
pendingTxs: PendingTxsState
): boolean => {
for (const category of Object.keys(pendingTxs) as TransactionCategory[]) {
const txMap = pendingTxs[category];
if (!txMap) continue;
for (const tx of Object.values(txMap)) {
if (tx.name === name) {
return true;
}
}
}
return false;
};
interface AdditionalFields { interface AdditionalFields {
callback: () => void; callback: () => void;
status: 'PENDING'; status: 'PENDING';
@ -35,7 +53,7 @@ interface UpdateNameTransaction {
newData: string; newData: string;
} }
interface SellNameTransaction { export interface SellNameTransaction {
type: 'SELL_NAME'; type: 'SELL_NAME';
timestamp: number; timestamp: number;
reference: string; reference: string;