mirror of
https://github.com/Qortal/Q-Wallets.git
synced 2025-11-03 11:37:04 +00:00
Update wallets
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" type="image/png" href="/ico.png" />
|
||||
<link rel="icon" type="image/png" href="/qw-ico.png" />
|
||||
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
||||
<title>Qortal Wallets App</title>
|
||||
</head>
|
||||
|
||||
26
package-lock.json
generated
26
package-lock.json
generated
@@ -22,7 +22,8 @@
|
||||
"react-number-format": "^5.4.3",
|
||||
"react-qr-code": "^2.0.15",
|
||||
"react-router": "^7.2.0",
|
||||
"react-router-dom": "^7.2.0"
|
||||
"react-router-dom": "^7.2.0",
|
||||
"react-window": "^1.8.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.0.10",
|
||||
@@ -2384,6 +2385,12 @@
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/memoize-one": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
||||
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/merge-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||
@@ -2767,6 +2774,23 @@
|
||||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-window": {
|
||||
"version": "1.8.11",
|
||||
"resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.11.tgz",
|
||||
"integrity": "sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"memoize-one": ">=3.1.1 <6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
"react-number-format": "^5.4.3",
|
||||
"react-qr-code": "^2.0.15",
|
||||
"react-router": "^7.2.0",
|
||||
"react-router-dom": "^7.2.0"
|
||||
"react-router-dom": "^7.2.0",
|
||||
"react-window": "^1.8.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.0.10",
|
||||
@@ -32,4 +33,4 @@
|
||||
"typescript": "^5.7.3",
|
||||
"vite": "^6.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
36
src/App.tsx
36
src/App.tsx
@@ -1,10 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import packageJson from '../package.json';
|
||||
import { Container, Typography } from "@mui/material";
|
||||
import { createTheme } from '@mui/material/styles';
|
||||
import { Session, Navigation } from '@toolpad/core/AppProvider';
|
||||
import { ReactRouterAppProvider } from '@toolpad/core/react-router';
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
import { DashboardLayout } from '@toolpad/core/DashboardLayout';
|
||||
import { DashboardLayout, type SidebarFooterProps } from '@toolpad/core/DashboardLayout';
|
||||
import WalletContext, { IContextProps, UserNameAvatar } from './contexts/walletContext';
|
||||
import qort from "./assets/qort.png";
|
||||
import btc from "./assets/btc.png";
|
||||
@@ -223,24 +224,37 @@ function App() {
|
||||
},
|
||||
Pirate,
|
||||
];
|
||||
|
||||
|
||||
function SidebarFooter({ mini }: SidebarFooterProps) {
|
||||
return (
|
||||
<Typography
|
||||
variant="caption"
|
||||
sx={{ m: 1, whiteSpace: 'nowrap', overflow: 'hidden' }}
|
||||
>
|
||||
{mini ? `v${packageJson.version}` : `© ${new Date().getFullYear()} Qortal Wallets App v${packageJson.version}`}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
const BRANDING = {
|
||||
logo: <img src={qwallets} alt="MWA Logo" />,
|
||||
title: <Typography>
|
||||
<span style={{ color: '#60d0fd', fontSize: "24px", fontWeight: 700 }}>QORTAL </span>
|
||||
<span style={{ color: '#05a2e4', fontSize: "24px", fontWeight: 700 }}>WALLETS </span>
|
||||
<span style={{ color: '#02648d', fontSize: "24px", fontWeight: 700 }}>APP</span>
|
||||
</Typography>
|
||||
}
|
||||
|
||||
return (
|
||||
<ReactRouterAppProvider
|
||||
session={session}
|
||||
authentication={authentication}
|
||||
navigation={NAVIGATION}
|
||||
branding={{
|
||||
logo: <img src={qwallets} alt="MWA Logo" />,
|
||||
title: (<Typography>
|
||||
<span style={{ color: '#60d0fd', fontSize: "24px", fontWeight: 700 }}>QORTAL </span>
|
||||
<span style={{ color: '#05a2e4', fontSize: "24px", fontWeight: 700 }}>WALLETS </span>
|
||||
<span style={{ color: '#02648d', fontSize: "24px", fontWeight: 700 }}>APP</span>
|
||||
</Typography>)
|
||||
}}
|
||||
branding={BRANDING}
|
||||
theme={walletTheme}
|
||||
>
|
||||
<WalletContext.Provider value={walletContextValue}>
|
||||
<DashboardLayout defaultSidebarCollapsed>
|
||||
<DashboardLayout defaultSidebarCollapsed slots={{ sidebarFooter: SidebarFooter }}>
|
||||
<Container sx={{ maxWidth: '100%' }} maxWidth={false}>
|
||||
<Routes>
|
||||
<Route path="/" element={<WelcomePage />} />
|
||||
|
||||
6
src/global.d.ts
vendored
6
src/global.d.ts
vendored
@@ -29,7 +29,9 @@ interface QortalRequestOptions {
|
||||
tag5?: string;
|
||||
coin?: string;
|
||||
destinationAddress?: string;
|
||||
amount?: number;
|
||||
amount?: number | Number;
|
||||
recipient?: string;
|
||||
fee?: number | any;
|
||||
blob?: Blob;
|
||||
mimeType?: string;
|
||||
file?: File;
|
||||
@@ -40,6 +42,8 @@ interface QortalRequestOptions {
|
||||
exactMatchNames?: boolean;
|
||||
creationBytes?: string;
|
||||
type?: string;
|
||||
host?: string;
|
||||
port?: number;
|
||||
assetId?: number;
|
||||
txType?: TransactionType[];
|
||||
confirmationStatus?: string;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -15,8 +15,10 @@ import {
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
IconButton,
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
Paper,
|
||||
Slide,
|
||||
Slider,
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -34,6 +36,8 @@ import {
|
||||
} from '@mui/material';
|
||||
import { NumericFormat } from 'react-number-format';
|
||||
import TableCell, { tableCellClasses } from '@mui/material/TableCell';
|
||||
import Snackbar, { SnackbarCloseReason } from '@mui/material/Snackbar';
|
||||
import Slide, { SlideProps } from '@mui/material/Slide';
|
||||
import { TransitionProps } from '@mui/material/transitions';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import LinearProgress from '@mui/material/LinearProgress';
|
||||
@@ -46,7 +50,9 @@ import {
|
||||
LastPage,
|
||||
CopyAllTwoTone,
|
||||
Close,
|
||||
Send
|
||||
Send,
|
||||
Refresh,
|
||||
PublishedWithChangesTwoTone
|
||||
} from '@mui/icons-material';
|
||||
import coinLogoDOGE from '../../assets/doge.png';
|
||||
|
||||
@@ -125,6 +131,10 @@ const Transition = React.forwardRef(function Transition(
|
||||
return <Slide direction="up" ref={ref} {...props} />;
|
||||
});
|
||||
|
||||
function SlideTransition(props: SlideProps) {
|
||||
return <Slide {...props} direction="up" />;
|
||||
}
|
||||
|
||||
const DogeQrDialog = styled(Dialog)(({ theme }) => ({
|
||||
'& .MuiDialogContent-root': {
|
||||
padding: theme.spacing(2),
|
||||
@@ -132,6 +142,33 @@ const DogeQrDialog = styled(Dialog)(({ theme }) => ({
|
||||
'& .MuiDialogActions-root': {
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
"& .MuiDialog-paper": {
|
||||
borderRadius: "15px",
|
||||
},
|
||||
}));
|
||||
|
||||
const DogeElectrumDialog = styled(Dialog)(({ theme }) => ({
|
||||
'& .MuiDialogContent-root': {
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
'& .MuiDialogActions-root': {
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
"& .MuiDialog-paper": {
|
||||
borderRadius: "15px",
|
||||
},
|
||||
}));
|
||||
|
||||
const DogeSubmittDialog = styled(Dialog)(({ theme }) => ({
|
||||
'& .MuiDialogContent-root': {
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
'& .MuiDialogActions-root': {
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
"& .MuiDialog-paper": {
|
||||
borderRadius: "15px",
|
||||
},
|
||||
}));
|
||||
|
||||
const CustomWidthTooltip = styled(({ className, ...props }: TooltipProps) => (
|
||||
@@ -223,6 +260,8 @@ export default function DogecoinWallet() {
|
||||
const [walletInfoDoge, setWalletInfoDoge] = React.useState<any>({});
|
||||
const [walletBalanceDoge, setWalletBalanceDoge] = React.useState<any>(null);
|
||||
const [isLoadingWalletBalanceDoge, setIsLoadingWalletBalanceDoge] = React.useState<boolean>(true);
|
||||
const [allElectrumServersDoge, setAllElectrumServersDoge] = React.useState<any>([]);
|
||||
const [currentElectrumServerDoge, setCurrentElectrumServerDoge] = React.useState<any>([]);
|
||||
const [allWalletAddressesDoge, setAllWalletAddressesDoge] = React.useState<any>([]);
|
||||
const [transactionsDoge, setTransactionsDoge] = React.useState<any>([]);
|
||||
const [isLoadingDogeTransactions, setIsLoadingDogeTransactions] = React.useState<boolean>(true);
|
||||
@@ -231,9 +270,15 @@ export default function DogecoinWallet() {
|
||||
const [copyDogeAddress, setCopyDogeAddress] = React.useState('');
|
||||
const [copyDogeTxHash, setCopyDogeTxHash] = React.useState('');
|
||||
const [openDogeQR, setOpenDogeQR] = React.useState(false);
|
||||
const [openDogeElectrum, setOpenDogeElectrum] = React.useState(false);
|
||||
const [openDogeSend, setOpenDogeSend] = React.useState(false);
|
||||
const [amountDoge, setAmountDoge] = React.useState<number>(0);
|
||||
const [feeDoge, setFeeDoge] = React.useState<number>(0);
|
||||
const [dogeAmount, setDogeAmount] = React.useState<number>(0);
|
||||
const [dogeRecipient, setDogeRecipient] = React.useState('');
|
||||
const [dogeFee, setDogeFee] = React.useState<number>(0);
|
||||
const [loadingRefreshDoge, setLoadingRefreshDoge] = React.useState(false);
|
||||
const [openTxDogeSubmit, setOpenTxDogeSubmit] = React.useState(false);
|
||||
const [openSendDogeSuccess, setOpenSendDogeSuccess] = React.useState(false);
|
||||
const [openSendDogeeError, setOpenSendDogeError] = React.useState(false);
|
||||
|
||||
const emptyRows = page > 0 ? Math.max(0, (1 + page) * rowsPerPage - transactionsDoge.length) : 0;
|
||||
|
||||
@@ -245,15 +290,30 @@ export default function DogecoinWallet() {
|
||||
setOpenDogeQR(false);
|
||||
}
|
||||
|
||||
const handleCloseDogeElectrum = () => {
|
||||
setOpenDogeElectrum(false);
|
||||
}
|
||||
|
||||
const handleOpenDogeSend = () => {
|
||||
setAmountDoge(0);
|
||||
setFeeDoge(1000);
|
||||
setDogeAmount(0);
|
||||
setDogeRecipient('');
|
||||
setDogeFee(1000);
|
||||
setOpenDogeSend(true);
|
||||
}
|
||||
|
||||
const validateCanSendDoge = () => {
|
||||
if (dogeAmount <= 0 || null || !dogeAmount) {
|
||||
return true;
|
||||
}
|
||||
if (dogeRecipient.length < 34 || '') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const handleCloseDogeSend = () => {
|
||||
setAmountDoge(0);
|
||||
setFeeDoge(0);
|
||||
setDogeAmount(0);
|
||||
setDogeFee(0);
|
||||
setOpenDogeSend(false);
|
||||
}
|
||||
|
||||
@@ -269,7 +329,7 @@ export default function DogecoinWallet() {
|
||||
setCopyDogeTxHash('');
|
||||
}
|
||||
|
||||
const handleChangePage = (event: React.MouseEvent<HTMLButtonElement> | null, newPage: number,) => {
|
||||
const handleChangePage = (_event: React.MouseEvent<HTMLButtonElement> | null, newPage: number,) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
@@ -278,13 +338,29 @@ export default function DogecoinWallet() {
|
||||
setPage(0);
|
||||
};
|
||||
|
||||
const handleChangeDogeAmount = (_: Event, newValue: number | number[]) => {
|
||||
setAmountDoge(newValue as number);
|
||||
console.log("AMOUNT DOGE", amountDoge)
|
||||
const handleChangeDogeFee = (_: Event, newValue: number | number[]) => {
|
||||
setDogeFee(newValue as number);
|
||||
setDogeAmount(0);
|
||||
};
|
||||
|
||||
const handleChangeDogeFee = (_: Event, newValue: number | number[]) => {
|
||||
setFeeDoge(newValue as number);
|
||||
const handleCloseSendDogeSuccess = (
|
||||
_event?: React.SyntheticEvent | Event,
|
||||
reason?: SnackbarCloseReason,
|
||||
) => {
|
||||
if (reason === 'clickaway') {
|
||||
return;
|
||||
}
|
||||
setOpenSendDogeSuccess(false);
|
||||
};
|
||||
|
||||
const handleCloseSendDogeError = (
|
||||
_event?: React.SyntheticEvent | Event,
|
||||
reason?: SnackbarCloseReason,
|
||||
) => {
|
||||
if (reason === 'clickaway') {
|
||||
return;
|
||||
}
|
||||
setOpenSendDogeError(false);
|
||||
};
|
||||
|
||||
const getWalletInfoDoge = async () => {
|
||||
@@ -337,6 +413,37 @@ export default function DogecoinWallet() {
|
||||
}
|
||||
}, [isAuthenticated]);
|
||||
|
||||
const getElectrumServersDoge = async () => {
|
||||
try {
|
||||
const response = await qortalRequest({
|
||||
action: "GET_CROSSCHAIN_SERVER_INFO",
|
||||
coin: "DOGE"
|
||||
});
|
||||
if (!response?.error) {
|
||||
setAllElectrumServersDoge(response);
|
||||
let currentDogeServer = response.filter(function (item: { isCurrent: boolean; }) {
|
||||
return item.isCurrent === true;
|
||||
});
|
||||
setCurrentElectrumServerDoge(currentDogeServer);
|
||||
console.log("GET DOGE SERVERS INFO", response);
|
||||
console.log("CURRENT DOGE SERVER INFO", currentDogeServer);
|
||||
}
|
||||
} catch (error) {
|
||||
setAllElectrumServersDoge({});
|
||||
console.log("ERROR GET DOGE SERVERS INFO", error);
|
||||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isAuthenticated) return;
|
||||
getElectrumServersDoge();
|
||||
}, [isAuthenticated]);
|
||||
|
||||
const handleOpenDogeElectrum = async () => {
|
||||
await getElectrumServersDoge();
|
||||
setOpenDogeElectrum(true);
|
||||
}
|
||||
|
||||
const getTransactionsDoge = async () => {
|
||||
try {
|
||||
setIsLoadingDogeTransactions(true);
|
||||
@@ -376,6 +483,21 @@ export default function DogecoinWallet() {
|
||||
getTransactionsDoge();
|
||||
}, [isAuthenticated]);
|
||||
|
||||
const handleLoadingRefreshDoge = async () => {
|
||||
setLoadingRefreshDoge(true);
|
||||
await getTransactionsDoge();
|
||||
setLoadingRefreshDoge(false);
|
||||
}
|
||||
|
||||
const handleSendMaxDoge = () => {
|
||||
const maxDogeAmount = parseFloat((walletBalanceDoge - ((dogeFee * 1000) / 1e8)).toFixed(8));
|
||||
if (maxDogeAmount <= 0) {
|
||||
setDogeAmount(0);
|
||||
} else {
|
||||
setDogeAmount(maxDogeAmount);
|
||||
}
|
||||
}
|
||||
|
||||
const DogeQrDialogPage = () => {
|
||||
return (
|
||||
<DogeQrDialog
|
||||
@@ -407,6 +529,44 @@ export default function DogecoinWallet() {
|
||||
);
|
||||
}
|
||||
|
||||
const sendDogeRequest = async () => {
|
||||
setOpenTxDogeSubmit(true);
|
||||
const dogeFeeCalculated = Number(dogeFee / 1e8).toFixed(8);
|
||||
console.log("RECIPIENT", dogeRecipient);
|
||||
console.log("AMOUNT", dogeAmount);
|
||||
console.log("FEE", dogeFeeCalculated);
|
||||
try {
|
||||
const sendRequest = await qortalRequest({
|
||||
action: "SEND_COIN",
|
||||
coin: "DOGE",
|
||||
recipient: dogeRecipient,
|
||||
amount: dogeAmount,
|
||||
fee: dogeFeeCalculated
|
||||
});
|
||||
if (!sendRequest?.error) {
|
||||
setDogeAmount(0);
|
||||
setDogeRecipient('');
|
||||
setDogeFee(1000);
|
||||
setOpenTxDogeSubmit(false);
|
||||
setOpenSendDogeSuccess(true);
|
||||
setIsLoadingWalletBalanceDoge(true);
|
||||
await timeoutDelay(3000);
|
||||
getWalletBalanceDoge();
|
||||
console.log("DOGE SENDED", sendRequest);
|
||||
}
|
||||
} catch (error) {
|
||||
setDogeAmount(0);
|
||||
setDogeRecipient('');
|
||||
setDogeFee(1000);
|
||||
setOpenTxDogeSubmit(false);
|
||||
setOpenSendDogeError(true);
|
||||
setIsLoadingWalletBalanceDoge(true);
|
||||
await timeoutDelay(3000);
|
||||
getWalletBalanceDoge();
|
||||
console.log("ERROR SENDING DOGE", error);
|
||||
}
|
||||
}
|
||||
|
||||
const DogeSendDialogPage = () => {
|
||||
return (
|
||||
<Dialog
|
||||
@@ -415,6 +575,58 @@ export default function DogecoinWallet() {
|
||||
onClose={handleCloseDogeSend}
|
||||
slots={{ transition: Transition }}
|
||||
>
|
||||
<DogeSubmittDialog
|
||||
fullWidth={true}
|
||||
maxWidth='xs'
|
||||
open={openTxDogeSubmit}
|
||||
>
|
||||
<DialogContent>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', flexWrap: 'wrap' }}>
|
||||
<div style={{
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<CircularProgress color="success" size={64} />
|
||||
</div>
|
||||
<div style={{
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
marginTop: '20px'
|
||||
}}>
|
||||
<Typography variant="h6" sx={{ color: 'primary.main', fontStyle: 'italic', fontWeight: 700 }}>
|
||||
Processing Transaction Please Wait...
|
||||
</Typography>
|
||||
</div>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
</DogeSubmittDialog>
|
||||
<Snackbar
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
||||
open={openSendDogeSuccess}
|
||||
autoHideDuration={4000}
|
||||
slots={{ transition: SlideTransition }}
|
||||
onClose={handleCloseSendDogeSuccess}>
|
||||
<Alert
|
||||
onClose={handleCloseSendDogeSuccess}
|
||||
severity="success"
|
||||
variant="filled"
|
||||
sx={{ width: '100%' }}
|
||||
>
|
||||
Sent DOGE transaction was successful!
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
<Snackbar open={openSendDogeeError} autoHideDuration={4000} onClose={handleCloseSendDogeError}>
|
||||
<Alert
|
||||
onClose={handleCloseSendDogeError}
|
||||
severity="error"
|
||||
variant="filled"
|
||||
sx={{ width: '100%' }}
|
||||
>
|
||||
Something went wrong, please try again!
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
<AppBar sx={{ position: 'static' }}>
|
||||
<Toolbar>
|
||||
<IconButton
|
||||
@@ -437,10 +649,11 @@ export default function DogecoinWallet() {
|
||||
Transfer DOGE
|
||||
</Typography>
|
||||
<Button
|
||||
disabled={validateCanSendDoge()}
|
||||
variant="contained"
|
||||
startIcon={<Send />}
|
||||
aria-label="send"
|
||||
onClick={handleOpenDogeSend}
|
||||
aria-label="send-doge"
|
||||
onClick={sendDogeRequest}
|
||||
sx={{ backgroundColor: "#05a2e4", color: "white", "&:hover": { backgroundColor: "#02648d", } }}
|
||||
>
|
||||
SEND
|
||||
@@ -468,7 +681,7 @@ export default function DogecoinWallet() {
|
||||
gutterBottom
|
||||
sx={{ color: 'text.primary', fontWeight: 700 }}
|
||||
>
|
||||
{walletBalanceDoge + " DOGE"}
|
||||
{isLoadingWalletBalanceDoge ? <Box sx={{ width: '175px' }}><LinearProgress /></Box> : walletBalanceDoge + " DOGE"}
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={{
|
||||
@@ -481,7 +694,6 @@ export default function DogecoinWallet() {
|
||||
<Typography
|
||||
variant="h5"
|
||||
align="center"
|
||||
gutterBottom
|
||||
sx={{ color: 'primary.main', fontWeight: 700 }}
|
||||
>
|
||||
Max Sendable:
|
||||
@@ -489,11 +701,27 @@ export default function DogecoinWallet() {
|
||||
<Typography
|
||||
variant="h5"
|
||||
align="center"
|
||||
gutterBottom
|
||||
sx={{ color: 'text.primary', fontWeight: 700 }}
|
||||
>
|
||||
{(walletBalanceDoge - 0.05000000) + " DOGE"}
|
||||
{(() => {
|
||||
const newMaxDogeAmount = parseFloat((walletBalanceDoge - ((dogeFee * 1000) / 1e8)).toFixed(8));
|
||||
if (newMaxDogeAmount < 0) {
|
||||
return Number(0.00000000) + " DOGE"
|
||||
} else {
|
||||
return newMaxDogeAmount + " DOGE"
|
||||
}
|
||||
})()}
|
||||
</Typography>
|
||||
<div style={{ marginInlineStart: '15px' }}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
onClick={handleSendMaxDoge}
|
||||
style={{ borderRadius: 50 }}
|
||||
>
|
||||
Send Max
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Box
|
||||
sx={{
|
||||
@@ -507,22 +735,32 @@ export default function DogecoinWallet() {
|
||||
>
|
||||
<NumericFormat
|
||||
decimalScale={8}
|
||||
value={amountDoge}
|
||||
defaultValue={0}
|
||||
value={dogeAmount}
|
||||
allowNegative={false}
|
||||
customInput={TextField}
|
||||
valueIsNumericString
|
||||
variant="outlined"
|
||||
label="Amount (DOGE)"
|
||||
isAllowed={(values) => {
|
||||
const maxDogeCoin = (walletBalanceDoge - (dogeFee * 1000) / 1e8);
|
||||
const { formattedValue, floatValue } = values;
|
||||
return formattedValue === "" || floatValue <= maxDogeCoin;
|
||||
}}
|
||||
onValueChange={(values) => {
|
||||
setAmountDoge(values.floatValue);
|
||||
setDogeAmount(values.floatValue);
|
||||
}}
|
||||
required
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
label="Receiver Adress"
|
||||
id="dogeaddress"
|
||||
id="dogea-address"
|
||||
margin="normal"
|
||||
value={dogeRecipient}
|
||||
helperText="Doge Address 34 Characters long !"
|
||||
slotProps={{ htmlInput: { maxLength: 34, minLength: 34 } }}
|
||||
onChange={(e) => setDogeRecipient(e.target.value)}
|
||||
/>
|
||||
</Box>
|
||||
<div style={{
|
||||
@@ -540,7 +778,7 @@ export default function DogecoinWallet() {
|
||||
width: '50ch'
|
||||
}}>
|
||||
<Typography id="doge-fee-slider" gutterBottom>
|
||||
Current fee per byte : {feeDoge} SAT
|
||||
Current fee per byte : {dogeFee} SAT
|
||||
</Typography>
|
||||
<Slider
|
||||
track={false}
|
||||
@@ -554,6 +792,12 @@ export default function DogecoinWallet() {
|
||||
marks={dogeMarks}
|
||||
onChange={handleChangeDogeFee}
|
||||
/>
|
||||
<Typography
|
||||
align="center"
|
||||
sx={{ fontWeight: 600, fontSize: '14px', marginTop: '15px' }}
|
||||
>
|
||||
Low fees may result in slow or unconfirmed transactions.
|
||||
</Typography>
|
||||
</Box>
|
||||
</div>
|
||||
</Dialog>
|
||||
@@ -602,8 +846,8 @@ export default function DogecoinWallet() {
|
||||
? transactionsDoge.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
|
||||
: transactionsDoge
|
||||
).map((row: {
|
||||
inputs: { address: any; }[];
|
||||
outputs: { address: any; }[];
|
||||
inputs: { address: any; addressInWallet: boolean; }[];
|
||||
outputs: { address: any; addressInWallet: boolean; }[];
|
||||
txHash: string;
|
||||
totalAmount: any;
|
||||
timestamp: number;
|
||||
@@ -611,27 +855,31 @@ export default function DogecoinWallet() {
|
||||
<StyledTableRow>
|
||||
<StyledTableCell style={{ width: 'auto' }} align="left">
|
||||
{(() => {
|
||||
if (allWalletAddressesDoge.some(dogeAll => dogeAll.address === row?.inputs[0].address)) {
|
||||
return <div style={{ color: '#05a2e4' }}>{row?.inputs[0].address}</div>;
|
||||
if (row?.totalAmount < 0) {
|
||||
let meWasSender = row?.outputs.filter(function (item: { addressInWallet: boolean; }) {
|
||||
return item.addressInWallet === true;
|
||||
});
|
||||
return <div style={{ color: '#05a2e4' }}>{meWasSender[0]?.address}</div>;
|
||||
} else {
|
||||
return row?.inputs[0].address;
|
||||
let meWasNotSender = row?.outputs.filter(function (item: { addressInWallet: boolean; }) {
|
||||
return item.addressInWallet === false;
|
||||
});
|
||||
return meWasNotSender[0].address;
|
||||
}
|
||||
})()}
|
||||
</StyledTableCell>
|
||||
<StyledTableCell style={{ width: 'auto' }} align="left">
|
||||
{(() => {
|
||||
if (row?.outputs[0].address === row?.inputs[0].address) {
|
||||
if (allWalletAddressesDoge.some((dogeAll: { address: any; }) => dogeAll.address === row?.outputs[1].address)) {
|
||||
return <div style={{ color: '#05a2e4' }}>{row?.outputs[1].address}</div>;
|
||||
} else {
|
||||
return row?.outputs[1].address;
|
||||
}
|
||||
} else {
|
||||
if (allWalletAddressesDoge.some((dogeAll: { address: any; }) => dogeAll.address === row?.outputs[0].address)) {
|
||||
return <div style={{ color: '#05a2e4' }}>{row?.outputs[0].address}</div>;
|
||||
} else {
|
||||
return row?.outputs[0].address;
|
||||
}
|
||||
if (row?.totalAmount < 0) {
|
||||
let meWasNotRecipient = row?.outputs.filter(function (item: { addressInWallet: boolean; }) {
|
||||
return item.addressInWallet === false;
|
||||
});
|
||||
return meWasNotRecipient[0].address;
|
||||
} else if (row?.totalAmount > 0) {
|
||||
let meWasRecipient = row?.outputs.filter(function (item: { addressInWallet: boolean; }) {
|
||||
return item.addressInWallet === true;
|
||||
});
|
||||
return <div style={{ color: '#05a2e4' }}>{meWasRecipient[0]?.address}</div>
|
||||
}
|
||||
})()}
|
||||
</StyledTableCell>
|
||||
@@ -644,7 +892,7 @@ export default function DogecoinWallet() {
|
||||
</CustomWidthTooltip>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell style={{ width: 'auto' }} align="left">
|
||||
{row?.outputs[0].address === walletInfoDoge?.address ?
|
||||
{row?.totalAmount > 0 ?
|
||||
<div style={{ color: '#66bb6a' }}>+{(Number(row?.totalAmount) / 1e8).toFixed(8)}</div> : <div style={{ color: '#f44336' }}>{(Number(row?.totalAmount) / 1e8).toFixed(8)}</div>
|
||||
}
|
||||
</StyledTableCell>
|
||||
@@ -686,10 +934,77 @@ export default function DogecoinWallet() {
|
||||
);
|
||||
}
|
||||
|
||||
const setNewCurrentDogeServer = async (typeServer: string, hostServer: string, portServer: number) => {
|
||||
console.log("SERVER CHHOSED", typeServer, hostServer, portServer);
|
||||
try {
|
||||
const setServer = await qortalRequest({
|
||||
action: "SET_CURRENT_FOREIGN_SERVER",
|
||||
coin: "DOGE",
|
||||
type: typeServer,
|
||||
host: hostServer,
|
||||
port: portServer
|
||||
});
|
||||
if (!setServer?.error) {
|
||||
await getElectrumServersDoge();
|
||||
setOpenDogeElectrum(false);
|
||||
await getWalletBalanceDoge();
|
||||
await getTransactionsDoge();
|
||||
}
|
||||
} catch (error) {
|
||||
await getElectrumServersDoge();
|
||||
setOpenDogeElectrum(false);
|
||||
console.log("ERROR GET DOGE SERVERS INFO", error);
|
||||
}
|
||||
}
|
||||
|
||||
const DogeElectrumDialogPage = () => {
|
||||
return (
|
||||
<DogeElectrumDialog
|
||||
onClose={handleCloseDogeQR}
|
||||
aria-labelledby="doge-electrum-servers"
|
||||
open={openDogeElectrum}
|
||||
keepMounted={false}
|
||||
>
|
||||
<DialogTitle sx={{ m: 0, p: 2, fontSize: '14px' }} id="doge-electrum-servers">
|
||||
Available Dogecoin Electrum Servers.
|
||||
</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<Box sx={{
|
||||
width: '100%',
|
||||
maxWidth: 500,
|
||||
position: 'relative',
|
||||
overflow: 'auto',
|
||||
maxHeight: 400
|
||||
}}>
|
||||
<List>
|
||||
{(
|
||||
allElectrumServersDoge
|
||||
).map((server: {
|
||||
connectionType: string;
|
||||
hostName: string;
|
||||
port: number;
|
||||
}) => (
|
||||
<ListItemButton onClick={() => { setNewCurrentDogeServer(server?.connectionType, server?.hostName, server?.port) }}>
|
||||
<ListItemText primary={server?.hostName + ':' + server?.port} />
|
||||
</ListItemButton>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button autoFocus onClick={handleCloseDogeElectrum}>
|
||||
CLOSE
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</DogeElectrumDialog>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%', marginTop: "20px" }}>
|
||||
{DogeSendDialogPage()}
|
||||
{DogeQrDialogPage()}
|
||||
{DogeElectrumDialogPage()}
|
||||
<Typography gutterBottom variant="h5" sx={{ color: 'primary.main', fontStyle: 'italic', fontWeight: 700 }}>
|
||||
Dogecoin Wallet
|
||||
</Typography>
|
||||
@@ -741,12 +1056,38 @@ export default function DogecoinWallet() {
|
||||
>
|
||||
{walletInfoDoge?.address}
|
||||
</Typography>
|
||||
<Tooltip placement="top" title={copyDogeAddress ? copyDogeAddress : "Copy Address"}>
|
||||
<Tooltip placement="right" title={copyDogeAddress ? copyDogeAddress : "Copy Address"}>
|
||||
<IconButton aria-label="copy" size="small" onClick={() => { navigator.clipboard.writeText(walletInfoDoge?.address), changeCopyDogeStatus() }}>
|
||||
<CopyAllTwoTone fontSize="small" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div style={{
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
align="center"
|
||||
sx={{ color: 'primary.main', fontWeight: 700 }}
|
||||
>
|
||||
Electrum Server:
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
align="center"
|
||||
sx={{ color: 'text.primary', fontWeight: 700 }}
|
||||
>
|
||||
{currentElectrumServerDoge[0]?.hostName + ":" + currentElectrumServerDoge[0]?.port}
|
||||
</Typography>
|
||||
<Tooltip placement="right" title="CHange Server">
|
||||
<IconButton aria-label="open-electrum" size="small" onClick={handleOpenDogeElectrum}>
|
||||
<PublishedWithChangesTwoTone fontSize="small" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div style={{
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
@@ -779,9 +1120,27 @@ export default function DogecoinWallet() {
|
||||
Address Book
|
||||
</WalletButtons>
|
||||
</div>
|
||||
<Typography variant="h6" paddingTop={2} paddingBottom={2}>
|
||||
Transactions:
|
||||
</Typography>
|
||||
<div style={{
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between'
|
||||
}}>
|
||||
<Typography variant="h6" paddingTop={2} paddingBottom={2}>
|
||||
Transactions:
|
||||
</Typography>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={handleLoadingRefreshDoge}
|
||||
loading={loadingRefreshDoge}
|
||||
loadingPosition="start"
|
||||
startIcon={<Refresh />}
|
||||
variant="outlined"
|
||||
style={{ borderRadius: 50 }}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
</div>
|
||||
{isLoadingDogeTransactions ? tableLoader() : transactionsTable()}
|
||||
</WalleteCard>
|
||||
</Box>
|
||||
|
||||
@@ -15,8 +15,10 @@ import {
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
IconButton,
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
Paper,
|
||||
Slide,
|
||||
Slider,
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -34,6 +36,8 @@ import {
|
||||
} from '@mui/material';
|
||||
import { NumericFormat } from 'react-number-format';
|
||||
import TableCell, { tableCellClasses } from '@mui/material/TableCell';
|
||||
import Snackbar, { SnackbarCloseReason } from '@mui/material/Snackbar';
|
||||
import Slide, { SlideProps } from '@mui/material/Slide';
|
||||
import { TransitionProps } from '@mui/material/transitions';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import LinearProgress from '@mui/material/LinearProgress';
|
||||
@@ -46,7 +50,9 @@ import {
|
||||
LastPage,
|
||||
CopyAllTwoTone,
|
||||
Close,
|
||||
Send
|
||||
Send,
|
||||
Refresh,
|
||||
PublishedWithChangesTwoTone
|
||||
} from '@mui/icons-material';
|
||||
import coinLogoLTC from '../../assets/ltc.png';
|
||||
|
||||
@@ -125,6 +131,10 @@ const Transition = React.forwardRef(function Transition(
|
||||
return <Slide direction="up" ref={ref} {...props} />;
|
||||
});
|
||||
|
||||
function SlideTransition(props: SlideProps) {
|
||||
return <Slide {...props} direction="up" />;
|
||||
}
|
||||
|
||||
const LtcQrDialog = styled(Dialog)(({ theme }) => ({
|
||||
'& .MuiDialogContent-root': {
|
||||
padding: theme.spacing(2),
|
||||
@@ -132,6 +142,33 @@ const LtcQrDialog = styled(Dialog)(({ theme }) => ({
|
||||
'& .MuiDialogActions-root': {
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
"& .MuiDialog-paper": {
|
||||
borderRadius: "15px",
|
||||
},
|
||||
}));
|
||||
|
||||
const LtcElectrumDialog = styled(Dialog)(({ theme }) => ({
|
||||
'& .MuiDialogContent-root': {
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
'& .MuiDialogActions-root': {
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
"& .MuiDialog-paper": {
|
||||
borderRadius: "15px",
|
||||
},
|
||||
}));
|
||||
|
||||
const LtcSubmittDialog = styled(Dialog)(({ theme }) => ({
|
||||
'& .MuiDialogContent-root': {
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
'& .MuiDialogActions-root': {
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
"& .MuiDialog-paper": {
|
||||
borderRadius: "15px",
|
||||
},
|
||||
}));
|
||||
|
||||
const CustomWidthTooltip = styled(({ className, ...props }: TooltipProps) => (
|
||||
@@ -212,7 +249,6 @@ export default function LitecoinWallet() {
|
||||
const { isAuthenticated } = React.useContext(WalletContext);
|
||||
|
||||
if (!isAuthenticated) {
|
||||
console.log("WE ARE NOT LOGGED IN");
|
||||
return (
|
||||
<Alert variant="filled" severity="error">
|
||||
You must sign in, to use the Litecoin wallet !!!
|
||||
@@ -223,6 +259,8 @@ export default function LitecoinWallet() {
|
||||
const [walletInfoLtc, setWalletInfoLtc] = React.useState<any>({});
|
||||
const [walletBalanceLtc, setWalletBalanceLtc] = React.useState<any>(null);
|
||||
const [isLoadingWalletBalanceLtc, setIsLoadingWalletBalanceLtc] = React.useState<boolean>(true);
|
||||
const [allElectrumServersLtc, setAllElectrumServersLtc] = React.useState<any>([]);
|
||||
const [currentElectrumServerLtc, setCurrentElectrumServerLtc] = React.useState<any>([]);
|
||||
const [allWalletAddressesLtc, setAllWalletAddressesLtc] = React.useState<any>([]);
|
||||
const [transactionsLtc, setTransactionsLtc] = React.useState<any>([]);
|
||||
const [isLoadingLtcTransactions, setIsLoadingLtcTransactions] = React.useState<boolean>(true);
|
||||
@@ -231,9 +269,15 @@ export default function LitecoinWallet() {
|
||||
const [copyLtcAddress, setCopyLtcAddress] = React.useState('');
|
||||
const [copyLtcTxHash, setCopyLtcTxHash] = React.useState('');
|
||||
const [openLtcQR, setOpenLtcQR] = React.useState(false);
|
||||
const [openLtcElectrum, setOpenLtcElectrum] = React.useState(false);
|
||||
const [openLtcSend, setOpenLtcSend] = React.useState(false);
|
||||
const [amountLtc, setAmountLtc] = React.useState<number>(0);
|
||||
const [feeLtc, setFeeLtc] = React.useState<number>(0);
|
||||
const [ltcAmount, setLtcAmount] = React.useState<number>(0);
|
||||
const [ltcRecipient, setLtcRecipient] = React.useState('');
|
||||
const [ltcFee, setLtcFee] = React.useState<number>(0);
|
||||
const [loadingRefreshLtc, setLoadingRefreshLtc] = React.useState(false);
|
||||
const [openTxLtcSubmit, setOpenTxLtcSubmit] = React.useState(false);
|
||||
const [openSendLtcSuccess, setOpenSendLtcSuccess] = React.useState(false);
|
||||
const [openSendLtceError, setOpenSendLtcError] = React.useState(false);
|
||||
|
||||
const emptyRows = page > 0 ? Math.max(0, (1 + page) * rowsPerPage - transactionsLtc.length) : 0;
|
||||
|
||||
@@ -245,15 +289,30 @@ export default function LitecoinWallet() {
|
||||
setOpenLtcQR(false);
|
||||
}
|
||||
|
||||
const handleCloseLtcElectrum = () => {
|
||||
setOpenLtcElectrum(false);
|
||||
}
|
||||
|
||||
const handleOpenLtcSend = () => {
|
||||
setAmountLtc(0);
|
||||
setFeeLtc(30);
|
||||
setLtcAmount(0);
|
||||
setLtcRecipient('');
|
||||
setLtcFee(30);
|
||||
setOpenLtcSend(true);
|
||||
}
|
||||
|
||||
const validateCanSendLtc = () => {
|
||||
if (ltcAmount <= 0 || null || !ltcAmount) {
|
||||
return true;
|
||||
}
|
||||
if (ltcRecipient.length < 34 || '') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const handleCloseLtcSend = () => {
|
||||
setAmountLtc(0);
|
||||
setFeeLtc(0);
|
||||
setLtcAmount(0);
|
||||
setLtcFee(0);
|
||||
setOpenLtcSend(false);
|
||||
}
|
||||
|
||||
@@ -269,7 +328,7 @@ export default function LitecoinWallet() {
|
||||
setCopyLtcTxHash('');
|
||||
}
|
||||
|
||||
const handleChangePage = (event: React.MouseEvent<HTMLButtonElement> | null, newPage: number,) => {
|
||||
const handleChangePage = (_event: React.MouseEvent<HTMLButtonElement> | null, newPage: number,) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
@@ -278,12 +337,29 @@ export default function LitecoinWallet() {
|
||||
setPage(0);
|
||||
};
|
||||
|
||||
const handleChangeLtcAmount = (_: Event, newValue: number | number[]) => {
|
||||
setAmountLtc(newValue as number);
|
||||
const handleChangeLtcFee = (_: Event, newValue: number | number[]) => {
|
||||
setLtcFee(newValue as number);
|
||||
setLtcAmount(0);
|
||||
};
|
||||
|
||||
const handleChangeLtcFee = (_: Event, newValue: number | number[]) => {
|
||||
setFeeLtc(newValue as number);
|
||||
const handleCloseSendLtcSuccess = (
|
||||
_event?: React.SyntheticEvent | Event,
|
||||
reason?: SnackbarCloseReason,
|
||||
) => {
|
||||
if (reason === 'clickaway') {
|
||||
return;
|
||||
}
|
||||
setOpenSendLtcSuccess(false);
|
||||
};
|
||||
|
||||
const handleCloseSendLtcError = (
|
||||
_event?: React.SyntheticEvent | Event,
|
||||
reason?: SnackbarCloseReason,
|
||||
) => {
|
||||
if (reason === 'clickaway') {
|
||||
return;
|
||||
}
|
||||
setOpenSendLtcError(false);
|
||||
};
|
||||
|
||||
const getWalletInfoLtc = async () => {
|
||||
@@ -294,11 +370,10 @@ export default function LitecoinWallet() {
|
||||
});
|
||||
if (!response?.error) {
|
||||
setWalletInfoLtc(response);
|
||||
console.log("GET LTC WALLET INFO", response);
|
||||
}
|
||||
} catch (error) {
|
||||
setWalletInfoLtc({});
|
||||
console.log("ERROR GET LTC WALLET INFO", error);
|
||||
console.error("ERROR GET LTC WALLET INFO", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,12 +391,11 @@ export default function LitecoinWallet() {
|
||||
if (!response?.error) {
|
||||
setWalletBalanceLtc(response);
|
||||
setIsLoadingWalletBalanceLtc(false);
|
||||
console.log("GET LTC BALANCE", response);
|
||||
}
|
||||
} catch (error) {
|
||||
setWalletBalanceLtc(null);
|
||||
setIsLoadingWalletBalanceLtc(false);
|
||||
console.log("ERROR GET LTC BALANCE", error);
|
||||
console.error("ERROR GET LTC BALANCE", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,6 +410,35 @@ export default function LitecoinWallet() {
|
||||
}
|
||||
}, [isAuthenticated]);
|
||||
|
||||
const getElectrumServersLtc = async () => {
|
||||
try {
|
||||
const response = await qortalRequest({
|
||||
action: "GET_CROSSCHAIN_SERVER_INFO",
|
||||
coin: "LTC"
|
||||
});
|
||||
if (!response?.error) {
|
||||
setAllElectrumServersLtc(response);
|
||||
let currentLtcServer = response.filter(function (item: { isCurrent: boolean; }) {
|
||||
return item.isCurrent === true;
|
||||
});
|
||||
setCurrentElectrumServerLtc(currentLtcServer);
|
||||
}
|
||||
} catch (error) {
|
||||
setAllElectrumServersLtc({});
|
||||
console.error("ERROR GET LTC SERVERS INFO", error);
|
||||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isAuthenticated) return;
|
||||
getElectrumServersLtc();
|
||||
}, [isAuthenticated]);
|
||||
|
||||
const handleOpenLtcElectrum = async () => {
|
||||
await getElectrumServersLtc();
|
||||
setOpenLtcElectrum(true);
|
||||
}
|
||||
|
||||
const getTransactionsLtc = async () => {
|
||||
try {
|
||||
setIsLoadingLtcTransactions(true);
|
||||
@@ -351,22 +454,20 @@ export default function LitecoinWallet() {
|
||||
await responseLtcAllAddresses;
|
||||
if (!responseLtcAllAddresses?.error) {
|
||||
setAllWalletAddressesLtc(responseLtcAllAddresses);
|
||||
console.log("GET LTC ALL ADDRESSES", responseLtcAllAddresses);
|
||||
}
|
||||
} catch (error) {
|
||||
setAllWalletAddressesLtc([]);
|
||||
console.log("ERROR GET LTC ALL ADDRESSES", error);
|
||||
console.error("ERROR GET LTC ALL ADDRESSES", error);
|
||||
}
|
||||
await responseLtcTransactions;
|
||||
if (!responseLtcTransactions?.error) {
|
||||
setTransactionsLtc(responseLtcTransactions);
|
||||
setIsLoadingLtcTransactions(false);
|
||||
console.log("GET LTC TRANSACTIONS", responseLtcTransactions);
|
||||
}
|
||||
} catch (error) {
|
||||
setIsLoadingLtcTransactions(false);
|
||||
setTransactionsLtc([]);
|
||||
console.log("ERROR GET LTC TRANSACTIONS", error);
|
||||
console.error("ERROR GET LTC TRANSACTIONS", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,6 +476,21 @@ export default function LitecoinWallet() {
|
||||
getTransactionsLtc();
|
||||
}, [isAuthenticated]);
|
||||
|
||||
const handleLoadingRefreshLtc = async () => {
|
||||
setLoadingRefreshLtc(true);
|
||||
await getTransactionsLtc();
|
||||
setLoadingRefreshLtc(false);
|
||||
}
|
||||
|
||||
const handleSendMaxLtc = () => {
|
||||
const maxLtcAmount = parseFloat((walletBalanceLtc - ((ltcFee * 1000) / 1e8)).toFixed(8));
|
||||
if (maxLtcAmount <= 0) {
|
||||
setLtcAmount(0);
|
||||
} else {
|
||||
setLtcAmount(maxLtcAmount);
|
||||
}
|
||||
}
|
||||
|
||||
const LtcQrDialogPage = () => {
|
||||
return (
|
||||
<LtcQrDialog
|
||||
@@ -406,6 +522,40 @@ export default function LitecoinWallet() {
|
||||
);
|
||||
}
|
||||
|
||||
const sendLtcRequest = async () => {
|
||||
setOpenTxLtcSubmit(true);
|
||||
const ltcFeeCalculated = Number(ltcFee / 1e8).toFixed(8);
|
||||
try {
|
||||
const sendRequest = await qortalRequest({
|
||||
action: "SEND_COIN",
|
||||
coin: "LTC",
|
||||
recipient: ltcRecipient,
|
||||
amount: ltcAmount,
|
||||
fee: ltcFeeCalculated
|
||||
});
|
||||
if (!sendRequest?.error) {
|
||||
setLtcAmount(0);
|
||||
setLtcRecipient('');
|
||||
setLtcFee(30);
|
||||
setOpenTxLtcSubmit(false);
|
||||
setOpenSendLtcSuccess(true);
|
||||
setIsLoadingWalletBalanceLtc(true);
|
||||
await timeoutDelay(3000);
|
||||
getWalletBalanceLtc();
|
||||
}
|
||||
} catch (error) {
|
||||
setLtcAmount(0);
|
||||
setLtcRecipient('');
|
||||
setLtcFee(30);
|
||||
setOpenTxLtcSubmit(false);
|
||||
setOpenSendLtcError(true);
|
||||
setIsLoadingWalletBalanceLtc(true);
|
||||
await timeoutDelay(3000);
|
||||
getWalletBalanceLtc();
|
||||
console.error("ERROR SENDING LTC", error);
|
||||
}
|
||||
}
|
||||
|
||||
const LtcSendDialogPage = () => {
|
||||
return (
|
||||
<Dialog
|
||||
@@ -414,6 +564,58 @@ export default function LitecoinWallet() {
|
||||
onClose={handleCloseLtcSend}
|
||||
slots={{ transition: Transition }}
|
||||
>
|
||||
<LtcSubmittDialog
|
||||
fullWidth={true}
|
||||
maxWidth='xs'
|
||||
open={openTxLtcSubmit}
|
||||
>
|
||||
<DialogContent>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', flexWrap: 'wrap' }}>
|
||||
<div style={{
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<CircularProgress color="success" size={64} />
|
||||
</div>
|
||||
<div style={{
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
marginTop: '20px'
|
||||
}}>
|
||||
<Typography variant="h6" sx={{ color: 'primary.main', fontStyle: 'italic', fontWeight: 700 }}>
|
||||
Processing Transaction Please Wait...
|
||||
</Typography>
|
||||
</div>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
</LtcSubmittDialog>
|
||||
<Snackbar
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
||||
open={openSendLtcSuccess}
|
||||
autoHideDuration={4000}
|
||||
slots={{ transition: SlideTransition }}
|
||||
onClose={handleCloseSendLtcSuccess}>
|
||||
<Alert
|
||||
onClose={handleCloseSendLtcSuccess}
|
||||
severity="success"
|
||||
variant="filled"
|
||||
sx={{ width: '100%' }}
|
||||
>
|
||||
Sent LTC transaction was successful!
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
<Snackbar open={openSendLtceError} autoHideDuration={4000} onClose={handleCloseSendLtcError}>
|
||||
<Alert
|
||||
onClose={handleCloseSendLtcError}
|
||||
severity="error"
|
||||
variant="filled"
|
||||
sx={{ width: '100%' }}
|
||||
>
|
||||
Something went wrong, please try again!
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
<AppBar sx={{ position: 'static' }}>
|
||||
<Toolbar>
|
||||
<IconButton
|
||||
@@ -436,10 +638,11 @@ export default function LitecoinWallet() {
|
||||
Transfer LTC
|
||||
</Typography>
|
||||
<Button
|
||||
disabled={validateCanSendLtc()}
|
||||
variant="contained"
|
||||
startIcon={<Send />}
|
||||
aria-label="send"
|
||||
onClick={handleOpenLtcSend}
|
||||
aria-label="send-ltc"
|
||||
onClick={sendLtcRequest}
|
||||
sx={{ backgroundColor: "#05a2e4", color: "white", "&:hover": { backgroundColor: "#02648d", } }}
|
||||
>
|
||||
SEND
|
||||
@@ -467,9 +670,48 @@ export default function LitecoinWallet() {
|
||||
gutterBottom
|
||||
sx={{ color: 'text.primary', fontWeight: 700 }}
|
||||
>
|
||||
{walletBalanceLtc + " LTC"}
|
||||
{isLoadingWalletBalanceLtc ? <Box sx={{ width: '175px' }}><LinearProgress /></Box> : walletBalanceLtc + " LTC"}
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={{
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginTop: '20px'
|
||||
}}>
|
||||
<Typography
|
||||
variant="h5"
|
||||
align="center"
|
||||
sx={{ color: 'primary.main', fontWeight: 700 }}
|
||||
>
|
||||
Max Sendable:
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h5"
|
||||
align="center"
|
||||
sx={{ color: 'text.primary', fontWeight: 700 }}
|
||||
>
|
||||
{(() => {
|
||||
const newMaxLtcAmount = parseFloat((walletBalanceLtc - ((ltcFee * 1000) / 1e8)).toFixed(8));
|
||||
if (newMaxLtcAmount < 0) {
|
||||
return Number(0.00000000) + " LTC"
|
||||
} else {
|
||||
return newMaxLtcAmount + " LTC"
|
||||
}
|
||||
})()}
|
||||
</Typography>
|
||||
<div style={{ marginInlineStart: '15px' }}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
onClick={handleSendMaxLtc}
|
||||
style={{ borderRadius: 50 }}
|
||||
>
|
||||
Send Max
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
@@ -482,22 +724,32 @@ export default function LitecoinWallet() {
|
||||
>
|
||||
<NumericFormat
|
||||
decimalScale={8}
|
||||
value={amountLtc}
|
||||
defaultValue={0}
|
||||
value={ltcAmount}
|
||||
allowNegative={false}
|
||||
customInput={TextField}
|
||||
valueIsNumericString
|
||||
variant="outlined"
|
||||
label="Amount (LTC)"
|
||||
isAllowed={(values) => {
|
||||
const maxLtcCoin = (walletBalanceLtc - (ltcFee * 1000) / 1e8);
|
||||
const { formattedValue, floatValue } = values;
|
||||
return formattedValue === "" || floatValue <= maxLtcCoin;
|
||||
}}
|
||||
onValueChange={(values) => {
|
||||
setAmountLtc(values.floatValue);
|
||||
setLtcAmount(values.floatValue);
|
||||
}}
|
||||
required
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
label="Receiver Adress"
|
||||
id="ltcaddress"
|
||||
id="ltca-address"
|
||||
margin="normal"
|
||||
value={ltcRecipient}
|
||||
helperText="Ltc Address 34 Characters long !"
|
||||
slotProps={{ htmlInput: { maxLength: 34, minLength: 34 } }}
|
||||
onChange={(e) => setLtcRecipient(e.target.value)}
|
||||
/>
|
||||
</Box>
|
||||
<div style={{
|
||||
@@ -514,8 +766,8 @@ export default function LitecoinWallet() {
|
||||
flexDirection: 'column',
|
||||
width: '50ch'
|
||||
}}>
|
||||
<Typography id="doge-fee-slider" gutterBottom>
|
||||
Current fee per byte : {feeLtc} SAT
|
||||
<Typography id="ltc-fee-slider" gutterBottom>
|
||||
Current fee per byte : {ltcFee} SAT
|
||||
</Typography>
|
||||
<Slider
|
||||
track={false}
|
||||
@@ -523,12 +775,18 @@ export default function LitecoinWallet() {
|
||||
min={10}
|
||||
max={100}
|
||||
valueLabelDisplay="auto"
|
||||
aria-labelledby="doge-fee-slider"
|
||||
aria-labelledby="ltc-fee-slider"
|
||||
getAriaValueText={valueTextLtc}
|
||||
defaultValue={30}
|
||||
marks={ltcMarks}
|
||||
onChange={handleChangeLtcFee}
|
||||
/>
|
||||
<Typography
|
||||
align="center"
|
||||
sx={{ fontWeight: 600, fontSize: '14px', marginTop: '15px' }}
|
||||
>
|
||||
Low fees may result in slow or unconfirmed transactions.
|
||||
</Typography>
|
||||
</Box>
|
||||
</div>
|
||||
</Dialog>
|
||||
@@ -577,37 +835,40 @@ export default function LitecoinWallet() {
|
||||
? transactionsLtc.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
|
||||
: transactionsLtc
|
||||
).map((row: {
|
||||
name: React.Key;
|
||||
inputs: { address: any; }[];
|
||||
outputs: { address: any; }[];
|
||||
inputs: { address: any; addressInWallet: boolean; }[];
|
||||
outputs: { address: any; addressInWallet: boolean; }[];
|
||||
txHash: string;
|
||||
totalAmount: any;
|
||||
timestamp: number;
|
||||
}) => (
|
||||
<StyledTableRow key={row.name}>
|
||||
<StyledTableRow>
|
||||
<StyledTableCell style={{ width: 'auto' }} align="left">
|
||||
{(() => {
|
||||
if (allWalletAddressesLtc.some(ltcAll => ltcAll.address === row?.inputs[0].address)) {
|
||||
return <div style={{ color: '#05a2e4' }}>{row?.inputs[0].address}</div>;
|
||||
if (row?.totalAmount < 0) {
|
||||
let meWasSender = row?.outputs.filter(function (item: { addressInWallet: boolean; }) {
|
||||
return item.addressInWallet === true;
|
||||
});
|
||||
return <div style={{ color: '#05a2e4' }}>{meWasSender[0]?.address}</div>;
|
||||
} else {
|
||||
return row?.inputs[0].address;
|
||||
let meWasNotSender = row?.outputs.filter(function (item: { addressInWallet: boolean; }) {
|
||||
return item.addressInWallet === false;
|
||||
});
|
||||
return meWasNotSender[0].address;
|
||||
}
|
||||
})()}
|
||||
</StyledTableCell>
|
||||
<StyledTableCell style={{ width: 'auto' }} align="left">
|
||||
{(() => {
|
||||
if (row?.outputs[0].address === row?.inputs[0].address) {
|
||||
if (allWalletAddressesLtc.some(ltcAll => ltcAll.address === row?.outputs[1].address)) {
|
||||
return <div style={{ color: '#05a2e4' }}>{row?.outputs[1].address}</div>;
|
||||
} else {
|
||||
return row?.outputs[1].address;
|
||||
}
|
||||
} else {
|
||||
if (allWalletAddressesLtc.some(ltcAll => ltcAll.address === row?.outputs[0].address)) {
|
||||
return <div style={{ color: '#05a2e4' }}>{row?.outputs[0].address}</div>;
|
||||
} else {
|
||||
return row?.outputs[0].address;
|
||||
}
|
||||
if (row?.totalAmount < 0) {
|
||||
let meWasNotRecipient = row?.outputs.filter(function (item: { addressInWallet: boolean; }) {
|
||||
return item.addressInWallet === false;
|
||||
});
|
||||
return meWasNotRecipient[0].address;
|
||||
} else if (row?.totalAmount > 0) {
|
||||
let meWasRecipient = row?.outputs.filter(function (item: { addressInWallet: boolean; }) {
|
||||
return item.addressInWallet === true;
|
||||
});
|
||||
return <div style={{ color: '#05a2e4' }}>{meWasRecipient[0]?.address}</div>
|
||||
}
|
||||
})()}
|
||||
</StyledTableCell>
|
||||
@@ -620,7 +881,7 @@ export default function LitecoinWallet() {
|
||||
</CustomWidthTooltip>
|
||||
</StyledTableCell>
|
||||
<StyledTableCell style={{ width: 'auto' }} align="left">
|
||||
{row?.outputs[0].address === walletInfoLtc?.address ?
|
||||
{row?.totalAmount > 0 ?
|
||||
<div style={{ color: '#66bb6a' }}>+{(Number(row?.totalAmount) / 1e8).toFixed(8)}</div> : <div style={{ color: '#f44336' }}>{(Number(row?.totalAmount) / 1e8).toFixed(8)}</div>
|
||||
}
|
||||
</StyledTableCell>
|
||||
@@ -662,10 +923,76 @@ export default function LitecoinWallet() {
|
||||
);
|
||||
}
|
||||
|
||||
const setNewCurrentLtcServer = async (typeServer: string, hostServer: string, portServer: number) => {
|
||||
try {
|
||||
const setServer = await qortalRequest({
|
||||
action: "SET_CURRENT_FOREIGN_SERVER",
|
||||
coin: "LTC",
|
||||
type: typeServer,
|
||||
host: hostServer,
|
||||
port: portServer
|
||||
});
|
||||
if (!setServer?.error) {
|
||||
await getElectrumServersLtc();
|
||||
setOpenLtcElectrum(false);
|
||||
await getWalletBalanceLtc();
|
||||
await getTransactionsLtc();
|
||||
}
|
||||
} catch (error) {
|
||||
await getElectrumServersLtc();
|
||||
setOpenLtcElectrum(false);
|
||||
console.error("ERROR GET LTC SERVERS INFO", error);
|
||||
}
|
||||
}
|
||||
|
||||
const LtcElectrumDialogPage = () => {
|
||||
return (
|
||||
<LtcElectrumDialog
|
||||
onClose={handleCloseLtcQR}
|
||||
aria-labelledby="ltc-electrum-servers"
|
||||
open={openLtcElectrum}
|
||||
keepMounted={false}
|
||||
>
|
||||
<DialogTitle sx={{ m: 0, p: 2, fontSize: '14px' }} id="ltc-electrum-servers">
|
||||
Available Litecoin Electrum Servers.
|
||||
</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
<Box sx={{
|
||||
width: '100%',
|
||||
maxWidth: 500,
|
||||
position: 'relative',
|
||||
overflow: 'auto',
|
||||
maxHeight: 400
|
||||
}}>
|
||||
<List>
|
||||
{(
|
||||
allElectrumServersLtc
|
||||
).map((server: {
|
||||
connectionType: string;
|
||||
hostName: string;
|
||||
port: number;
|
||||
}) => (
|
||||
<ListItemButton onClick={() => { setNewCurrentLtcServer(server?.connectionType, server?.hostName, server?.port) }}>
|
||||
<ListItemText primary={server?.hostName + ':' + server?.port} />
|
||||
</ListItemButton>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button autoFocus onClick={handleCloseLtcElectrum}>
|
||||
CLOSE
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</LtcElectrumDialog>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%', marginTop: "20px" }}>
|
||||
{LtcSendDialogPage()}
|
||||
{LtcQrDialogPage()}
|
||||
{LtcElectrumDialogPage()}
|
||||
<Typography gutterBottom variant="h5" sx={{ color: 'primary.main', fontStyle: 'italic', fontWeight: 700 }}>
|
||||
Litecoin Wallet
|
||||
</Typography>
|
||||
@@ -717,12 +1044,38 @@ export default function LitecoinWallet() {
|
||||
>
|
||||
{walletInfoLtc?.address}
|
||||
</Typography>
|
||||
<Tooltip placement="top" title={copyLtcAddress ? copyLtcAddress : "Copy Address"}>
|
||||
<Tooltip placement="right" title={copyLtcAddress ? copyLtcAddress : "Copy Address"}>
|
||||
<IconButton aria-label="copy" size="small" onClick={() => { navigator.clipboard.writeText(walletInfoLtc?.address), changeCopyLtcStatus() }}>
|
||||
<CopyAllTwoTone fontSize="small" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div style={{
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
align="center"
|
||||
sx={{ color: 'primary.main', fontWeight: 700 }}
|
||||
>
|
||||
Electrum Server:
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
align="center"
|
||||
sx={{ color: 'text.primary', fontWeight: 700 }}
|
||||
>
|
||||
{currentElectrumServerLtc[0]?.hostName + ":" + currentElectrumServerLtc[0]?.port}
|
||||
</Typography>
|
||||
<Tooltip placement="right" title="CHange Server">
|
||||
<IconButton aria-label="open-electrum" size="small" onClick={handleOpenLtcElectrum}>
|
||||
<PublishedWithChangesTwoTone fontSize="small" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div style={{
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
@@ -755,9 +1108,27 @@ export default function LitecoinWallet() {
|
||||
Address Book
|
||||
</WalletButtons>
|
||||
</div>
|
||||
<Typography variant="h6" paddingTop={2} paddingBottom={2}>
|
||||
Transactions:
|
||||
</Typography>
|
||||
<div style={{
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between'
|
||||
}}>
|
||||
<Typography variant="h6" paddingTop={2} paddingBottom={2}>
|
||||
Transactions:
|
||||
</Typography>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={handleLoadingRefreshLtc}
|
||||
loading={loadingRefreshLtc}
|
||||
loadingPosition="start"
|
||||
startIcon={<Refresh />}
|
||||
variant="outlined"
|
||||
style={{ borderRadius: 50 }}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
</div>
|
||||
{isLoadingLtcTransactions ? tableLoader() : transactionsTable()}
|
||||
</WalleteCard>
|
||||
</Box>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user