import { createContext, useCallback, useEffect, useMemo, useRef, useState, } from "react"; import reactLogo from "./assets/react.svg"; import viteLogo from "/vite.svg"; import "./App.css"; import { useDropzone } from "react-dropzone"; import { Box, Button, Checkbox, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Input, InputLabel, Popover, Tooltip, Typography, } from "@mui/material"; import { decryptStoredWallet } from "./utils/decryptWallet"; import { CountdownCircleTimer } from "react-countdown-circle-timer"; import Logo1 from "./assets/svgs/Logo1.svg"; import Logo1Dark from "./assets/svgs/Logo1Dark.svg"; import RefreshIcon from "@mui/icons-material/Refresh"; import Logo2 from "./assets/svgs/Logo2.svg"; import Copy from "./assets/svgs/Copy.svg"; import ltcLogo from "./assets/ltc.png"; import qortLogo from "./assets/qort.png"; import { CopyToClipboard } from "react-copy-to-clipboard"; import Download from "./assets/svgs/Download.svg"; import Logout from "./assets/svgs/Logout.svg"; import Return from "./assets/svgs/Return.svg"; import Success from "./assets/svgs/Success.svg"; import Info from "./assets/svgs/Info.svg"; import CloseIcon from '@mui/icons-material/Close'; import { createAccount, generateRandomSentence, saveFileToDisk, } from "./utils/generateWallet/generateWallet"; import { kdf } from "./deps/kdf"; import { generateSaveWalletData } from "./utils/generateWallet/storeWallet"; import { crypto, walletVersion } from "./constants/decryptWallet"; import PhraseWallet from "./utils/generateWallet/phrase-wallet"; import { AddressBox, AppContainer, AuthenticatedContainer, AuthenticatedContainerInnerLeft, AuthenticatedContainerInnerRight, CustomButton, CustomInput, CustomLabel, TextItalic, TextP, TextSpan, } from "./App-styles"; import { Spacer } from "./common/Spacer"; import { Loader } from "./components/Loader"; import { PasswordField, ErrorText } from "./components"; import { ChatGroup } from "./components/Chat/ChatGroup"; import { Group, requestQueueMemberNames } from "./components/Group/Group"; import { TaskManger } from "./components/TaskManager/TaskManger"; import { useModal } from "./common/useModal"; import { LoadingButton } from "@mui/lab"; import { Label } from "./components/Group/AddGroup"; import { CustomizedSnackbars } from "./components/Snackbar/Snackbar"; import { getFee, groupApi, groupApiLocal, groupApiSocket, groupApiSocketLocal, } from "./background"; import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "./utils/events"; import { requestQueueCommentCount, requestQueuePublishedAccouncements } from "./components/Chat/GroupAnnouncements"; import { requestQueueGroupJoinRequests } from "./components/Group/GroupJoinRequests"; import { DrawerComponent } from "./components/Drawer/Drawer"; type extStates = | "not-authenticated" | "authenticated" | "send-qort" | "web-app-request-connection" | "web-app-request-payment" | "web-app-request-authentication" | "download-wallet" | "create-wallet" | "transfer-success-regular" | "transfer-success-request" | "wallet-dropped" | "web-app-request-buy-order" | "buy-order-submitted" | "group"; interface MyContextInterface { txList: any[]; memberGroups: any[]; setTxList: (val) => void; setMemberGroups: (val) => void; isShow: boolean; onCancel: () => void; onOk: () => void; show: () => void; message: any; } const defaultValues: MyContextInterface = { txList: [], memberGroups: [], setTxList: () => {}, setMemberGroups: () => {}, isShow: false, onCancel: () => {}, onOk: () => {}, show: () => {}, message: { publishFee: "", message: "", }, }; export let isMobile = true const isMobileDevice = () => { const userAgent = navigator.userAgent || navigator.vendor || window.opera; if (/android/i.test(userAgent)) { return true; // Android device } if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) { return true; // iOS device } return false; }; if (isMobileDevice()) { isMobile = true console.log("Running on a mobile device"); } else { console.log("Running on a desktop"); } export const allQueues = { requestQueueCommentCount: requestQueueCommentCount, requestQueuePublishedAccouncements: requestQueuePublishedAccouncements, requestQueueMemberNames: requestQueueMemberNames, requestQueueGroupJoinRequests: requestQueueGroupJoinRequests } const controlAllQueues = (action) => { Object.keys(allQueues).forEach((key) => { const val = allQueues[key]; try { if (typeof val[action] === 'function') { val[action](); } } catch (error) { console.error(error); } }); }; export const clearAllQueues = () => { Object.keys(allQueues).forEach((key) => { const val = allQueues[key]; try { val.clear(); } catch (error) { console.error(error); } }); } export const pauseAllQueues = () => { controlAllQueues('pause'); chrome?.runtime?.sendMessage( { action: "pauseAllQueues", payload: { }, } ); } export const resumeAllQueues = () => { controlAllQueues('resume'); chrome?.runtime?.sendMessage( { action: "resumeAllQueues", payload: { }, } ); } export const MyContext = createContext(defaultValues); export let globalApiKey: string | null = null; export const getBaseApiReact = (customApi?: string) => { if (customApi) { return customApi; } if (globalApiKey) { return groupApiLocal; } else { return groupApi; } }; // export const getArbitraryEndpointReact = () => { // if (globalApiKey) { // return `/arbitrary/resources/search`; // } else { // return `/arbitrary/resources/searchsimple`; // } // }; export const getArbitraryEndpointReact = () => { if (globalApiKey) { return `/arbitrary/resources/search`; } else { return `/arbitrary/resources/searchsimple`; } }; export const getBaseApiReactSocket = (customApi?: string) => { if (customApi) { return customApi; } if (globalApiKey) { return groupApiSocketLocal; } else { return groupApiSocket; } }; export const isMainWindow = window?.location?.href?.includes("?main=true"); function App() { const [extState, setExtstate] = useState("not-authenticated"); const [backupjson, setBackupjson] = useState(null); const [rawWallet, setRawWallet] = useState(null); const [ltcBalanceLoading, setLtcBalanceLoading] = useState(false); const [qortBalanceLoading, setQortBalanceLoading] = useState(false); const [decryptedWallet, setdecryptedWallet] = useState(null); const [requestConnection, setRequestConnection] = useState(null); const [requestBuyOrder, setRequestBuyOrder] = useState(null); const [authenticatedMode, setAuthenticatedMode] = useState("qort"); const [requestAuthentication, setRequestAuthentication] = useState(null); const [userInfo, setUserInfo] = useState(null); const [balance, setBalance] = useState(null); const [ltcBalance, setLtcBalance] = useState(null); const [paymentTo, setPaymentTo] = useState(""); const [paymentAmount, setPaymentAmount] = useState(0); const [paymentPassword, setPaymentPassword] = useState(""); const [sendPaymentError, setSendPaymentError] = useState(""); const [sendPaymentSuccess, setSendPaymentSuccess] = useState(""); const [countdown, setCountdown] = useState(null); const [walletToBeDownloaded, setWalletToBeDownloaded] = useState(null); const [walletToBeDownloadedPassword, setWalletToBeDownloadedPassword] = useState(""); const [isMain, setIsMain] = useState( window?.location?.href?.includes("?main=true") ); const isMainRef = useRef(false); const [authenticatePassword, setAuthenticatePassword] = useState(""); const [sendqortState, setSendqortState] = useState(null); const [isLoading, setIsLoading] = useState(false); const [ walletToBeDownloadedPasswordConfirm, setWalletToBeDownloadedPasswordConfirm, ] = useState(""); const [walletToBeDownloadedError, setWalletToBeDownloadedError] = useState(""); const [walletToBeDecryptedError, setWalletToBeDecryptedError] = useState(""); const [txList, setTxList] = useState([]); const [memberGroups, setMemberGroups] = useState([]); const [isFocused, setIsFocused] = useState(true); const holdRefExtState = useRef("not-authenticated"); const isFocusedRef = useRef(true); const { isShow, onCancel, onOk, show, message } = 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 [openSnack, setOpenSnack] = useState(false); const [hasLocalNode, setHasLocalNode] = useState(false); const [openAdvancedSettings, setOpenAdvancedSettings] = useState(false); const [useLocalNode, setUseLocalNode] = useState(false); const [confirmUseOfLocal, setConfirmUseOfLocal] = useState(false); const [isOpenDrawerProfile, setIsOpenDrawerProfile] = useState(false); const [apiKey, setApiKey] = useState(""); const [isOpenSendQort, setIsOpenSendQort] = useState(false) const [isOpenSendQortSuccess, setIsOpenSendQortSuccess] = useState(false) useEffect(() => { if(!isMobile) return // Function to set the height of the app to the viewport height const resetHeight = () => { const height = window.visualViewport ? window.visualViewport.height : window.innerHeight; // Set the height to the root element (usually #root) document.getElementById('root').style.height = height + "px"; }; // Set the initial height resetHeight(); // Add event listeners for resize and visualViewport changes window.addEventListener('resize', resetHeight); window.visualViewport?.addEventListener('resize', resetHeight); // Clean up the event listeners when the component unmounts return () => { window.removeEventListener('resize', resetHeight); window.visualViewport?.removeEventListener('resize', resetHeight); }; }, []); useEffect(() => { chrome?.runtime?.sendMessage({ action: "getApiKey" }, (response) => { if (response) { globalApiKey = response; setApiKey(response); setUseLocalNode(true) setConfirmUseOfLocal(true) setOpenAdvancedSettings(true) } }); }, []); useEffect(() => { if (extState) { holdRefExtState.current = extState; } }, [extState]); useEffect(() => { isFocusedRef.current = isFocused; }, [isFocused]); // Handler for file selection const handleFileChangeApiKey = (event) => { const file = event.target.files[0]; // Get the selected file if (file) { const reader = new FileReader(); reader.onload = (e) => { const text = e.target.result; // Get the file content setApiKey(text); // Store the file content in the state }; reader.readAsText(file); // Read the file as text } }; // const checkIfUserHasLocalNode = useCallback(async () => { // try { // const url = `http://127.0.0.1:12391/admin/status`; // const response = await fetch(url, { // method: "GET", // headers: { // "Content-Type": "application/json", // }, // }); // const data = await response.json(); // if (data?.isSynchronizing === false && data?.syncPercent === 100) { // setHasLocalNode(true); // } // } catch (error) {} // }, []); // useEffect(() => { // checkIfUserHasLocalNode(); // }, [extState]); const address = useMemo(() => { if (!rawWallet?.address0) return ""; return rawWallet.address0; }, [rawWallet]); const { getRootProps, getInputProps } = useDropzone({ accept: { "application/json": [".json"], // Only accept JSON files }, maxFiles: 1, onDrop: async (acceptedFiles) => { const file: any = acceptedFiles[0]; const fileContents = await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onabort = () => reject("File reading was aborted"); reader.onerror = () => reject("File reading has failed"); reader.onload = () => { // Resolve the promise with the reader result when reading completes resolve(reader.result); }; // Read the file as text reader.readAsText(file); }); let error: any = null; let pf: any; try { if (typeof fileContents !== "string") return; pf = JSON.parse(fileContents); } catch (e) {} try { const requiredFields = [ "address0", "salt", "iv", "version", "encryptedSeed", "mac", "kdfThreads", ]; for (const field of requiredFields) { if (!(field in pf)) throw new Error(field + " not found in JSON"); } // storeWalletInfo(pf); setRawWallet(pf); // setExtstate("authenticated"); setExtstate("wallet-dropped"); setdecryptedWallet(null); } catch (e) { console.log(e); error = e; } }, }); const saveWalletFunc = async (password: string) => { let wallet = structuredClone(rawWallet); const res = await decryptStoredWallet(password, wallet); const wallet2 = new PhraseWallet(res, walletVersion); wallet = await wallet2.generateSaveWalletData( password, crypto.kdfThreads, () => {} ); setWalletToBeDownloaded({ wallet, qortAddress: rawWallet.address0, }); return { wallet, qortAddress: rawWallet.address0, }; }; const storeWalletInfo = (wallet: any) => { chrome?.runtime?.sendMessage( { action: "storeWalletInfo", wallet }, (response) => { if (response) { } } ); chrome.tabs.query( { active: true, currentWindow: true }, function (tabs: any[]) { chrome.tabs.sendMessage( tabs[0].id, { from: "popup", subject: "anySubject" }, function (response) {} ); } ); }; const getBalanceFunc = () => { setQortBalanceLoading(true); chrome?.runtime?.sendMessage({ action: "balance" }, (response) => { if (!response?.error && !isNaN(+response)) { setBalance(response); } setQortBalanceLoading(false); }); }; const getLtcBalanceFunc = () => { setLtcBalanceLoading(true); chrome?.runtime?.sendMessage({ action: "ltcBalance" }, (response) => { if (!response?.error && !isNaN(+response)) { setLtcBalance(response); } setLtcBalanceLoading(false); }); }; const sendCoinFunc = () => { setSendPaymentError(""); setSendPaymentSuccess(""); if (!paymentTo) { setSendPaymentError("Please enter a recipient"); return; } if (!paymentAmount) { setSendPaymentError("Please enter an amount greater than 0"); return; } if (!paymentPassword) { setSendPaymentError("Please enter your wallet password"); return; } setIsLoading(true); chrome?.runtime?.sendMessage( { action: "sendCoin", payload: { amount: Number(paymentAmount), receiver: paymentTo.trim(), password: paymentPassword, }, }, (response) => { if (response?.error) { setSendPaymentError(response.error); } else { setIsOpenSendQort(false) setIsOpenSendQortSuccess(true) // setExtstate("transfer-success-regular"); // setSendPaymentSuccess("Payment successfully sent"); } setIsLoading(false); } ); }; const clearAllStates = () => { setRequestConnection(null); setRequestAuthentication(null); }; useEffect(() => { // Listen for messages from the background script chrome.runtime?.onMessage.addListener((message, sender, sendResponse) => { // Check if the message is to update the state if ( message.action === "UPDATE_STATE_CONFIRM_SEND_QORT" && !isMainWindow ) { // Update the component state with the received 'sendqort' state setSendqortState(message.payload); setExtstate("web-app-request-payment"); } else if (message.action === "closePopup" && !isMainWindow) { // Update the component state with the received 'sendqort' state window.close(); } else if ( message.action === "UPDATE_STATE_REQUEST_CONNECTION" && !isMainWindow ) { // Update the component state with the received 'sendqort' state setRequestConnection(message.payload); setExtstate("web-app-request-connection"); } else if ( message.action === "UPDATE_STATE_REQUEST_BUY_ORDER" && !isMainWindow ) { // Update the component state with the received 'sendqort' state setRequestBuyOrder(message.payload); setExtstate("web-app-request-buy-order"); } else if ( message.action === "UPDATE_STATE_REQUEST_AUTHENTICATION" && !isMainWindow ) { // Update the component state with the received 'sendqort' state setRequestAuthentication(message.payload); setExtstate("web-app-request-authentication"); } else if (message.action === "SET_COUNTDOWN" && !isMainWindow) { setCountdown(message.payload); } else if (message.action === "INITIATE_MAIN") { // Update the component state with the received 'sendqort' state setIsMain(true); isMainRef.current = true; } else if (message.action === "CHECK_FOCUS" && isMainWindow) { sendResponse(isFocusedRef.current); } else if ( message.action === "NOTIFICATION_OPEN_DIRECT" && isMainWindow ) { executeEvent("openDirectMessage", { from: message.payload.from, }); } else if (message.action === "NOTIFICATION_OPEN_GROUP" && isMainWindow) { executeEvent("openGroupMessage", { from: message.payload.from, }); } else if ( message.action === "NOTIFICATION_OPEN_ANNOUNCEMENT_GROUP" && isMainWindow ) { executeEvent("openGroupAnnouncement", { from: message.payload.from, }); } else if ( message.action === "NOTIFICATION_OPEN_THREAD_NEW_POST" && isMainWindow ) { executeEvent("openThreadNewPost", { data: message.payload.data, }); } }); }, []); //param = isDecline const confirmPayment = (isDecline: boolean) => { if (isDecline) { chrome?.runtime?.sendMessage( { action: "sendQortConfirmation", payload: { amount: sendqortState?.amount, receiver: sendqortState?.address, password: paymentPassword, interactionId: sendqortState?.interactionId, isDecline: true, }, }, (response) => { if (response) { setSendPaymentSuccess("Payment successfully sent"); } else { window.close(); } } ); return; } setIsLoading(true); chrome?.runtime?.sendMessage( { action: "sendQortConfirmation", payload: { amount: sendqortState.amount, receiver: sendqortState.address, password: paymentPassword, interactionId: sendqortState.interactionId, isDecline: false, }, }, (response) => { if (response === true) { setExtstate("transfer-success-request"); setCountdown(null); } else { setSendPaymentError( response?.error || "Unable to perform payment. Please try again." ); } setIsLoading(false); } ); }; const confirmBuyOrder = (isDecline: boolean) => { if (isDecline) { chrome?.runtime?.sendMessage( { action: "buyOrderConfirmation", payload: { crosschainAtInfo: requestBuyOrder?.crosschainAtInfo, interactionId: requestBuyOrder?.interactionId, isDecline: true, useLocal: requestBuyOrder?.useLocal }, }, (response) => { window.close(); } ); return; } setIsLoading(true); chrome?.runtime?.sendMessage( { action: "buyOrderConfirmation", payload: { crosschainAtInfo: requestBuyOrder?.crosschainAtInfo, interactionId: requestBuyOrder?.interactionId, isDecline: false, useLocal: requestBuyOrder?.useLocal }, }, (response) => { if (response === true) { setExtstate("buy-order-submitted"); setCountdown(null); } else { setSendPaymentError( response?.error || "Unable to perform payment. Please try again." ); } setIsLoading(false); } ); }; const responseToConnectionRequest = ( isOkay: boolean, hostname: string, interactionId: string ) => { chrome?.runtime?.sendMessage( { action: "responseToConnectionRequest", payload: { isOkay, hostname, interactionId }, }, (response) => { if (response === false || response === true) { window.close(); } } ); }; // const rawWalletRef = useRef(null) // useEffect(()=> { // rawWalletRef.current = rawWallet // }, [rawWallet]) useEffect(() => { try { setIsLoading(true); chrome?.runtime?.sendMessage({ action: "getWalletInfo" }, (response) => { if (response && response?.walletInfo) { setRawWallet(response?.walletInfo); if ( holdRefExtState.current === "web-app-request-payment" || holdRefExtState.current === "web-app-request-connection" || holdRefExtState.current === "web-app-request-buy-order" ) return; setExtstate("authenticated"); } }); } catch (error) { } finally { setIsLoading(false); } }, []); const getUserInfo = useCallback(async (useTimer?: boolean) => { try { if (useTimer) { await new Promise((res) => { setTimeout(() => { res(null); }, 10000); }); } chrome?.runtime?.sendMessage({ action: "userInfo" }, (response) => { if (response && !response.error) { setUserInfo(response); } }); getBalanceFunc(); } catch (error) {} }, []); useEffect(() => { if (!address) return; getUserInfo(); }, [address]); useEffect(() => { return () => { console.log("exit"); }; }, []); useEffect(() => { if ( authenticatedMode === "ltc" && !ltcBalanceLoading && ltcBalance === null ) { getLtcBalanceFunc(); } }, [authenticatedMode]); const confirmPasswordToDownload = async () => { try { setWalletToBeDownloadedError(""); if (!walletToBeDownloadedPassword) { setSendPaymentError("Please enter your password"); return; } setIsLoading(true); await new Promise((res) => { setTimeout(() => { res(); }, 250); }); const res = await saveWalletFunc(walletToBeDownloadedPassword); } catch (error: any) { setWalletToBeDownloadedError(error?.message); } finally { setIsLoading(false); } }; const saveFileToDiskFunc = async () => { try { await saveFileToDisk( walletToBeDownloaded.wallet, walletToBeDownloaded.qortAddress ); } catch (error: any) { setWalletToBeDownloadedError(error?.message); } finally { } }; const createAccountFunc = async () => { try { if (!walletToBeDownloadedPassword) { setWalletToBeDownloadedError("Please enter a password"); return; } if (!walletToBeDownloadedPasswordConfirm) { setWalletToBeDownloadedError("Please confirm your password"); return; } if ( walletToBeDownloadedPasswordConfirm !== walletToBeDownloadedPassword ) { setWalletToBeDownloadedError("Password fields do not match!"); return; } setIsLoading(true); await new Promise((res) => { setTimeout(() => { res(); }, 250); }); const res = await createAccount(); const wallet = await res.generateSaveWalletData( walletToBeDownloadedPassword, crypto.kdfThreads, () => {} ); chrome?.runtime?.sendMessage( { action: "decryptWallet", payload: { password: walletToBeDownloadedPassword, wallet, }, }, (response) => { if (response && !response?.error) { setRawWallet(wallet); setWalletToBeDownloaded({ wallet, qortAddress: wallet.address0, }); chrome?.runtime?.sendMessage({ action: "userInfo" }, (response2) => { setIsLoading(false); if (response2 && !response2.error) { setUserInfo(response); } }); getBalanceFunc(); } else if (response?.error) { setIsLoading(false); setWalletToBeDecryptedError(response.error); } } ); } catch (error: any) { setWalletToBeDownloadedError(error?.message); setIsLoading(false); } }; const logoutFunc = () => { try { chrome?.runtime?.sendMessage({ action: "logout" }, (response) => { if (response) { resetAllStates(); executeEvent("logout-event", {}); } }); } catch (error) {} }; const returnToMain = () => { setPaymentTo(""); setPaymentAmount(0); setPaymentPassword(""); setSendPaymentError(""); setSendPaymentSuccess(""); setCountdown(null); setWalletToBeDownloaded(null); setWalletToBeDownloadedPassword(""); setExtstate("authenticated"); setIsOpenSendQort(false) setIsOpenSendQortSuccess(false) }; const resetAllStates = () => { setExtstate("not-authenticated"); setAuthenticatedMode("qort"); setBackupjson(null); setRawWallet(null); setdecryptedWallet(null); setRequestConnection(null); setRequestBuyOrder(null); setRequestAuthentication(null); setUserInfo(null); setBalance(null); setLtcBalance(null); setPaymentTo(""); setPaymentAmount(0); setPaymentPassword(""); setSendPaymentError(""); setSendPaymentSuccess(""); setCountdown(null); setWalletToBeDownloaded(null); setWalletToBeDownloadedPassword(""); setWalletToBeDownloadedPasswordConfirm(""); setWalletToBeDownloadedError(""); setSendqortState(null); globalApiKey = null; setApiKey(""); setUseLocalNode(false); setHasLocalNode(false); setOpenAdvancedSettings(false); setConfirmUseOfLocal(false) setTxList([]) setMemberGroups([]) }; function roundUpToDecimals(number, decimals = 8) { const factor = Math.pow(10, decimals); // Create a factor based on the number of decimals return Math.ceil(+number * factor) / factor; } const authenticateWallet = async () => { try { setIsLoading(true); setWalletToBeDecryptedError(""); await new Promise((res) => { setTimeout(() => { res(); }, 250); }); chrome?.runtime?.sendMessage( { action: "decryptWallet", payload: { password: authenticatePassword, wallet: rawWallet, }, }, (response) => { if (response && !response?.error) { setAuthenticatePassword(""); setExtstate("authenticated"); setWalletToBeDecryptedError(""); chrome?.runtime?.sendMessage({ action: "userInfo" }, (response) => { setIsLoading(false); if (response && !response.error) { setUserInfo(response); } }); getBalanceFunc(); chrome?.runtime?.sendMessage( { action: "getWalletInfo" }, (response) => { if (response && response?.walletInfo) { setRawWallet(response?.walletInfo); } } ); } else if (response?.error) { setIsLoading(false); setWalletToBeDecryptedError(response.error); } } ); } catch (error) { setWalletToBeDecryptedError("Unable to authenticate. Wrong password"); } }; // const handleBeforeUnload = (e)=> { // const shouldClose = confirm('Are you sure you want to close this window? You may have unsaved changes.'); // if (!shouldClose) { // // Prevent the window from closing // e.preventDefault(); // e.returnValue = ''; // Required for Chrome // } else { // // Allow the window to close // // No need to call preventDefault here; returnValue must be left empty // } // } // useEffect(()=> { // window.addEventListener('beforeunload', handleBeforeUnload); // return ()=> { // window.removeEventListener('beforeunload', handleBeforeUnload); // } // }, []) useEffect(() => { if (!isMainWindow) return; const handleBeforeUnload = (e) => { e.preventDefault(); e.returnValue = ""; // This is required for Chrome to display the confirmation dialog. }; // Add the event listener when the component mounts window.addEventListener("beforeunload", handleBeforeUnload); // Clean up the event listener when the component unmounts return () => { window.removeEventListener("beforeunload", handleBeforeUnload); }; }, []); useEffect(() => { if (!isMainWindow) return; // Handler for when the window gains focus const handleFocus = () => { setIsFocused(true); if(isMobile){ chrome?.runtime?.sendMessage( { action: "clearAllNotifications", payload: { }, } ); } console.log("Webview is focused"); }; // Handler for when the window loses focus const handleBlur = () => { setIsFocused(false); console.log("Webview is not focused"); }; // Attach the event listeners window.addEventListener("focus", handleFocus); window.addEventListener("blur", handleBlur); // Optionally, listen for visibility changes const handleVisibilityChange = () => { if (document.visibilityState === "visible") { setIsFocused(true); if(isMobile){ chrome?.runtime?.sendMessage( { action: "clearAllNotifications", payload: { }, } ); } console.log("Webview is visible"); } else { setIsFocused(false); console.log("Webview is hidden"); } }; document.addEventListener("visibilitychange", handleVisibilityChange); // Cleanup the event listeners on component unmount return () => { window.removeEventListener("focus", handleFocus); window.removeEventListener("blur", handleBlur); document.removeEventListener("visibilitychange", handleVisibilityChange); }; }, []); const openPaymentInternal = (e) => { const directAddress = e.detail?.address; const name = e.detail?.name setIsOpenSendQort(true) setPaymentTo(name || directAddress) }; useEffect(() => { subscribeToEvent("openPaymentInternal", openPaymentInternal); return () => { unsubscribeFromEvent("openPaymentInternal", openPaymentInternal); }; }, []); const registerName = async () => { try { if (!userInfo?.address) throw new Error("Your address was not found"); 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) => { chrome?.runtime?.sendMessage( { action: "registerName", payload: { name: registerNameValue, }, }, (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) { if (error?.message) { setInfoSnack({ type: "error", message: error?.message, }); } } finally { setIsLoadingRegisterName(false); } }; const renderProfile = ()=> { return ( {isMobile && ( { setIsOpenDrawerProfile(false) }} sx={{ cursor: 'pointer', color: 'white' }} /> )} {authenticatedMode === "ltc" ? ( <> {rawWallet?.ltcAddress?.slice(0, 6)}... {rawWallet?.ltcAddress?.slice(-4)} {ltcBalanceLoading && ( )} {!isNaN(+ltcBalance) && !ltcBalanceLoading && ( {ltcBalance} LTC )} ) : ( <> {userInfo?.name} {rawWallet?.address0?.slice(0, 6)}... {rawWallet?.address0?.slice(-4)} {qortBalanceLoading && ( )} {!qortBalanceLoading && balance >= 0 && ( {balance?.toFixed(2)} QORT )} {userInfo && !userInfo?.name && ( { setOpenRegisterName(true); }} > REGISTER NAME )} { setIsOpenSendQort(true) // setExtstate("send-qort"); setIsOpenDrawerProfile(false) }} > Transfer QORT )} { chrome.tabs.create({ url: "https://www.qort.trade", active: true }); }} > Get QORT at qort.trade { setExtstate("download-wallet"); setIsOpenDrawerProfile(false) }} src={Download} style={{ cursor: "pointer", }} /> {!isMobile && ( <> { logoutFunc() setIsOpenDrawerProfile(false) }} style={{ cursor: "pointer", }} /> )} {authenticatedMode === "qort" && ( { setAuthenticatedMode("ltc"); }} src={ltcLogo} style={{ cursor: "pointer", width: "20px", height: "auto", }} /> )} {authenticatedMode === "ltc" && ( { setAuthenticatedMode("qort"); }} src={qortLogo} style={{ cursor: "pointer", width: "20px", height: "auto", }} /> )} ) } return ( {/* {extState === 'group' && ( )} */} {extState === "not-authenticated" && ( <>
WELCOME TO YOUR

QORTAL WALLET
Authenticate { setExtstate("create-wallet"); }} > Create account <> { setOpenAdvancedSettings(true); }} > Advanced settings {openAdvancedSettings && ( <> { setUseLocalNode(event.target.checked); }} disabled={confirmUseOfLocal} sx={{ "&.Mui-checked": { color: "white", // Customize the color when checked }, "& .MuiSvgIcon-root": { color: "white", }, }} /> Use local node {useLocalNode && ( <> {apiKey} )} )} )} {/* {extState !== "not-authenticated" && ( )} */} {extState === "authenticated" && isMainWindow && ( {!isMobile && renderProfile()} {!isMobile && ( )} )} {isOpenSendQort && isMainWindow && ( Transfer QORT Balance: {balance?.toFixed(2)} QORT To setPaymentTo(e.target.value)} autoComplete="off" /> Amount setPaymentAmount(+e.target.value)} autoComplete="off" /> Confirm Wallet Password setPaymentPassword(e.target.value)} autoComplete="off" /> {sendPaymentError} {/* {sendPaymentSuccess} */} { sendCoinFunc(); }} > Send )} {extState === "web-app-request-buy-order" && !isMainWindow && ( <> The Application

{" "} {requestBuyOrder?.hostname}

is requesting {requestBuyOrder?.crosschainAtInfo?.length} {`buy order${requestBuyOrder?.crosschainAtInfo.length === 1 ? '' : 's'}`}
{requestBuyOrder?.crosschainAtInfo?.reduce((latest, cur)=> { return latest + +cur?.qortAmount }, 0)} QORT FOR {roundUpToDecimals(requestBuyOrder?.crosschainAtInfo?.reduce((latest, cur)=> { return latest + +cur?.expectedForeignAmount }, 0))} {` ${requestBuyOrder?.crosschainAtInfo?.[0]?.foreignBlockchain}`} {/* Confirm Wallet Password setPaymentPassword(e.target.value)} /> */} confirmBuyOrder(false)} > accept confirmBuyOrder(true)} > decline {sendPaymentError} )} {extState === "web-app-request-payment" && !isMainWindow && ( <> The Application

{" "} {sendqortState?.hostname}

is requesting a payment
{sendqortState?.description} {sendqortState?.amount} QORT {/* Confirm Wallet Password setPaymentPassword(e.target.value)} /> */} confirmPayment(false)} > accept confirmPayment(true)} > decline {sendPaymentError} )} {extState === "web-app-request-connection" && !isMainWindow && ( <>
The Application

{" "} {requestConnection?.hostname}

is requestion a connection
responseToConnectionRequest( true, requestConnection?.hostname, requestConnection.interactionId ) } > accept responseToConnectionRequest( false, requestConnection?.hostname, requestConnection.interactionId ) } > decline )} {extState === "web-app-request-authentication" && !isMainWindow && ( <>
The Application

{" "} {requestConnection?.hostname}

requests authentication
Authenticate { setExtstate("create-wallet"); }} > Create account )} {rawWallet && extState === "wallet-dropped" && ( <> { setRawWallet(null); setExtstate("not-authenticated"); }} src={Return} />
Authenticate <> Wallet Password setAuthenticatePassword(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { authenticateWallet(); } }} /> Authenticate {walletToBeDecryptedError} )} {extState === "download-wallet" && ( <>
Download Wallet {!walletToBeDownloaded && ( <> Confirm Wallet Password setWalletToBeDownloadedPassword(e.target.value) } /> Confirm password {walletToBeDownloadedError} )} {walletToBeDownloaded && ( <> Download wallet )} )} {extState === "create-wallet" && ( <> {!walletToBeDownloaded && ( <> { setExtstate("not-authenticated"); }} src={Return} />
Set up your Qortal account Wallet Password setWalletToBeDownloadedPassword(e.target.value) } /> Confirm Wallet Password setWalletToBeDownloadedPasswordConfirm(e.target.value) } /> Create Account {walletToBeDownloadedError} )} {walletToBeDownloaded && ( <> Congrats, you’re all set up! { saveFileToDiskFunc(); returnToMain(); }} > Backup Account )} )} {isOpenSendQortSuccess && ( The transfer was succesful! { returnToMain(); }} > Continue )} {extState === "transfer-success-request" && ( <> The transfer was succesful! { window.close(); }} > Continue )} {extState === "buy-order-submitted" && ( <> Your buy order was submitted { window.close(); }} > Close )} {countdown && ( {/* */} { window.close(); }} size={75} strokeWidth={8} > {({ remainingTime }) => {remainingTime}} )} {isLoading && } {isShow && ( {"Publish"} {message.message} publish fee: {message.publishFee} )} { setOpenRegisterName(false); setRegisterNameValue(""); }} anchorOrigin={{ vertical: "bottom", horizontal: "center", }} transformOrigin={{ vertical: "top", horizontal: "center", }} style={{ marginTop: "8px" }} > setRegisterNameValue(e.target.value)} value={registerNameValue} placeholder="Choose a name" /> Register Name {renderProfile()}
); } export default App;