import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, Button, Box, Tooltip, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, TextField, useTheme, Typography, CircularProgress, Avatar, } from '@mui/material'; import { useAtom } from 'jotai'; import { forwardRef, useCallback, useEffect, useMemo, useState } from 'react'; import { TableVirtuoso, TableComponents } from 'react-virtuoso'; import { forSaleAtom, namesAtom } from '../../state/global/names'; import PersonIcon from '@mui/icons-material/Person'; import { useModal } from '../../hooks/useModal'; import { dismissToast, ImagePicker, RequestQueueWithPromise, showError, showLoading, showSuccess, Spacer, useGlobal, } from 'qapp-core'; import { Availability } from '../RegisterName'; import CheckIcon from '@mui/icons-material/Check'; import ErrorIcon from '@mui/icons-material/Error'; import { BarSpinner } from '../../common/Spinners/BarSpinner/BarSpinner'; interface NameData { name: string; isSelling?: boolean; } const getNameQueue = new RequestQueueWithPromise(2); const VirtuosoTableComponents: TableComponents = { Scroller: forwardRef((props, ref) => ( )), Table: (props) => ( ), TableHead: forwardRef((props, ref) => ( )), TableRow, TableBody: forwardRef((props, ref) => ( )), }; function fixedHeaderContent() { return ( Name Actions ); } const ManageAvatar = ({ name, modalFunctionsAvatar }) => { const [hasAvatar, setHasAvatar] = useState(null); const checkIfAvatarExists = useCallback(async (name) => { try { const identifier = `qortal_avatar`; const url = `/arbitrary/resources/searchsimple?mode=ALL&service=THUMBNAIL&identifier=${identifier}&limit=1&name=${name}&includemetadata=false&prefix=true`; const response = await getNameQueue.enqueue(() => fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }) ); const responseData = await response.json(); if (responseData?.length > 0) { setHasAvatar(true); } else { setHasAvatar(false); } } catch (error) { console.log(error); } }, []); useEffect(() => { if (!name) return; checkIfAvatarExists(name); }, [name, checkIfAvatarExists]); return ( ); }; function rowContent( _index: number, row: NameData, primaryName?: string, modalFunctions?: any, modalFunctionsUpdateName?: any, modalFunctionsAvatar?: any, modalFunctionsSellName?: any ) { const handleUpdate = async (name: string) => { try { const response = await modalFunctionsUpdateName.show(); console.log('Update:', row.name); console.log('hello', response); await qortalRequest({ action: 'UPDATE_NAME', newName: response, oldName: name, }); } catch (error) { console.log('error', error); } // Your logic here }; const handleSell = async (name: string) => { try { if (name === primaryName) { await modalFunctions.show({ name }); } const price = await modalFunctionsSellName.show(name); console.log('hello'); await qortalRequest({ action: 'SELL_NAME', nameForSale: name, salePrice: price, }); } catch (error) { console.log('error', error); } }; const handleCancel = async (name: string) => { try { console.log('hello', name); await qortalRequest({ action: 'CANCEL_SELL_NAME', nameForSale: name, }); } catch (error) { console.log('error', error); } }; return ( <> {primaryName === row.name && ( )} {row.name} {!row.isSelling ? ( ) : ( )} ); } export const NameTable = ({ names, primaryName }) => { // const [names, setNames] = useAtom(namesAtom); const [namesForSale] = useAtom(forSaleAtom); const modalFunctions = useModal(); const modalFunctionsUpdateName = useModal(); const modalFunctionsAvatar = useModal(); const modalFunctionsSellName = useModal(); console.log('names', names); const namesToDisplay = useMemo(() => { const namesForSaleString = namesForSale.map((item) => item.name); return names.map((name) => { return { name: name.name, isSelling: namesForSaleString.includes(name.name), }; }); }, [names, namesForSale]); return ( rowContent( index, row, primaryName, modalFunctions, modalFunctionsUpdateName, modalFunctionsAvatar, modalFunctionsSellName ) } /> {modalFunctions?.isShow && ( Warning Caution when selling your primary name {modalFunctions?.data?.name} is your primary name. If you are an admin of a private group, selling this name will remove your group keys for the group. Make sure another admin re-encrypts the latest keys before selling. Proceed with caution! )} {modalFunctionsUpdateName?.isShow && ( )} {modalFunctionsAvatar?.isShow && ( )} {modalFunctionsSellName?.isShow && ( )} ); }; const AvatarModal = ({ modalFunctionsAvatar }) => { const [arbitraryFee, setArbitraryFee] = useState(''); const [pickedAvatar, setPickedAvatar] = useState(null); const [isLoadingPublish, setIsLoadingPublish] = useState(false); const theme = useTheme(); console.log('pickedAvatar', pickedAvatar); useEffect(() => { const getArbitraryName = async () => { try { const data = await fetch(`/transactions/unitfee?txType=ARBITRARY`); const fee = await data.text(); setArbitraryFee((Number(fee) / 1e8).toFixed(8)); } catch (error) { console.error(error); } }; getArbitraryName(); }, []); const publishAvatar = async () => { const loadId = showLoading('Publishing avatar...please wait'); try { setIsLoadingPublish(true); await qortalRequest({ action: 'PUBLISH_QDN_RESOURCE', base64: pickedAvatar?.base64, service: 'THUMBNAIL', identifier: 'qortal_avatar', name: modalFunctionsAvatar.data.name, }); showSuccess('Successfully published avatar'); modalFunctionsAvatar.onOk(); } catch (error) { showError(error?.message || 'Unable to publish avatar'); } finally { dismissToast(loadId); setIsLoadingPublish(false); } }; return ( Publish Avatar {modalFunctionsAvatar.data.hasAvatar && !pickedAvatar?.base64 && ( )} {pickedAvatar?.base64 && ( )} {pickedAvatar?.name && ( <> {pickedAvatar?.name} )} (500 KB max. for GIFS){' '} setPickedAvatar(file)} mode="single"> ); }; const UpdateNameModal = ({ modalFunctionsUpdateName }) => { const [step, setStep] = useState(1); const [newName, setNewName] = useState(''); const [isNameAvailable, setIsNameAvailable] = useState( Availability.NULL ); const [nameFee, setNameFee] = useState(null); const balance = useGlobal().auth.balance; const theme = useTheme(); const checkIfNameExisits = async (name) => { if (!name?.trim()) { setIsNameAvailable(Availability.NULL); return; } setIsNameAvailable(Availability.LOADING); try { const res = await fetch(`/names/` + name); const data = await res.json(); if (data?.message === 'name unknown') { setIsNameAvailable(Availability.AVAILABLE); } else { setIsNameAvailable(Availability.NOT_AVAILABLE); } } catch (error) { console.error(error); } finally { } }; useEffect(() => { const handler = setTimeout(() => { checkIfNameExisits(newName); }, 500); // Cleanup timeout if searchValue changes before the timeout completes return () => { clearTimeout(handler); }; }, [newName]); useEffect(() => { const nameRegistrationFee = async () => { try { const data = await fetch(`/transactions/unitfee?txType=REGISTER_NAME`); const fee = await data.text(); setNameFee((Number(fee) / 1e8).toFixed(8)); } catch (error) { console.error(error); } }; nameRegistrationFee(); }, []); return ( {step === 1 && ( <> Warning Caution when updating your name If you update your Name, you will forfeit the resources associated with the original Name. In other words, you will lose ownership of the content under the original Name on QDN. Proceed with caution! )} {step === 2 && ( <> Warning Choose new name setNewName(e.target.value)} value={newName} placeholder="Choose a name" /> {(!balance || (nameFee && balance && balance < nameFee)) && ( <> Your balance is {balance ?? 0} QORT. A name registration requires a {nameFee} QORT fee )} {isNameAvailable === Availability.AVAILABLE && ( {newName} is available )} {isNameAvailable === Availability.NOT_AVAILABLE && ( {newName} is unavailable )} {isNameAvailable === Availability.LOADING && ( Checking if name already existis )} )} ); }; const SellNameModal = ({ modalFunctionsSellName }) => { const [price, setPrice] = useState(0); return ( Selling name Choose selling price setPrice(+e.target.value)} value={price} type="number" placeholder="Choose a name" /> ); };