import React, { useState, useEffect, useRef } from "react"; import { Link, useLocation, useNavigate } from "react-router-dom"; import Modal from "react-modal"; import { deleteCaches, exportAccount, importAccount, setAccount, setActiveAccount, useAccount, useAccounts, useResolvedAuthorAddress } from "@plebbit/plebbit-react-hooks"; 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 SettingsModal = ({ isOpen, closeModal }) => { const { selectedStyle, } = useGeneralStore(state => state); const { anonymousMode, setAnonymousMode } = useAnonModeStore(); const navigate = useNavigate(); const location = useLocation(); const [expanded, setExpanded] = useState([]); const [accountJson, setAccountJson] = useState(null); 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 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 author = {...account?.author, address: ensName}; const { resolvedAddress, state } = useResolvedAuthorAddress({ author, cache: false }); 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"); } catch (error) { setNewErrorMessage(error.message); console.log(error); } } else if (resolvedAddress !== account?.signer?.address) { setNewErrorMessage(`Failed resolving address, ${ensName}`); } }; const defaultGatewayUrls = [ 'https://ipfs.io', 'https://ipfsgateway.xyz', 'https://cloudflare-ipfs.com', 'https://plebpubsub.live' ]; const defaultPubsubHttpClientsOptions = [ 'https://pubsubprovider.xyz/api/v0', 'https://plebpubsub.live/api/v0' ]; const defaultEthereumRpcUrls = [ 'ethers.js', 'https://ethrpc.xyz', 'viem', ]; const defaultPolygonRpcUrls = [ 'https://polygon-rpc.com', ]; 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 (!gatewayUrls.length) { gatewayUrls = defaultGatewayUrls; } if (!ipfsClientsOptions.length) { ipfsClientsOptions = undefined; } if (!pubsubClientsOptions.length) { pubsubClientsOptions = defaultPubsubHttpClientsOptions; } const invalidUrls = [ ...gatewayUrls || defaultGatewayUrls, ...(ipfsClientsOptions || []), ...pubsubClientsOptions || 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"); 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"); window.location.reload(); } catch (error) { setNewErrorMessage(error.message); console.log(error); } }; const handleResetPlebbitOptions = async () => { setNewErrorMessage(null); setNewSuccessMessage(null); gatewayRef.current.value = defaultGatewayUrls.join('\n'); ipfsRef.current.value = ""; pubsubRef.current.value = defaultPubsubHttpClientsOptions.join('\n'); dataPathRef.current.value = ""; try { await setAccount({ ...account, plebbitOptions: { ipfsGatewayUrls: defaultGatewayUrls, ipfsHttpClientsOptions: undefined, pubsubHttpClientsOptions: defaultPubsubHttpClientsOptions, }, }); localStorage.setItem("successToast", "Settings Reset"); 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"); window.location.reload(); } catch (error) { setNewErrorMessage(error.message); console.log(error); } }; const handleCloseModal = () => { setAccountJson(null); 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"); localStorage.removeItem("cacheCleared"); } }, [setNewSuccessMessage]); const handleExport = async () => { const activeAccountJson = await exportAccount(); setAccountJson(activeAccountJson); }; const handleImport = async () => { const accountJson = importRef.current.value; try { const parsedJson = JSON.parse(accountJson); await importAccount(accountJson); setActiveAccount(parsedJson.account?.name); setNewSuccessMessage("Account Imported"); } catch (error) { setNewErrorMessage(error.message); console.log(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"); } catch (error) { setNewErrorMessage(error.message); console.log(error); } }; return (
v{version} -  {window.electron && window.electron.isElectron ? ( full node ) : (web) } Settings
[ ]