mirror of
https://github.com/Qortal/names.git
synced 2025-06-14 18:21:21 +00:00
added conditions for actions
This commit is contained in:
parent
d1233cc1ad
commit
18151bfa9b
@ -18,12 +18,17 @@ import {
|
||||
Spacer,
|
||||
useGlobal,
|
||||
} from 'qapp-core';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { BarSpinner } from '../common/Spinners/BarSpinner/BarSpinner';
|
||||
import CheckIcon from '@mui/icons-material/Check';
|
||||
import ErrorIcon from '@mui/icons-material/Error';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { namesAtom, pendingTxsAtom } from '../state/global/names';
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
import {
|
||||
forSaleAtom,
|
||||
namesAtom,
|
||||
pendingTxsAtom,
|
||||
primaryNameAtom,
|
||||
} from '../state/global/names';
|
||||
import { Availability } from '../interfaces';
|
||||
|
||||
const Label = styled('label')`
|
||||
@ -38,7 +43,9 @@ const RegisterName = () => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const balance = useGlobal().auth.balance;
|
||||
const setNames = useSetAtom(namesAtom);
|
||||
|
||||
const [namesForSale] = useAtom(forSaleAtom);
|
||||
const [primaryName] = useAtom(primaryNameAtom);
|
||||
const [pendingTxs] = useAtom(pendingTxsAtom);
|
||||
const address = useGlobal().auth.address;
|
||||
const [nameValue, setNameValue] = useState('');
|
||||
const [isNameAvailable, setIsNameAvailable] = useState<Availability>(
|
||||
@ -49,6 +56,25 @@ const RegisterName = () => {
|
||||
const [isLoadingRegisterName, setIsLoadingRegisterName] = useState(false);
|
||||
const theme = useTheme();
|
||||
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 () => {
|
||||
if (!address) return;
|
||||
const loadId = showLoading('Registering name...please wait');
|
||||
@ -142,7 +168,7 @@ const RegisterName = () => {
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
// disabled={!nameValue?.trim()}
|
||||
disabled={isPrimaryNameForSale}
|
||||
onClick={() => setIsOpen(true)}
|
||||
variant="outlined"
|
||||
sx={{
|
||||
|
@ -94,7 +94,8 @@ function rowContent(
|
||||
row: NamesForSale,
|
||||
setPendingTxs: SetPendingTxs,
|
||||
setNames: SetNames,
|
||||
setNamesForSale: SetNamesForSale
|
||||
setNamesForSale: SetNamesForSale,
|
||||
isPrimaryNameForSale: boolean
|
||||
) {
|
||||
const handleBuy = async (name: string) => {
|
||||
const loadId = showLoading('Attempting to purchase name...please wait');
|
||||
@ -146,6 +147,7 @@ function rowContent(
|
||||
<TableCell>{row.salePrice}</TableCell>
|
||||
<TableCell>
|
||||
<Button
|
||||
disabled={isPrimaryNameForSale}
|
||||
variant="contained"
|
||||
size="small"
|
||||
onClick={() => handleBuy(row.name)}
|
||||
@ -162,6 +164,7 @@ interface ForSaleTable {
|
||||
sortDirection: SortDirection;
|
||||
sortBy: SortBy;
|
||||
handleSort: (sortBy: SortBy) => void;
|
||||
isPrimaryNameForSale: boolean;
|
||||
}
|
||||
|
||||
export const ForSaleTable = ({
|
||||
@ -169,6 +172,7 @@ export const ForSaleTable = ({
|
||||
sortDirection,
|
||||
sortBy,
|
||||
handleSort,
|
||||
isPrimaryNameForSale,
|
||||
}: ForSaleTable) => {
|
||||
const setNames = useSetAtom(namesAtom);
|
||||
const setNamesForSale = useSetAtom(forSaleAtom);
|
||||
@ -188,7 +192,14 @@ export const ForSaleTable = ({
|
||||
fixedHeaderContent(sortBy, sortDirection, handleSort)
|
||||
}
|
||||
itemContent={(index, row: NamesForSale) =>
|
||||
rowContent(index, row, setPendingTxs, setNames, setNamesForSale)
|
||||
rowContent(
|
||||
index,
|
||||
row,
|
||||
setPendingTxs,
|
||||
setNames,
|
||||
setNamesForSale,
|
||||
isPrimaryNameForSale
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Paper>
|
||||
|
@ -26,6 +26,7 @@ import { TableVirtuoso, TableComponents } from 'react-virtuoso';
|
||||
import {
|
||||
forceRefreshAtom,
|
||||
forSaleAtom,
|
||||
isNamePendingTx,
|
||||
Names,
|
||||
namesAtom,
|
||||
NamesForSale,
|
||||
@ -99,9 +100,14 @@ function fixedHeaderContent() {
|
||||
interface ManageAvatarProps {
|
||||
name: string;
|
||||
modalFunctionsAvatar: ModalFunctionsAvatar;
|
||||
isNameCurrentlyDoingATx?: boolean;
|
||||
}
|
||||
|
||||
const ManageAvatar = ({ name, modalFunctionsAvatar }: ManageAvatarProps) => {
|
||||
const ManageAvatar = ({
|
||||
name,
|
||||
modalFunctionsAvatar,
|
||||
isNameCurrentlyDoingATx,
|
||||
}: ManageAvatarProps) => {
|
||||
const { setHasAvatar, getHasAvatar } = usePendingTxs();
|
||||
const [refresh] = useAtom(refreshAtom); // just to subscribe
|
||||
const [hasAvatarState, setHasAvatarState] = useState<boolean | null>(null);
|
||||
@ -146,7 +152,7 @@ const ManageAvatar = ({ name, modalFunctionsAvatar }: ManageAvatarProps) => {
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
disabled={hasAvatarState === null}
|
||||
disabled={hasAvatarState === null || isNameCurrentlyDoingATx}
|
||||
onClick={() =>
|
||||
modalFunctionsAvatar.show({ name, hasAvatar: Boolean(hasAvatarState) })
|
||||
}
|
||||
@ -179,7 +185,8 @@ function rowContent(
|
||||
modalFunctionsSellName: ReturnType<typeof useModal>,
|
||||
setPendingTxs: SetPendingTxs,
|
||||
setNames: SetNames,
|
||||
setNamesForSale: SetNamesForSale
|
||||
setNamesForSale: SetNamesForSale,
|
||||
isNameCurrentlyDoingATx: boolean
|
||||
) {
|
||||
const handleUpdate = async (name: string) => {
|
||||
if (name === primaryName && numberOfNames > 1) {
|
||||
@ -366,6 +373,10 @@ function rowContent(
|
||||
color={primaryName === row.name ? 'warning' : 'primary'}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
disabled={
|
||||
(row?.name === primaryName && numberOfNames > 1) ||
|
||||
isNameCurrentlyDoingATx
|
||||
}
|
||||
onClick={() => handleUpdate(row.name)}
|
||||
>
|
||||
Update
|
||||
@ -376,6 +387,10 @@ function rowContent(
|
||||
size="small"
|
||||
variant="outlined"
|
||||
onClick={() => handleSell(row.name)}
|
||||
disabled={
|
||||
(row?.name === primaryName && numberOfNames > 1) ||
|
||||
isNameCurrentlyDoingATx
|
||||
}
|
||||
>
|
||||
Sell
|
||||
</Button>
|
||||
@ -384,6 +399,7 @@ function rowContent(
|
||||
color="error"
|
||||
size="small"
|
||||
onClick={() => handleCancel(row.name)}
|
||||
disabled={isNameCurrentlyDoingATx}
|
||||
>
|
||||
Cancel Sell
|
||||
</Button>
|
||||
@ -391,6 +407,7 @@ function rowContent(
|
||||
<ManageAvatar
|
||||
name={row.name}
|
||||
modalFunctionsAvatar={modalFunctionsAvatar}
|
||||
isNameCurrentlyDoingATx={isNameCurrentlyDoingATx}
|
||||
/>
|
||||
</Box>
|
||||
</TableCell>
|
||||
@ -406,6 +423,8 @@ export const NameTable = ({ names, primaryName }: NameTableProps) => {
|
||||
const setNames = useSetAtom(namesAtom);
|
||||
const { auth } = useGlobal();
|
||||
const [namesForSale, setNamesForSale] = useAtom(forSaleAtom);
|
||||
const [pendingTxs] = useAtom(pendingTxsAtom);
|
||||
|
||||
const modalFunctions = useModal<{ name: string }>();
|
||||
const modalFunctionsUpdateName = useModal();
|
||||
const modalFunctionsAvatar = useModal<{ name: string; hasAvatar: boolean }>();
|
||||
@ -435,8 +454,12 @@ export const NameTable = ({ names, primaryName }: NameTableProps) => {
|
||||
data={namesToDisplay}
|
||||
components={VirtuosoTableComponents}
|
||||
fixedHeaderContent={fixedHeaderContent}
|
||||
itemContent={(index, row) =>
|
||||
rowContent(
|
||||
itemContent={(index, row) => {
|
||||
const isNameCurrentlyDoingATx = isNamePendingTx(
|
||||
row?.name,
|
||||
pendingTxs
|
||||
);
|
||||
return rowContent(
|
||||
index,
|
||||
row,
|
||||
primaryName,
|
||||
@ -449,9 +472,10 @@ export const NameTable = ({ names, primaryName }: NameTableProps) => {
|
||||
modalFunctionsSellName,
|
||||
setPendingTxs,
|
||||
setNames,
|
||||
setNamesForSale
|
||||
)
|
||||
}
|
||||
setNamesForSale,
|
||||
isNameCurrentlyDoingATx
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{modalFunctions?.isShow && (
|
||||
<Dialog
|
||||
|
@ -1,12 +1,19 @@
|
||||
import { Box, TextField } from '@mui/material';
|
||||
import { ForSaleTable } from '../components/Tables/ForSaleTable';
|
||||
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 { SortBy, SortDirection } from '../interfaces';
|
||||
|
||||
export const Market = () => {
|
||||
const [namesForSale] = useAtom(forSaleAtom);
|
||||
const [pendingTxs] = useAtom(pendingTxsAtom);
|
||||
const [primaryName] = useAtom(primaryNameAtom);
|
||||
|
||||
const [sortBy, setSortBy] = useState<SortBy>('name');
|
||||
const [sortDirection, setSortDirection] = useState<SortDirection>('asc');
|
||||
const [filterValue, setFilterValue] = useState('');
|
||||
@ -41,6 +48,26 @@ export const Market = () => {
|
||||
});
|
||||
}, [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(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setFilterValue(value);
|
||||
@ -90,6 +117,7 @@ export const Market = () => {
|
||||
sortBy={sortBy}
|
||||
sortDirection={sortDirection}
|
||||
handleSort={handleSort}
|
||||
isPrimaryNameForSale={isPrimaryNameForSale}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,5 +1,23 @@
|
||||
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 {
|
||||
callback: () => void;
|
||||
status: 'PENDING';
|
||||
@ -35,7 +53,7 @@ interface UpdateNameTransaction {
|
||||
newData: string;
|
||||
}
|
||||
|
||||
interface SellNameTransaction {
|
||||
export interface SellNameTransaction {
|
||||
type: 'SELL_NAME';
|
||||
timestamp: number;
|
||||
reference: string;
|
||||
|
Loading…
x
Reference in New Issue
Block a user