mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-04-23 19:37:52 +00:00
register name dialog redesigned
This commit is contained in:
parent
c772e8cebe
commit
e02ac2d869
161
src/App.tsx
161
src/App.tsx
@ -149,6 +149,8 @@ import { useBlockedAddresses } from "./components/Group/useBlockUsers";
|
|||||||
import { WalletIcon } from "./assets/Icons/WalletIcon";
|
import { WalletIcon } from "./assets/Icons/WalletIcon";
|
||||||
import { DrawerUserLookup } from "./components/Drawer/DrawerUserLookup";
|
import { DrawerUserLookup } from "./components/Drawer/DrawerUserLookup";
|
||||||
import { UserLookup } from "./components/UserLookup.tsx/UserLookup";
|
import { UserLookup } from "./components/UserLookup.tsx/UserLookup";
|
||||||
|
import { RegisterName } from "./components/RegisterName";
|
||||||
|
import { BuyQortInformation } from "./components/BuyQortInformation";
|
||||||
|
|
||||||
type extStates =
|
type extStates =
|
||||||
| "not-authenticated"
|
| "not-authenticated"
|
||||||
@ -361,6 +363,7 @@ function App() {
|
|||||||
const [hasSettingsChanged, setHasSettingsChanged] = useRecoilState(
|
const [hasSettingsChanged, setHasSettingsChanged] = useRecoilState(
|
||||||
hasSettingsChangedAtom
|
hasSettingsChangedAtom
|
||||||
);
|
);
|
||||||
|
const balanceSetIntervalRef = useRef(null)
|
||||||
const {downloadResource} = useFetchResources()
|
const {downloadResource} = useFetchResources()
|
||||||
const holdRefExtState = useRef<extStates>("not-authenticated");
|
const holdRefExtState = useRef<extStates>("not-authenticated");
|
||||||
const isFocusedRef = useRef<boolean>(true);
|
const isFocusedRef = useRef<boolean>(true);
|
||||||
@ -396,10 +399,7 @@ function App() {
|
|||||||
message: messageQortalRequestExtension,
|
message: messageQortalRequestExtension,
|
||||||
} = useModal();
|
} = useModal();
|
||||||
|
|
||||||
const [openRegisterName, setOpenRegisterName] = useState(false);
|
|
||||||
const registerNamePopoverRef = useRef(null);
|
|
||||||
const [isLoadingRegisterName, setIsLoadingRegisterName] = useState(false);
|
|
||||||
const [registerNameValue, setRegisterNameValue] = useState("");
|
|
||||||
const [infoSnack, setInfoSnack] = useState(null);
|
const [infoSnack, setInfoSnack] = useState(null);
|
||||||
const [openSnack, setOpenSnack] = useState(false);
|
const [openSnack, setOpenSnack] = useState(false);
|
||||||
const [hasLocalNode, setHasLocalNode] = useState(false);
|
const [hasLocalNode, setHasLocalNode] = useState(false);
|
||||||
@ -702,6 +702,35 @@ function App() {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const balanceSetInterval = ()=> {
|
||||||
|
try {
|
||||||
|
if(balanceSetIntervalRef?.current){
|
||||||
|
clearInterval(balanceSetIntervalRef?.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
let isCalling = false;
|
||||||
|
balanceSetIntervalRef.current = setInterval(async () => {
|
||||||
|
if (isCalling) return;
|
||||||
|
isCalling = true;
|
||||||
|
window
|
||||||
|
.sendMessage("balance")
|
||||||
|
.then((response) => {
|
||||||
|
if (!response?.error && !isNaN(+response)) {
|
||||||
|
setBalance(response);
|
||||||
|
}
|
||||||
|
isCalling = false;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Failed to get balance:", error);
|
||||||
|
isCalling = false;
|
||||||
|
});
|
||||||
|
}, 40000);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const getBalanceFunc = () => {
|
const getBalanceFunc = () => {
|
||||||
setQortBalanceLoading(true);
|
setQortBalanceLoading(true);
|
||||||
window
|
window
|
||||||
@ -710,11 +739,14 @@ function App() {
|
|||||||
if (!response?.error && !isNaN(+response)) {
|
if (!response?.error && !isNaN(+response)) {
|
||||||
setBalance(response);
|
setBalance(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
setQortBalanceLoading(false);
|
setQortBalanceLoading(false);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error("Failed to get balance:", error);
|
console.error("Failed to get balance:", error);
|
||||||
setQortBalanceLoading(false);
|
setQortBalanceLoading(false);
|
||||||
|
}).finally(()=> {
|
||||||
|
balanceSetInterval()
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const getLtcBalanceFunc = () => {
|
const getLtcBalanceFunc = () => {
|
||||||
@ -1121,6 +1153,9 @@ function App() {
|
|||||||
setTxList([]);
|
setTxList([]);
|
||||||
setMemberGroups([]);
|
setMemberGroups([]);
|
||||||
resetAllRecoil();
|
resetAllRecoil();
|
||||||
|
if(balanceSetIntervalRef?.current){
|
||||||
|
clearInterval(balanceSetIntervalRef?.current);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function roundUpToDecimals(number, decimals = 8) {
|
function roundUpToDecimals(number, decimals = 8) {
|
||||||
@ -1275,72 +1310,7 @@ function App() {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const registerName = async () => {
|
|
||||||
try {
|
|
||||||
if (!userInfo?.address) throw new Error("Your address was not found");
|
|
||||||
if(!registerNameValue) throw new Error('Enter a name')
|
|
||||||
const fee = await getFee("REGISTER_NAME");
|
|
||||||
await show({
|
|
||||||
message: "Would you like to register this name?",
|
|
||||||
publishFee: fee.fee + " QORT",
|
|
||||||
});
|
|
||||||
setIsLoadingRegisterName(true);
|
|
||||||
new Promise((res, rej) => {
|
|
||||||
window
|
|
||||||
.sendMessage("registerName", {
|
|
||||||
name: registerNameValue,
|
|
||||||
})
|
|
||||||
.then((response) => {
|
|
||||||
if (!response?.error) {
|
|
||||||
res(response);
|
|
||||||
setIsLoadingRegisterName(false);
|
|
||||||
setInfoSnack({
|
|
||||||
type: "success",
|
|
||||||
message:
|
|
||||||
"Successfully registered. It may take a couple of minutes for the changes to propagate",
|
|
||||||
});
|
|
||||||
setOpenRegisterName(false);
|
|
||||||
setRegisterNameValue("");
|
|
||||||
setOpenSnack(true);
|
|
||||||
setTxList((prev) => [
|
|
||||||
{
|
|
||||||
...response,
|
|
||||||
type: "register-name",
|
|
||||||
label: `Registered name: awaiting confirmation. This may take a couple minutes.`,
|
|
||||||
labelDone: `Registered name: success!`,
|
|
||||||
done: false,
|
|
||||||
},
|
|
||||||
...prev.filter((item) => !item.done),
|
|
||||||
]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setInfoSnack({
|
|
||||||
type: "error",
|
|
||||||
message: response?.error,
|
|
||||||
});
|
|
||||||
setOpenSnack(true);
|
|
||||||
rej(response.error);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
setInfoSnack({
|
|
||||||
type: "error",
|
|
||||||
message: error.message || "An error occurred",
|
|
||||||
});
|
|
||||||
setOpenSnack(true);
|
|
||||||
rej(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
if (error?.message) {
|
|
||||||
setInfoSnack({
|
|
||||||
type: "error",
|
|
||||||
message: error?.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
setIsLoadingRegisterName(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderProfileLeft = ()=> {
|
const renderProfileLeft = ()=> {
|
||||||
|
|
||||||
@ -1527,7 +1497,6 @@ function App() {
|
|||||||
<Spacer height="35px" />
|
<Spacer height="35px" />
|
||||||
{userInfo && !userInfo?.name && (
|
{userInfo && !userInfo?.name && (
|
||||||
<TextP
|
<TextP
|
||||||
ref={registerNamePopoverRef}
|
|
||||||
sx={{
|
sx={{
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
lineHeight: 1.2,
|
lineHeight: 1.2,
|
||||||
@ -1539,7 +1508,7 @@ function App() {
|
|||||||
textDecoration: "underline",
|
textDecoration: "underline",
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setOpenRegisterName(true);
|
executeEvent('openRegisterName', {})
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
REGISTER NAME
|
REGISTER NAME
|
||||||
@ -3616,52 +3585,6 @@ function App() {
|
|||||||
</Box>
|
</Box>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)}
|
)}
|
||||||
<Popover
|
|
||||||
open={openRegisterName}
|
|
||||||
anchorEl={registerNamePopoverRef.current}
|
|
||||||
onClose={() => {
|
|
||||||
setOpenRegisterName(false);
|
|
||||||
setRegisterNameValue("");
|
|
||||||
}}
|
|
||||||
anchorOrigin={{
|
|
||||||
vertical: "bottom",
|
|
||||||
horizontal: "center",
|
|
||||||
}}
|
|
||||||
transformOrigin={{
|
|
||||||
vertical: "top",
|
|
||||||
horizontal: "center",
|
|
||||||
}}
|
|
||||||
style={{ marginTop: "8px" }}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "325px",
|
|
||||||
height: "250px",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: "10px",
|
|
||||||
padding: "10px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Label>Choose a name</Label>
|
|
||||||
<Input
|
|
||||||
onChange={(e) => setRegisterNameValue(e.target.value)}
|
|
||||||
value={registerNameValue}
|
|
||||||
placeholder="Choose a name"
|
|
||||||
/>
|
|
||||||
<Spacer height="25px" />
|
|
||||||
<LoadingButton
|
|
||||||
loading={isLoadingRegisterName}
|
|
||||||
loadingPosition="start"
|
|
||||||
variant="contained"
|
|
||||||
disabled={!registerNameValue}
|
|
||||||
onClick={registerName}
|
|
||||||
>
|
|
||||||
Register Name
|
|
||||||
</LoadingButton>
|
|
||||||
</Box>
|
|
||||||
</Popover>
|
|
||||||
{isSettingsOpen && (
|
{isSettingsOpen && (
|
||||||
<Settings open={isSettingsOpen} setOpen={setIsSettingsOpen} />
|
<Settings open={isSettingsOpen} setOpen={setIsSettingsOpen} />
|
||||||
)}
|
)}
|
||||||
@ -3678,6 +3601,8 @@ function App() {
|
|||||||
{renderProfileLeft()}
|
{renderProfileLeft()}
|
||||||
</DrawerComponent>
|
</DrawerComponent>
|
||||||
<UserLookup isOpenDrawerLookup={isOpenDrawerLookup} setIsOpenDrawerLookup={setIsOpenDrawerLookup} />
|
<UserLookup isOpenDrawerLookup={isOpenDrawerLookup} setIsOpenDrawerLookup={setIsOpenDrawerLookup} />
|
||||||
|
<RegisterName balance={balance} show={show} setTxList={setTxList} userInfo={userInfo} setOpenSnack={setOpenSnack} setInfoSnack={setInfoSnack}/>
|
||||||
|
<BuyQortInformation balance={balance} />
|
||||||
</GlobalContext.Provider>
|
</GlobalContext.Provider>
|
||||||
{extState === "create-wallet" && walletToBeDownloaded && (
|
{extState === "create-wallet" && walletToBeDownloaded && (
|
||||||
<ButtonBase onClick={()=> {
|
<ButtonBase onClick={()=> {
|
||||||
|
154
src/components/BuyQortInformation.tsx
Normal file
154
src/components/BuyQortInformation.tsx
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import React, { useCallback, useContext, useEffect, useState } from 'react'
|
||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
ButtonBase,
|
||||||
|
Collapse,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogContentText,
|
||||||
|
DialogTitle,
|
||||||
|
Input,
|
||||||
|
ListItem,
|
||||||
|
ListItemAvatar,
|
||||||
|
ListItemButton,
|
||||||
|
ListItemIcon,
|
||||||
|
ListItemText,
|
||||||
|
List,
|
||||||
|
MenuItem,
|
||||||
|
Popover,
|
||||||
|
Select,
|
||||||
|
TextField,
|
||||||
|
Typography,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { Label } from './Group/AddGroup';
|
||||||
|
import { Spacer } from '../common/Spacer';
|
||||||
|
import { LoadingButton } from '@mui/lab';
|
||||||
|
import { getBaseApiReact, MyContext } from '../App';
|
||||||
|
import { getFee } from '../background';
|
||||||
|
import qTradeLogo from "../assets/Icons/q-trade-logo.webp";
|
||||||
|
|
||||||
|
import RadioButtonCheckedIcon from '@mui/icons-material/RadioButtonChecked';
|
||||||
|
import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from '../utils/events';
|
||||||
|
import { BarSpinner } from '../common/Spinners/BarSpinner/BarSpinner';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const BuyQortInformation = ({balance}) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
|
||||||
|
const openBuyQortInfoFunc = useCallback((e) => {
|
||||||
|
setIsOpen(true)
|
||||||
|
|
||||||
|
}, [ setIsOpen]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
subscribeToEvent("openBuyQortInfo", openBuyQortInfoFunc);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unsubscribeFromEvent("openBuyQortInfo", openBuyQortInfoFunc);
|
||||||
|
};
|
||||||
|
}, [openBuyQortInfoFunc]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={isOpen}
|
||||||
|
aria-labelledby="alert-dialog-title"
|
||||||
|
aria-describedby="alert-dialog-description"
|
||||||
|
>
|
||||||
|
<DialogTitle id="alert-dialog-title">
|
||||||
|
{"Get QORT"}
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "400px",
|
||||||
|
maxWidth: '90vw',
|
||||||
|
height: "400px",
|
||||||
|
maxHeight: '90vh',
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "10px",
|
||||||
|
padding: "10px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography>Get QORT using Qortal's crosschain trade portal</Typography>
|
||||||
|
<ButtonBase
|
||||||
|
sx={{
|
||||||
|
"&:hover": { backgroundColor: "secondary.main" },
|
||||||
|
transition: "all 0.1s ease-in-out",
|
||||||
|
padding: "5px",
|
||||||
|
borderRadius: "5px",
|
||||||
|
gap: "5px",
|
||||||
|
}}
|
||||||
|
onClick={async () => {
|
||||||
|
executeEvent("addTab", {
|
||||||
|
data: { service: "APP", name: "q-trade" },
|
||||||
|
});
|
||||||
|
executeEvent("open-apps-mode", {});
|
||||||
|
setIsOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
style={{
|
||||||
|
borderRadius: "50%",
|
||||||
|
height: '30px'
|
||||||
|
}}
|
||||||
|
src={qTradeLogo}
|
||||||
|
/>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "1rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Trade QORT
|
||||||
|
</Typography>
|
||||||
|
</ButtonBase>
|
||||||
|
<Spacer height='40px' />
|
||||||
|
<Typography sx={{
|
||||||
|
textDecoration: 'underline'
|
||||||
|
}}>Benefits of having QORT</Typography>
|
||||||
|
<List
|
||||||
|
sx={{ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }}
|
||||||
|
aria-label="contacts"
|
||||||
|
>
|
||||||
|
<ListItem disablePadding>
|
||||||
|
|
||||||
|
<ListItemIcon>
|
||||||
|
<RadioButtonCheckedIcon sx={{
|
||||||
|
color: 'white'
|
||||||
|
}} />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="Create transactions on the Qortal Blockchain" />
|
||||||
|
|
||||||
|
</ListItem>
|
||||||
|
<ListItem disablePadding>
|
||||||
|
|
||||||
|
<ListItemIcon>
|
||||||
|
<RadioButtonCheckedIcon sx={{
|
||||||
|
color: 'white'
|
||||||
|
}} />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="Having at least 4 QORT in your balance allows you to send chat messages at near instant speed." />
|
||||||
|
|
||||||
|
</ListItem>
|
||||||
|
</List>
|
||||||
|
</Box>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => {
|
||||||
|
setIsOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
@ -2528,6 +2528,7 @@ export const Group = ({
|
|||||||
|
|
||||||
|
|
||||||
<HomeDesktop
|
<HomeDesktop
|
||||||
|
name={userInfo?.name}
|
||||||
refreshHomeDataFunc={refreshHomeDataFunc}
|
refreshHomeDataFunc={refreshHomeDataFunc}
|
||||||
myAddress={myAddress}
|
myAddress={myAddress}
|
||||||
isLoadingGroups={isLoadingGroups}
|
isLoadingGroups={isLoadingGroups}
|
||||||
|
@ -14,6 +14,7 @@ import { NewUsersCTA } from "../Home/NewUsersCTA";
|
|||||||
export const HomeDesktop = ({
|
export const HomeDesktop = ({
|
||||||
refreshHomeDataFunc,
|
refreshHomeDataFunc,
|
||||||
myAddress,
|
myAddress,
|
||||||
|
name,
|
||||||
isLoadingGroups,
|
isLoadingGroups,
|
||||||
balance,
|
balance,
|
||||||
userInfo,
|
userInfo,
|
||||||
@ -27,6 +28,31 @@ export const HomeDesktop = ({
|
|||||||
setDesktopViewMode,
|
setDesktopViewMode,
|
||||||
desktopViewMode,
|
desktopViewMode,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [checked1, setChecked1] = React.useState(false);
|
||||||
|
const [checked2, setChecked2] = React.useState(false);
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (balance && +balance >= 6) {
|
||||||
|
setChecked1(true);
|
||||||
|
}
|
||||||
|
}, [balance]);
|
||||||
|
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (name) setChecked2(true);
|
||||||
|
}, [name]);
|
||||||
|
|
||||||
|
|
||||||
|
const isLoaded = React.useMemo(()=> {
|
||||||
|
if(userInfo !== null) return true
|
||||||
|
return false
|
||||||
|
}, [ userInfo])
|
||||||
|
|
||||||
|
const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(()=> {
|
||||||
|
if(isLoaded && checked1 && checked2) return true
|
||||||
|
return false
|
||||||
|
}, [checked1, isLoaded, checked2])
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -114,7 +140,9 @@ export const HomeDesktop = ({
|
|||||||
}}>
|
}}>
|
||||||
<ListOfThreadPostsWatched />
|
<ListOfThreadPostsWatched />
|
||||||
</Box> */}
|
</Box> */}
|
||||||
<Box
|
{hasDoneNameAndBalanceAndIsLoaded && (
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "330px",
|
width: "330px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -150,6 +178,9 @@ export const HomeDesktop = ({
|
|||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<QortPrice />
|
<QortPrice />
|
||||||
</Box>
|
</Box>
|
||||||
@ -185,6 +216,9 @@ export const HomeDesktop = ({
|
|||||||
</Typography>{" "}
|
</Typography>{" "}
|
||||||
</Box>
|
</Box>
|
||||||
</Divider>
|
</Divider>
|
||||||
|
{!hasDoneNameAndBalanceAndIsLoaded && (
|
||||||
|
<Spacer height="40px" />
|
||||||
|
)}
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -194,9 +228,14 @@ export const HomeDesktop = ({
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ListOfGroupPromotions />
|
{hasDoneNameAndBalanceAndIsLoaded && (
|
||||||
|
<ListOfGroupPromotions />
|
||||||
|
|
||||||
|
)}
|
||||||
|
|
||||||
<Explore setDesktopViewMode={setDesktopViewMode} />
|
<Explore setDesktopViewMode={setDesktopViewMode} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<NewUsersCTA balance={balance} />
|
<NewUsersCTA balance={balance} />
|
||||||
</>
|
</>
|
||||||
|
|
||||||
|
@ -12,27 +12,17 @@ import { Box, Typography } from "@mui/material";
|
|||||||
import { Spacer } from "../../common/Spacer";
|
import { Spacer } from "../../common/Spacer";
|
||||||
import { isMobile } from "../../App";
|
import { isMobile } from "../../App";
|
||||||
import { QMailMessages } from "./QMailMessages";
|
import { QMailMessages } from "./QMailMessages";
|
||||||
|
import { executeEvent } from "../../utils/events";
|
||||||
|
|
||||||
export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance, userInfo }) => {
|
export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance, userInfo }) => {
|
||||||
const [checked1, setChecked1] = React.useState(false);
|
const [checked1, setChecked1] = React.useState(false);
|
||||||
const [checked2, setChecked2] = React.useState(false);
|
const [checked2, setChecked2] = React.useState(false);
|
||||||
const [checked3, setChecked3] = React.useState(false);
|
// const [checked3, setChecked3] = React.useState(false);
|
||||||
|
|
||||||
// const getAddressInfo = async (address) => {
|
// React.useEffect(() => {
|
||||||
// const response = await fetch(getBaseApiReact() + "/addresses/" + address);
|
// if (hasGroups) setChecked3(true);
|
||||||
// const data = await response.json();
|
// }, [hasGroups]);
|
||||||
// if (data.error && data.error === 124) {
|
|
||||||
// setChecked1(false);
|
|
||||||
// } else if (data.address) {
|
|
||||||
// setChecked1(true);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const checkInfo = async () => {
|
|
||||||
// try {
|
|
||||||
// getAddressInfo(myAddress);
|
|
||||||
// } catch (error) {}
|
|
||||||
// };
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (balance && +balance >= 6) {
|
if (balance && +balance >= 6) {
|
||||||
@ -40,9 +30,6 @@ export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance, userInf
|
|||||||
}
|
}
|
||||||
}, [balance]);
|
}, [balance]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (hasGroups) setChecked3(true);
|
|
||||||
}, [hasGroups]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (name) setChecked2(true);
|
if (name) setChecked2(true);
|
||||||
@ -120,11 +107,14 @@ if(!isLoaded) return null
|
|||||||
disableRipple
|
disableRipple
|
||||||
role={undefined}
|
role={undefined}
|
||||||
dense
|
dense
|
||||||
|
onClick={()=> {
|
||||||
|
executeEvent("openBuyQortInfo", {})
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
sx={{
|
sx={{
|
||||||
"& .MuiTypography-root": {
|
"& .MuiTypography-root": {
|
||||||
fontSize: "13px",
|
fontSize: "1rem",
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
@ -181,9 +171,11 @@ if(!isLoaded) return null
|
|||||||
padding: "0px",
|
padding: "0px",
|
||||||
}} disableRipple role={undefined} dense>
|
}} disableRipple role={undefined} dense>
|
||||||
|
|
||||||
<ListItemText sx={{
|
<ListItemText onClick={() => {
|
||||||
|
executeEvent('openRegisterName', {})
|
||||||
|
}} sx={{
|
||||||
"& .MuiTypography-root": {
|
"& .MuiTypography-root": {
|
||||||
fontSize: "13px",
|
fontSize: "1rem",
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
},
|
},
|
||||||
}} primary={`Register a name`} />
|
}} primary={`Register a name`} />
|
||||||
@ -202,16 +194,7 @@ if(!isLoaded) return null
|
|||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem
|
{/* <ListItem
|
||||||
// secondaryAction={
|
|
||||||
// <IconButton edge="end" aria-label="comments">
|
|
||||||
// <InfoIcon
|
|
||||||
// sx={{
|
|
||||||
// color: "white",
|
|
||||||
// }}
|
|
||||||
// />
|
|
||||||
// </IconButton>
|
|
||||||
// }
|
|
||||||
disablePadding
|
disablePadding
|
||||||
>
|
>
|
||||||
<ListItemButton sx={{
|
<ListItemButton sx={{
|
||||||
@ -238,7 +221,7 @@ if(!isLoaded) return null
|
|||||||
/>
|
/>
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem> */}
|
||||||
</List>
|
</List>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
312
src/components/RegisterName.tsx
Normal file
312
src/components/RegisterName.tsx
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
import React, { useCallback, useContext, useEffect, useState } from 'react'
|
||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
ButtonBase,
|
||||||
|
Collapse,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogContentText,
|
||||||
|
DialogTitle,
|
||||||
|
Input,
|
||||||
|
ListItem,
|
||||||
|
ListItemAvatar,
|
||||||
|
ListItemButton,
|
||||||
|
ListItemIcon,
|
||||||
|
ListItemText,
|
||||||
|
List,
|
||||||
|
MenuItem,
|
||||||
|
Popover,
|
||||||
|
Select,
|
||||||
|
TextField,
|
||||||
|
Typography,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { Label } from './Group/AddGroup';
|
||||||
|
import { Spacer } from '../common/Spacer';
|
||||||
|
import { LoadingButton } from '@mui/lab';
|
||||||
|
import { getBaseApiReact, MyContext } from '../App';
|
||||||
|
import { getFee } from '../background';
|
||||||
|
import RadioButtonCheckedIcon from '@mui/icons-material/RadioButtonChecked';
|
||||||
|
import { subscribeToEvent, unsubscribeFromEvent } from '../utils/events';
|
||||||
|
import { BarSpinner } from '../common/Spinners/BarSpinner/BarSpinner';
|
||||||
|
import CheckIcon from '@mui/icons-material/Check';
|
||||||
|
import ErrorIcon from '@mui/icons-material/Error';
|
||||||
|
|
||||||
|
enum Availability {
|
||||||
|
NULL = 'null',
|
||||||
|
LOADING = 'loading',
|
||||||
|
AVAILABLE = 'available',
|
||||||
|
NOT_AVAILABLE = 'not-available'
|
||||||
|
}
|
||||||
|
export const RegisterName = ({setOpenSnack, setInfoSnack, userInfo, show, setTxList, balance}) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
const [registerNameValue, setRegisterNameValue] = useState('')
|
||||||
|
const [isLoadingRegisterName, setIsLoadingRegisterName] = useState(false)
|
||||||
|
const [isNameAvailable, setIsNameAvailable] = useState<Availability>(Availability.NULL)
|
||||||
|
const [nameFee, setNameFee] = useState(null)
|
||||||
|
|
||||||
|
const checkIfNameExisits = async (name)=> {
|
||||||
|
if(!name?.trim()){
|
||||||
|
setIsNameAvailable(Availability.NULL)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setIsNameAvailable(Availability.LOADING)
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${getBaseApiReact()}/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 {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Debounce logic
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = setTimeout(() => {
|
||||||
|
checkIfNameExisits(registerNameValue);
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
// Cleanup timeout if searchValue changes before the timeout completes
|
||||||
|
return () => {
|
||||||
|
clearTimeout(handler);
|
||||||
|
};
|
||||||
|
}, [registerNameValue]);
|
||||||
|
|
||||||
|
const openRegisterNameFunc = useCallback((e) => {
|
||||||
|
setIsOpen(true)
|
||||||
|
|
||||||
|
}, [ setIsOpen]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
subscribeToEvent("openRegisterName", openRegisterNameFunc);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unsubscribeFromEvent("openRegisterName", openRegisterNameFunc);
|
||||||
|
};
|
||||||
|
}, [openRegisterNameFunc]);
|
||||||
|
|
||||||
|
useEffect(()=> {
|
||||||
|
const nameRegistrationFee = async ()=> {
|
||||||
|
try {
|
||||||
|
const fee = await getFee("REGISTER_NAME");
|
||||||
|
setNameFee(fee?.fee)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nameRegistrationFee()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const registerName = async () => {
|
||||||
|
try {
|
||||||
|
if (!userInfo?.address) throw new Error("Your address was not found");
|
||||||
|
if(!registerNameValue) throw new Error('Enter a name')
|
||||||
|
|
||||||
|
const fee = await getFee("REGISTER_NAME");
|
||||||
|
await show({
|
||||||
|
message: "Would you like to register this name?",
|
||||||
|
publishFee: fee.fee + " QORT",
|
||||||
|
});
|
||||||
|
setIsLoadingRegisterName(true);
|
||||||
|
new Promise((res, rej) => {
|
||||||
|
window
|
||||||
|
.sendMessage("registerName", {
|
||||||
|
name: registerNameValue,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (!response?.error) {
|
||||||
|
res(response);
|
||||||
|
setIsLoadingRegisterName(false);
|
||||||
|
setInfoSnack({
|
||||||
|
type: "success",
|
||||||
|
message:
|
||||||
|
"Successfully registered. It may take a couple of minutes for the changes to propagate",
|
||||||
|
});
|
||||||
|
setIsOpen(false);
|
||||||
|
setRegisterNameValue("");
|
||||||
|
setOpenSnack(true);
|
||||||
|
setTxList((prev) => [
|
||||||
|
{
|
||||||
|
...response,
|
||||||
|
type: "register-name",
|
||||||
|
label: `Registered name: awaiting confirmation. This may take a couple minutes.`,
|
||||||
|
labelDone: `Registered name: success!`,
|
||||||
|
done: false,
|
||||||
|
},
|
||||||
|
...prev.filter((item) => !item.done),
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setInfoSnack({
|
||||||
|
type: "error",
|
||||||
|
message: response?.error,
|
||||||
|
});
|
||||||
|
setOpenSnack(true);
|
||||||
|
rej(response.error);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setInfoSnack({
|
||||||
|
type: "error",
|
||||||
|
message: error.message || "An error occurred",
|
||||||
|
});
|
||||||
|
setOpenSnack(true);
|
||||||
|
rej(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.message) {
|
||||||
|
setOpenSnack(true)
|
||||||
|
setInfoSnack({
|
||||||
|
type: "error",
|
||||||
|
message: error?.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setIsLoadingRegisterName(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={isOpen}
|
||||||
|
aria-labelledby="alert-dialog-title"
|
||||||
|
aria-describedby="alert-dialog-description"
|
||||||
|
>
|
||||||
|
<DialogTitle id="alert-dialog-title">
|
||||||
|
{"Register name"}
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "400px",
|
||||||
|
maxWidth: '90vw',
|
||||||
|
height: "500px",
|
||||||
|
maxHeight: '90vh',
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "10px",
|
||||||
|
padding: "10px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Label>Choose a name</Label>
|
||||||
|
<TextField
|
||||||
|
autoComplete='off'
|
||||||
|
autoFocus
|
||||||
|
onChange={(e) => setRegisterNameValue(e.target.value)}
|
||||||
|
value={registerNameValue}
|
||||||
|
placeholder="Choose a name"
|
||||||
|
/>
|
||||||
|
{(!balance || (nameFee && balance && balance < nameFee))&& (
|
||||||
|
<>
|
||||||
|
<Spacer height="10px" />
|
||||||
|
<Box sx={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: '5px',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}>
|
||||||
|
<ErrorIcon sx={{
|
||||||
|
color: 'white'
|
||||||
|
}} />
|
||||||
|
<Typography>Your balance is {balance ?? 0} QORT. A name registration requires a {nameFee} QORT fee</Typography>
|
||||||
|
</Box>
|
||||||
|
<Spacer height="10px" />
|
||||||
|
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<Spacer height="5px" />
|
||||||
|
{isNameAvailable === Availability.AVAILABLE && (
|
||||||
|
<Box sx={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: '5px',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}>
|
||||||
|
<CheckIcon sx={{
|
||||||
|
color: 'white'
|
||||||
|
}} />
|
||||||
|
<Typography>{registerNameValue} is available</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{isNameAvailable === Availability.NOT_AVAILABLE && (
|
||||||
|
<Box sx={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: '5px',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}>
|
||||||
|
<ErrorIcon sx={{
|
||||||
|
color: 'white'
|
||||||
|
}} />
|
||||||
|
<Typography>{registerNameValue} is unavailable</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{isNameAvailable === Availability.LOADING && (
|
||||||
|
<Box sx={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: '5px',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}>
|
||||||
|
<BarSpinner width="16px" color="white" />
|
||||||
|
<Typography>Checking if name already existis</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
<Spacer height="25px" />
|
||||||
|
<Typography sx={{
|
||||||
|
textDecoration: 'underline'
|
||||||
|
}}>Benefits of a name</Typography>
|
||||||
|
<List
|
||||||
|
sx={{ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }}
|
||||||
|
aria-label="contacts"
|
||||||
|
>
|
||||||
|
<ListItem disablePadding>
|
||||||
|
|
||||||
|
<ListItemIcon>
|
||||||
|
<RadioButtonCheckedIcon sx={{
|
||||||
|
color: 'white'
|
||||||
|
}} />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="Publish data to Qortal: anything from apps to videos. Fully decentralized!" />
|
||||||
|
|
||||||
|
</ListItem>
|
||||||
|
<ListItem disablePadding>
|
||||||
|
|
||||||
|
<ListItemIcon>
|
||||||
|
<RadioButtonCheckedIcon sx={{
|
||||||
|
color: 'white'
|
||||||
|
}} />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="Secure ownership of data published by your name. You can even sell your name, along with your data to a third party." />
|
||||||
|
|
||||||
|
</ListItem>
|
||||||
|
</List>
|
||||||
|
</Box>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
disabled={isLoadingRegisterName}
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => {
|
||||||
|
setIsOpen(false)
|
||||||
|
setRegisterNameValue('')
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
disabled={!registerNameValue.trim() ||isLoadingRegisterName || isNameAvailable !== Availability.AVAILABLE || !balance || ((balance && nameFee) && +balance < +nameFee)}
|
||||||
|
variant="contained"
|
||||||
|
onClick={registerName}
|
||||||
|
autoFocus
|
||||||
|
>
|
||||||
|
Register Name
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
@ -24,6 +24,10 @@ const theme = createTheme({
|
|||||||
secondary: '#b0b0b0', // Light gray for secondary text
|
secondary: '#b0b0b0', // Light gray for secondary text
|
||||||
disabled: '#808080', // Gray for disabled text
|
disabled: '#808080', // Gray for disabled text
|
||||||
},
|
},
|
||||||
|
action: {
|
||||||
|
// disabledBackground: 'set color of background here',
|
||||||
|
disabled: 'rgb(255 255 255 / 70%)'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
typography: {
|
typography: {
|
||||||
fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif', // Font family
|
fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif', // Font family
|
||||||
|
Loading…
x
Reference in New Issue
Block a user