import React, { useState, useEffect, useMemo, useRef } from "react"; import { Link, useLocation, useNavigate } from "react-router-dom"; import Modal from "react-modal"; import { deleteAccount, deleteCaches, importAccount, setAccount, setActiveAccount, useAccount, useAccounts, useResolvedAuthorAddress } from "@plebbit/plebbit-react-hooks"; import stringify from "json-stringify-pretty-compact" import { StyledModal } from "../styled/modals/SettingsModal.styled"; import useError from "../../hooks/useError"; import useSuccess from "../../hooks/useSuccess"; import useAnonModeStore from '../../hooks/stores/useAnonModeStore'; import useGeneralStore from '../../hooks/stores/useGeneralStore'; import packageJson from '../../../package.json'; const {version} = packageJson; const commitRef = process?.env?.REACT_APP_COMMIT_REF ? ` ${process.env.REACT_APP_COMMIT_REF.slice(0, 7)}` : ''; const SettingsModal = ({ isOpen, closeModal }) => { const { selectedStyle } = useGeneralStore(state => state); const { anonymousMode, setAnonymousMode } = useAnonModeStore(); const navigate = useNavigate(); const location = useLocation(); const [expanded, setExpanded] = useState([]); const [copyStatus, setCopyStatus] = useState(false); const [ensName, setEnsName] = useState(''); const [checkedENS, setCheckedENS] = useState(false); const [, setNewErrorMessage] = useError(); const [, setNewSuccessMessage] = useSuccess(); const account = useAccount(); const { accounts } = useAccounts(); const accountJson = useMemo(() => { if (account) { const { plebbit, karma, unreadNotificationCount, ...restOfAccount } = account; return stringify({ account: restOfAccount }); } }, [account]); const [editedAccountJson, setEditedAccountJson] = useState(accountJson); useEffect(() => { setEditedAccountJson(accountJson)}, [accountJson]) const gatewayRef = useRef(); const ipfsRef = useRef(); const pubsubRef = useRef(); const ethereumRpcRef = useRef(); const polygonRpcRef = useRef(); const dataPathRef = useRef(); const importRef = useRef(); const nameRef = useRef(); const ensRef = useRef(); const isElectron = window.electron && window.electron.isElectron; const author = {...account?.author, address: ensName}; const { resolvedAddress, state } = useResolvedAuthorAddress({ author, cache: false }); const defaultGatewayUrls = isElectron ? undefined : [ 'https://ipfs.io', 'https://ipfsgateway.xyz', 'https://cloudflare-ipfs.com', 'https://plebpubsub.live' ]; const defaultPubsubHttpClientsOptions = isElectron ? undefined : [ 'https://pubsubprovider.xyz/api/v0', 'https://plebpubsub.live/api/v0' ]; const defaultEthereumRpcUrls = [ 'ethers.js', 'https://ethrpc.xyz', 'viem', ]; const defaultPolygonRpcUrls = [ 'https://polygon-rpc.com', ]; useEffect(() => { if (checkedENS && resolvedAddress && state === 'succeeded') { setCheckedENS(false); } }, [checkedENS, state, resolvedAddress]); const handleENSChange = () => { const newEnsName = ensRef.current.value; setEnsName(newEnsName); }; const handleENSCheck = () => { const newEnsName = ensRef.current.value; setEnsName(newEnsName); setCheckedENS(true); }; const handleENSSave = async () => { if (resolvedAddress === account?.signer?.address) { try { await setAccount({ ...account, author: { ...account?.author, address: ensName, }, }); setNewSuccessMessage("ENS name saved successfully."); } catch (error) { setNewErrorMessage(error.message); console.log(error); } } else if (resolvedAddress !== account?.signer?.address) { setNewErrorMessage(`Failed resolving address, ${ensName}`); } }; const isValidURL = (url) => { try { new URL(url); return true; } catch { return false; } }; const handleCopyAddress = async () => { try { await navigator.clipboard.writeText(account?.author.address); setCopyStatus(true); } catch (err) { console.error('Failed to copy text: ', err); } }; useEffect(() => { let copyStatusTimeoutId; let checkedENSTimeoutId; if (copyStatus) { copyStatusTimeoutId = setTimeout(() => setCopyStatus(false), 3000); } if (checkedENS) { checkedENSTimeoutId = setTimeout(() => setCheckedENS(false), 5000); } return () => { clearTimeout(copyStatusTimeoutId); clearTimeout(checkedENSTimeoutId); }; }, [copyStatus, checkedENS]); const handleSavePlebbitOptions = async () => { let gatewayUrls = gatewayRef.current.value.split('\n').filter((url) => url.trim()); let ipfsClientsOptions = ipfsRef.current.value.split('\n').filter(url => url.trim()); let pubsubClientsOptions = pubsubRef.current.value.split('\n').filter((url) => url.trim()); if (!isElectron) { if (!gatewayUrls.length) gatewayUrls = defaultGatewayUrls; if (!pubsubClientsOptions.length) pubsubClientsOptions = defaultPubsubHttpClientsOptions; } else { if (!gatewayUrls.length) gatewayUrls = undefined; if (!pubsubClientsOptions.length) pubsubClientsOptions = undefined; } if (!ipfsClientsOptions.length) { ipfsClientsOptions = undefined; } const invalidUrls = [ ...(Array.isArray(gatewayUrls) ? gatewayUrls : (Array.isArray(defaultGatewayUrls) ? defaultGatewayUrls : [])), ...(Array.isArray(ipfsClientsOptions) ? ipfsClientsOptions : []), ...(Array.isArray(pubsubClientsOptions) ? pubsubClientsOptions : (Array.isArray(defaultPubsubHttpClientsOptions) ? defaultPubsubHttpClientsOptions : [])), ].filter((url) => !isValidURL(url)); if (invalidUrls.length > 0) { setNewErrorMessage(`Invalid URL(s): ${invalidUrls.join(', ')}`); return; } const plebbitOptions = { ipfsGatewayUrls: gatewayUrls, ipfsHttpClientsOptions: ipfsClientsOptions, pubsubHttpClientsOptions: pubsubClientsOptions, }; try { await setAccount({ ...account, plebbitOptions }); localStorage.setItem("successToast", "Settings saved successfully."); window.location.reload(); } catch (error) { setNewErrorMessage(error.message); console.log(error); } }; const handleSaveChainProviders = async () => { let ethereumRpcUrls = ethereumRpcRef.current.value.split('\\n').filter(url => url.trim()); let polygonRpcUrls = polygonRpcRef.current.value.split('\\n').filter(url => url.trim()); if (!ethereumRpcUrls.length) { ethereumRpcUrls = defaultEthereumRpcUrls; } if (!polygonRpcUrls.length) { polygonRpcUrls = defaultPolygonRpcUrls; } const invalidUrls = [ ...ethereumRpcUrls || defaultEthereumRpcUrls, ...polygonRpcUrls || defaultPolygonRpcUrls, ].filter((url) => !isValidURL(url)); if (invalidUrls.length > 0) { setNewErrorMessage(`Invalid URL(s): ${invalidUrls.join(', ')}`); return; } const chainProviders = { 'eth': { urls: ethereumRpcUrls, chainId: 1, }, 'matic': { urls: polygonRpcUrls, chainId: 137, } } try { await setAccount({ ...account, plebbitOptions: { ...account?.plebbitOptions, chainProviders, }, }); localStorage.setItem("successToast", "Blockchain options saved successfully."); window.location.reload(); } catch (error) { setNewErrorMessage(error.message); console.log(error); } }; const handleResetPlebbitOptions = async () => { setNewErrorMessage(null); setNewSuccessMessage(null); gatewayRef.current.value = defaultGatewayUrls ? defaultGatewayUrls.join('\n') : ""; ipfsRef.current.value = ""; pubsubRef.current.value = defaultPubsubHttpClientsOptions ? defaultPubsubHttpClientsOptions.join('\n') : ""; dataPathRef.current.value = ""; try { await setAccount({ ...account, plebbitOptions: { ipfsGatewayUrls: defaultGatewayUrls, ipfsHttpClientsOptions: undefined, pubsubHttpClientsOptions: defaultPubsubHttpClientsOptions, }, }); localStorage.setItem("successToast", "Settings reset successfully."); window.location.reload(); } catch (error) { setNewErrorMessage(error.message); console.log(error); } }; const handleResetChainProviders = async () => { setNewErrorMessage(null); setNewSuccessMessage(null); ethereumRpcRef.current.value = defaultEthereumRpcUrls.join('\n'); polygonRpcRef.current.value = defaultPolygonRpcUrls.join('\n'); const chainProviders = { 'eth': { urls: defaultEthereumRpcUrls, chainId: 1, }, 'matic': { urls: defaultPolygonRpcUrls, chainId: 137, } } try { await setAccount({ ...account, plebbitOptions: { ...account?.plebbitOptions, chainProviders, }, }); localStorage.setItem("successToast", "Blockchain options reset successfully."); window.location.reload(); } catch (error) { setNewErrorMessage(error.message); console.log(error); } }; const handleCloseModal = () => { if (location.pathname.endsWith("/settings")) { const newPath = location.pathname.slice(0, -9); closeModal(); navigate(newPath, { replace: true }); } else { closeModal(); } }; const toggleExpanded = (index) => { setExpanded((prevExpanded) => { const newExpanded = [...prevExpanded]; if (newExpanded.includes(index)) { newExpanded.splice(newExpanded.indexOf(index), 1); } else { newExpanded.push(index); } return newExpanded; }); }; const expandAll = () => { if (expanded.length === 4) { setExpanded([]); } else { setExpanded([0, 1, 2, 3]); } }; useEffect(() => { if (localStorage.getItem("cacheCleared") === "true") { setNewSuccessMessage("Cache cleared successfully."); localStorage.removeItem("cacheCleared"); } }, [setNewSuccessMessage]); const handleSaveAccount = async () => { try { const parsedJson = JSON.parse(editedAccountJson); await setAccount(parsedJson); setNewSuccessMessage("Account data saved successfully."); } catch (error) { setNewErrorMessage("Error saving account data: " + error.message); console.error(error); } }; const handleImportAccount = async () => { const data = importRef.current.value; try { const parsedJson = JSON.parse(data); await importAccount(data); setActiveAccount(parsedJson.account?.name); setNewSuccessMessage("Account imported successfully."); } catch (error) { setNewErrorMessage(error.message); console.log(error); } }; const handleDeleteAccount = async () => { if (window.confirm("Are you sure you want to delete this account?")) { try { await deleteAccount(account?.name); localStorage.setItem("successToast", "Account deleted successfully."); window.location.reload(); } catch (error) { setNewErrorMessage("Error deleting account: " + error.message); console.error(error); } } }; const handleAccountChange = (e) => { setActiveAccount(e.target.value); }; const handleDisplayName = async () => { const name = nameRef.current.value; try { await setAccount({ ...account, name: name || account?.name, author: { ...account?.author, displayName: name, }}); setNewSuccessMessage("Account name saved successfully."); } catch (error) { setNewErrorMessage(error.message); console.log(error); } }; return (
v{version}{commitRef} -  {window.electron && window.electron.isElectron ? ( full node ) : (web) } Settings
[ ]