feat(SettingsModal): automatically disable anon mode and tell the user, detecting ENS name when importing account, saving account or saving ENS

This commit is contained in:
plebeius.eth
2023-09-23 13:21:58 +02:00
parent f0c0e64d75
commit c59bbb6f50
+260 -301
View File
@@ -1,24 +1,32 @@
import React, { useState, useEffect, useMemo, useRef } from "react"; import React, { useState, useEffect, useMemo, useRef } from 'react';
import { Link, useLocation, useNavigate } from "react-router-dom"; import { Link, useLocation, useNavigate } from 'react-router-dom';
import Modal from "react-modal"; import Modal from 'react-modal';
import { deleteAccount, deleteCaches, importAccount, setAccount, setActiveAccount, useAccount, useAccounts, useResolvedAuthorAddress } from "@plebbit/plebbit-react-hooks"; import {
import stringify from "json-stringify-pretty-compact" deleteAccount,
import { StyledModal } from "../styled/modals/SettingsModal.styled"; deleteCaches,
import useError from "../../hooks/useError"; importAccount,
import useSuccess from "../../hooks/useSuccess"; 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 useAnonModeStore from '../../hooks/stores/useAnonModeStore';
import useGeneralStore from '../../hooks/stores/useGeneralStore'; import useGeneralStore from '../../hooks/stores/useGeneralStore';
import packageJson from '../../../package.json'; import packageJson from '../../../package.json';
const {version} = packageJson; const { version } = packageJson;
const commitRef = process?.env?.REACT_APP_COMMIT_REF ? ` ${process.env.REACT_APP_COMMIT_REF.slice(0, 7)}` : ''; const commitRef = process?.env?.REACT_APP_COMMIT_REF ? ` ${process.env.REACT_APP_COMMIT_REF.slice(0, 7)}` : '';
const SettingsModal = ({ isOpen, closeModal }) => { const SettingsModal = ({ isOpen, closeModal }) => {
const { selectedStyle } = useGeneralStore(state => state); const { selectedStyle } = useGeneralStore((state) => state);
const { anonymousMode, setAnonymousMode } = useAnonModeStore(); const { anonymousMode, setAnonymousMode } = useAnonModeStore();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const [expanded, setExpanded] = useState([]); const [expanded, setExpanded] = useState([]);
const [copyStatus, setCopyStatus] = useState(false); const [copyStatus, setCopyStatus] = useState(false);
const [ensName, setEnsName] = useState(''); const [ensName, setEnsName] = useState('');
@@ -38,7 +46,9 @@ const SettingsModal = ({ isOpen, closeModal }) => {
}, [account]); }, [account]);
const [editedAccountJson, setEditedAccountJson] = useState(accountJson); const [editedAccountJson, setEditedAccountJson] = useState(accountJson);
useEffect(() => { setEditedAccountJson(accountJson)}, [accountJson]) useEffect(() => {
setEditedAccountJson(accountJson);
}, [accountJson]);
const gatewayRef = useRef(); const gatewayRef = useRef();
const ipfsRef = useRef(); const ipfsRef = useRef();
@@ -51,39 +61,22 @@ const SettingsModal = ({ isOpen, closeModal }) => {
const ensRef = useRef(); const ensRef = useRef();
const isElectron = window.electron && window.electron.isElectron; const isElectron = window.electron && window.electron.isElectron;
const author = {...account?.author, address: ensName}; const author = { ...account?.author, address: ensName };
const { resolvedAddress, state } = useResolvedAuthorAddress({ author, cache: false }); const { resolvedAddress, state } = useResolvedAuthorAddress({ author, cache: false });
const defaultGatewayUrls = isElectron ? undefined : [ const defaultGatewayUrls = isElectron ? undefined : ['https://ipfs.io', 'https://ipfsgateway.xyz', 'https://cloudflare-ipfs.com', 'https://plebpubsub.live'];
'https://ipfs.io',
'https://ipfsgateway.xyz',
'https://cloudflare-ipfs.com',
'https://plebpubsub.live'
];
const defaultPubsubHttpClientsOptions = isElectron ? undefined : [ const defaultPubsubHttpClientsOptions = isElectron ? undefined : ['https://pubsubprovider.xyz/api/v0', 'https://plebpubsub.live/api/v0'];
'https://pubsubprovider.xyz/api/v0',
'https://plebpubsub.live/api/v0'
];
const defaultEthereumRpcUrls = [ const defaultEthereumRpcUrls = ['ethers.js', 'https://ethrpc.xyz', 'viem'];
'ethers.js',
'https://ethrpc.xyz',
'viem',
];
const defaultPolygonRpcUrls = [
'https://polygon-rpc.com',
];
const defaultPolygonRpcUrls = ['https://polygon-rpc.com'];
useEffect(() => { useEffect(() => {
if (checkedENS && resolvedAddress && state === 'succeeded') { if (checkedENS && resolvedAddress && state === 'succeeded') {
setCheckedENS(false); setCheckedENS(false);
} }
}, [checkedENS, state, resolvedAddress]); }, [checkedENS, state, resolvedAddress]);
const handleENSChange = () => { const handleENSChange = () => {
const newEnsName = ensRef.current.value; const newEnsName = ensRef.current.value;
@@ -106,8 +99,8 @@ const SettingsModal = ({ isOpen, closeModal }) => {
address: ensName, address: ensName,
}, },
}); });
setNewSuccessMessage("ENS name saved successfully."); setAnonymousMode(false);
setNewSuccessMessage('ENS name saved successfully. Anon mode has been disabled.');
} catch (error) { } catch (error) {
setNewErrorMessage(error.message); setNewErrorMessage(error.message);
console.log(error); console.log(error);
@@ -117,7 +110,6 @@ const SettingsModal = ({ isOpen, closeModal }) => {
} }
}; };
const isValidURL = (url) => { const isValidURL = (url) => {
try { try {
new URL(url); new URL(url);
@@ -127,7 +119,6 @@ const SettingsModal = ({ isOpen, closeModal }) => {
} }
}; };
const handleCopyAddress = async () => { const handleCopyAddress = async () => {
try { try {
await navigator.clipboard.writeText(account?.author.address); await navigator.clipboard.writeText(account?.author.address);
@@ -135,8 +126,7 @@ const SettingsModal = ({ isOpen, closeModal }) => {
} catch (err) { } catch (err) {
console.error('Failed to copy text: ', err); console.error('Failed to copy text: ', err);
} }
}; };
useEffect(() => { useEffect(() => {
let copyStatusTimeoutId; let copyStatusTimeoutId;
@@ -148,17 +138,16 @@ const SettingsModal = ({ isOpen, closeModal }) => {
checkedENSTimeoutId = setTimeout(() => setCheckedENS(false), 5000); checkedENSTimeoutId = setTimeout(() => setCheckedENS(false), 5000);
} }
return () => { return () => {
clearTimeout(copyStatusTimeoutId); clearTimeout(copyStatusTimeoutId);
clearTimeout(checkedENSTimeoutId); clearTimeout(checkedENSTimeoutId);
}; };
}, [copyStatus, checkedENS]); }, [copyStatus, checkedENS]);
const handleSavePlebbitOptions = async () => { const handleSavePlebbitOptions = async () => {
let gatewayUrls = gatewayRef.current.value.split('\n').filter((url) => url.trim()); let gatewayUrls = gatewayRef.current.value.split('\n').filter((url) => url.trim());
let ipfsClientsOptions = ipfsRef.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()); let pubsubClientsOptions = pubsubRef.current.value.split('\n').filter((url) => url.trim());
if (!isElectron) { if (!isElectron) {
if (!gatewayUrls.length) gatewayUrls = defaultGatewayUrls; if (!gatewayUrls.length) gatewayUrls = defaultGatewayUrls;
if (!pubsubClientsOptions.length) pubsubClientsOptions = defaultPubsubHttpClientsOptions; if (!pubsubClientsOptions.length) pubsubClientsOptions = defaultPubsubHttpClientsOptions;
@@ -172,9 +161,9 @@ const SettingsModal = ({ isOpen, closeModal }) => {
} }
const invalidUrls = [ const invalidUrls = [
...(Array.isArray(gatewayUrls) ? gatewayUrls : (Array.isArray(defaultGatewayUrls) ? defaultGatewayUrls : [])), ...(Array.isArray(gatewayUrls) ? gatewayUrls : Array.isArray(defaultGatewayUrls) ? defaultGatewayUrls : []),
...(Array.isArray(ipfsClientsOptions) ? ipfsClientsOptions : []), ...(Array.isArray(ipfsClientsOptions) ? ipfsClientsOptions : []),
...(Array.isArray(pubsubClientsOptions) ? pubsubClientsOptions : (Array.isArray(defaultPubsubHttpClientsOptions) ? defaultPubsubHttpClientsOptions : [])), ...(Array.isArray(pubsubClientsOptions) ? pubsubClientsOptions : Array.isArray(defaultPubsubHttpClientsOptions) ? defaultPubsubHttpClientsOptions : []),
].filter((url) => !isValidURL(url)); ].filter((url) => !isValidURL(url));
if (invalidUrls.length > 0) { if (invalidUrls.length > 0) {
@@ -190,47 +179,44 @@ const SettingsModal = ({ isOpen, closeModal }) => {
try { try {
await setAccount({ ...account, plebbitOptions }); await setAccount({ ...account, plebbitOptions });
localStorage.setItem("successToast", "Settings saved successfully."); localStorage.setItem('successToast', 'Settings saved successfully.');
window.location.reload(); window.location.reload();
} catch (error) { } catch (error) {
setNewErrorMessage(error.message); console.log(error); setNewErrorMessage(error.message);
console.log(error);
} }
}; };
const handleSaveChainProviders = async () => { const handleSaveChainProviders = async () => {
let ethereumRpcUrls = ethereumRpcRef.current.value.split('\\n').filter(url => url.trim()); let ethereumRpcUrls = ethereumRpcRef.current.value.split('\\n').filter((url) => url.trim());
let polygonRpcUrls = polygonRpcRef.current.value.split('\\n').filter(url => url.trim()); let polygonRpcUrls = polygonRpcRef.current.value.split('\\n').filter((url) => url.trim());
if (!ethereumRpcUrls.length) { if (!ethereumRpcUrls.length) {
ethereumRpcUrls = defaultEthereumRpcUrls; ethereumRpcUrls = defaultEthereumRpcUrls;
} }
if (!polygonRpcUrls.length) { if (!polygonRpcUrls.length) {
polygonRpcUrls = defaultPolygonRpcUrls; polygonRpcUrls = defaultPolygonRpcUrls;
} }
const invalidUrls = [ const invalidUrls = [...(ethereumRpcUrls || defaultEthereumRpcUrls), ...(polygonRpcUrls || defaultPolygonRpcUrls)].filter((url) => !isValidURL(url));
...ethereumRpcUrls || defaultEthereumRpcUrls,
...polygonRpcUrls || defaultPolygonRpcUrls,
].filter((url) => !isValidURL(url));
if (invalidUrls.length > 0) { if (invalidUrls.length > 0) {
setNewErrorMessage(`Invalid URL(s): ${invalidUrls.join(', ')}`); setNewErrorMessage(`Invalid URL(s): ${invalidUrls.join(', ')}`);
return; return;
} }
const chainProviders = { const chainProviders = {
'eth': { eth: {
urls: ethereumRpcUrls, urls: ethereumRpcUrls,
chainId: 1, chainId: 1,
}, },
'matic': { matic: {
urls: polygonRpcUrls, urls: polygonRpcUrls,
chainId: 137, chainId: 137,
} },
} };
try { try {
await setAccount({ await setAccount({
...account, ...account,
@@ -239,22 +225,22 @@ const SettingsModal = ({ isOpen, closeModal }) => {
chainProviders, chainProviders,
}, },
}); });
localStorage.setItem("successToast", "Blockchain options saved successfully."); localStorage.setItem('successToast', 'Blockchain options saved successfully.');
window.location.reload(); window.location.reload();
} catch (error) { } catch (error) {
setNewErrorMessage(error.message); console.log(error); setNewErrorMessage(error.message);
console.log(error);
} }
}; };
const handleResetPlebbitOptions = async () => { const handleResetPlebbitOptions = async () => {
setNewErrorMessage(null); setNewErrorMessage(null);
setNewSuccessMessage(null); setNewSuccessMessage(null);
gatewayRef.current.value = defaultGatewayUrls ? defaultGatewayUrls.join('\n') : ""; gatewayRef.current.value = defaultGatewayUrls ? defaultGatewayUrls.join('\n') : '';
ipfsRef.current.value = ""; ipfsRef.current.value = '';
pubsubRef.current.value = defaultPubsubHttpClientsOptions ? defaultPubsubHttpClientsOptions.join('\n') : ""; pubsubRef.current.value = defaultPubsubHttpClientsOptions ? defaultPubsubHttpClientsOptions.join('\n') : '';
dataPathRef.current.value = ""; dataPathRef.current.value = '';
try { try {
await setAccount({ await setAccount({
@@ -265,32 +251,32 @@ const SettingsModal = ({ isOpen, closeModal }) => {
pubsubHttpClientsOptions: defaultPubsubHttpClientsOptions, pubsubHttpClientsOptions: defaultPubsubHttpClientsOptions,
}, },
}); });
localStorage.setItem("successToast", "Settings reset successfully."); localStorage.setItem('successToast', 'Settings reset successfully.');
window.location.reload(); window.location.reload();
} catch (error) { } catch (error) {
setNewErrorMessage(error.message); console.log(error); setNewErrorMessage(error.message);
console.log(error);
} }
}; };
const handleResetChainProviders = async () => { const handleResetChainProviders = async () => {
setNewErrorMessage(null); setNewErrorMessage(null);
setNewSuccessMessage(null); setNewSuccessMessage(null);
ethereumRpcRef.current.value = defaultEthereumRpcUrls.join('\n'); ethereumRpcRef.current.value = defaultEthereumRpcUrls.join('\n');
polygonRpcRef.current.value = defaultPolygonRpcUrls.join('\n'); polygonRpcRef.current.value = defaultPolygonRpcUrls.join('\n');
const chainProviders = { const chainProviders = {
'eth': { eth: {
urls: defaultEthereumRpcUrls, urls: defaultEthereumRpcUrls,
chainId: 1, chainId: 1,
}, },
'matic': { matic: {
urls: defaultPolygonRpcUrls, urls: defaultPolygonRpcUrls,
chainId: 137, chainId: 137,
} },
} };
try { try {
await setAccount({ await setAccount({
...account, ...account,
@@ -299,25 +285,23 @@ const SettingsModal = ({ isOpen, closeModal }) => {
chainProviders, chainProviders,
}, },
}); });
localStorage.setItem("successToast", "Blockchain options reset successfully."); localStorage.setItem('successToast', 'Blockchain options reset successfully.');
window.location.reload(); window.location.reload();
} catch (error) { } catch (error) {
setNewErrorMessage(error.message); console.log(error); setNewErrorMessage(error.message);
console.log(error);
} }
}; };
const handleCloseModal = () => { const handleCloseModal = () => {
if (location.pathname.endsWith("/settings")) { if (location.pathname.endsWith('/settings')) {
const newPath = location.pathname.slice(0, -9); const newPath = location.pathname.slice(0, -9);
closeModal(); closeModal();
navigate(newPath, { replace: true }); navigate(newPath, { replace: true });
} else { } else {
closeModal(); closeModal();
} }
}; };
const toggleExpanded = (index) => { const toggleExpanded = (index) => {
setExpanded((prevExpanded) => { setExpanded((prevExpanded) => {
@@ -331,7 +315,6 @@ const SettingsModal = ({ isOpen, closeModal }) => {
}); });
}; };
const expandAll = () => { const expandAll = () => {
if (expanded.length === 4) { if (expanded.length === 4) {
setExpanded([]); setExpanded([]);
@@ -339,29 +322,33 @@ const SettingsModal = ({ isOpen, closeModal }) => {
setExpanded([0, 1, 2, 3]); setExpanded([0, 1, 2, 3]);
} }
}; };
useEffect(() => { useEffect(() => {
if (localStorage.getItem("cacheCleared") === "true") { if (localStorage.getItem('cacheCleared') === 'true') {
setNewSuccessMessage("Cache cleared successfully."); setNewSuccessMessage('Cache cleared successfully.');
localStorage.removeItem("cacheCleared"); localStorage.removeItem('cacheCleared');
} }
}, [setNewSuccessMessage]); }, [setNewSuccessMessage]);
const handleSaveAccount = async () => { const handleSaveAccount = async () => {
const data = importRef.current.value; const data = importRef.current.value;
const oldData = accountJson;
try { try {
const parsedJson = JSON.parse(data); const parsedJson = JSON.parse(data);
await setAccount(parsedJson.account); await setAccount(parsedJson.account);
setNewSuccessMessage("Account data saved successfully.");
if (!oldData.account?.author?.address.endsWith('.eth') && parsedJson.account?.author?.address.endsWith('.eth') && anonymousMode === true) {
setAnonymousMode(false);
setNewSuccessMessage('Account data saved successfully. ENS address detected, Anon mode disabled.');
} else {
setNewSuccessMessage('Account data saved successfully.');
}
} catch (error) { } catch (error) {
setNewErrorMessage("Error saving account data: " + error.message); setNewErrorMessage('Error saving account data: ' + error.message);
console.error(error); console.error(error);
} }
}; };
const handleImportAccount = async () => { const handleImportAccount = async () => {
const data = importRef.current.value; const data = importRef.current.value;
@@ -369,28 +356,32 @@ const SettingsModal = ({ isOpen, closeModal }) => {
const parsedJson = JSON.parse(data); const parsedJson = JSON.parse(data);
await importAccount(data); await importAccount(data);
setActiveAccount(parsedJson.account?.name); setActiveAccount(parsedJson.account?.name);
setNewSuccessMessage("Account imported successfully.");
if (parsedJson.account?.author?.address.endsWith('.eth') && anonymousMode === true) {
setAnonymousMode(false);
setNewSuccessMessage('Account imported successfully. ENS address detected, Anon mode disabled.');
} else {
setNewSuccessMessage('Account imported successfully.');
}
} catch (error) { } catch (error) {
setNewErrorMessage(error.message); console.log(error); setNewErrorMessage(error.message);
console.log(error);
} }
}; };
const handleDeleteAccount = async () => { const handleDeleteAccount = async () => {
if (window.confirm("Are you sure you want to delete this account?")) { if (window.confirm('Are you sure you want to delete this account?')) {
try { try {
await deleteAccount(account?.name); await deleteAccount(account?.name);
localStorage.setItem("successToast", "Account deleted successfully."); localStorage.setItem('successToast', 'Account deleted successfully.');
window.location.reload(); window.location.reload();
} catch (error) { } catch (error) {
setNewErrorMessage("Error deleting account: " + error.message); setNewErrorMessage('Error deleting account: ' + error.message);
console.error(error); console.error(error);
} }
} }
}; };
const handleAccountChange = (e) => { const handleAccountChange = (e) => {
setActiveAccount(e.target.value); setActiveAccount(e.target.value);
}; };
@@ -399,181 +390,165 @@ const SettingsModal = ({ isOpen, closeModal }) => {
const name = nameRef.current.value; const name = nameRef.current.value;
try { try {
await setAccount({ await setAccount({
...account, ...account,
name: name || account?.name, name: name || account?.name,
author: { author: {
...account?.author, ...account?.author,
displayName: name, displayName: name,
}}); },
setNewSuccessMessage("Account name saved successfully."); });
setNewSuccessMessage('Account name saved successfully.');
} catch (error) { } catch (error) {
setNewErrorMessage(error.message); console.log(error); setNewErrorMessage(error.message);
console.log(error);
} }
}; };
return ( return (
<StyledModal <StyledModal
isOpen={isOpen} isOpen={isOpen}
onRequestClose={handleCloseModal} onRequestClose={handleCloseModal}
contentLabel="Settings" contentLabel='Settings'
style={{ overlay: { backgroundColor: "rgba(0,0,0,.25)" }}} style={{ overlay: { backgroundColor: 'rgba(0,0,0,.25)' } }}
selectedStyle={selectedStyle} selectedStyle={selectedStyle}
expanded={expanded} expanded={expanded}
> >
<div className="panel"> <div className='panel'>
<div className="panel-header"> <div className='panel-header'>
<span id="version"> <span id='version'>
v{version}{commitRef} -&nbsp; v{version}
{commitRef} -&nbsp;
{window.electron && window.electron.isElectron ? ( {window.electron && window.electron.isElectron ? (
<Link className="all-button" id="node-stats" to="http://localhost:5001/webui/" target="_blank" rel="noreferrer"> <Link className='all-button' id='node-stats' to='http://localhost:5001/webui/' target='_blank' rel='noreferrer'>
full node full node
</Link> </Link>
) : (<span>web</span>) ) : (
} <span>web</span>
)}
</span> </span>
Settings Settings
<Link to="" onClick={handleCloseModal}> <Link to='' onClick={handleCloseModal}>
<span className="icon" title="close" /> <span className='icon' title='close' />
</Link> </Link>
</div> </div>
<div className="all-div"> <div className='all-div'>
[ [
<button className="all-button" <button className='all-button' onClick={expandAll} style={{ all: 'unset', cursor: 'pointer' }}>
onClick={expandAll} style={{all: "unset", cursor: "pointer"}}> {expanded.length === 4 ? 'Collapse All Settings' : 'Expand All Settings'}
{expanded.length === 4 ? "Collapse All Settings" : "Expand All Settings"}
</button> </button>
] ]
</div> </div>
<ul> <ul>
<li className="settings-cat-lbl"> <li className='settings-cat-lbl'>
<span className={`${expanded.includes(1) ? 'minus' : 'plus'}`} <span className={`${expanded.includes(1) ? 'minus' : 'plus'}`} onClick={() => toggleExpanded(1)} />
onClick={() => toggleExpanded(1)} <span className='settings-pointer' style={{ cursor: 'pointer' }} onClick={() => toggleExpanded(1)}>
/> Account
<span className="settings-pointer" style={{cursor: "pointer"}} </span>
onClick={() => toggleExpanded(1)}
>Account</span>
</li> </li>
<ul className="settings-cat" style={{ display: expanded.includes(1) ? 'block' : 'none' }}> <ul className='settings-cat' style={{ display: expanded.includes(1) ? 'block' : 'none' }}>
<li className="anon-off"> <li className='anon-off'>
<label title={ !anonymousMode ? <label title={!anonymousMode ? 'Enable anon mode' : 'Disable anon mode'}>
"Enable anon mode" : <input
"Disable anon mode" type='checkbox'
}> checked={anonymousMode}
<input onChange={() => {
type="checkbox" setAnonymousMode(!anonymousMode);
checked={anonymousMode} window.location.reload();
onChange={() => {setAnonymousMode(!anonymousMode); window.location.reload();}} }}
/> />
&nbsp;Anon Mode &nbsp;Anon Mode
</label> </label>
</li> </li>
<li className="settings-tip anon-tip"> <li className='settings-tip anon-tip'>Use a newly generated u/address per thread to post.</li>
Use a newly generated u/address per thread to post. <li className='settings-option disc'>Account Data</li>
<li className='settings-tip'>
To save manual changes, click "Save". To undo changes, click "Reset". To delete the account and create a new one, click "Delete". To add another account,
paste its data and click "Import".
</li> </li>
<li className="settings-option disc"> <div className='settings-input'>
Account Data <textarea
</li> id='account-data-text'
<li className="settings-tip"> value={editedAccountJson || accountJson}
To save manual changes, click "Save". To undo changes, click "Reset". To delete the account and create a new one, click "Delete". To add another account, paste its data and click "Import". ref={importRef}
</li> onChange={(e) => setEditedAccountJson(e.target.value)}
<div className="settings-input"> autoComplete='off'
<textarea id="account-data-text" autoCorrect='off'
value={editedAccountJson || accountJson} spellCheck='false'
ref={importRef} />
onChange={(e) => setEditedAccountJson(e.target.value)} <div className='account-buttons'>
autoComplete="off" <button onClick={handleSaveAccount}>Save</button>
autoCorrect="off" <button onClick={() => setEditedAccountJson(accountJson)}>Reset</button>
spellCheck="false" /> <button onClick={handleImportAccount}>Import</button>
<div className="account-buttons"> <button onClick={handleDeleteAccount}>Delete</button>
<button onClick={handleSaveAccount}>
Save
</button>
<button onClick={() => setEditedAccountJson(accountJson)}>
Reset
</button>
<button onClick={handleImportAccount}>
Import
</button>
<button onClick={handleDeleteAccount}>
Delete
</button>
</div> </div>
</div> </div>
<li className="settings-option disc"> <li className='settings-option disc'>Account Address: u/{account?.author.shortAddress}</li>
Account Address: u/{account?.author.shortAddress} <li className='settings-tip'>Select a different account to use in the dropdown below.</li>
</li>
<li className="settings-tip">
Select a different account to use in the dropdown below.
</li>
<li> <li>
<div className="settings-input"> <div className='settings-input'>
<select className="settings-select" <select className='settings-select' value={account?.name} onChange={handleAccountChange}>
value={account?.name}
onChange={handleAccountChange}
>
{accounts.map((account) => ( {accounts.map((account) => (
<option key={account?.name} value={account?.name}> <option key={account?.name} value={account?.name}>
{account?.name} {account?.name}
</option> </option>
))} ))}
</select> </select>
{account?.author.address.endsWith(".eth") ? null : ( {account?.author.address.endsWith('.eth') ? null : (
<button style={{marginLeft: '35px'}} className="save-button" id="save-name" onClick={handleCopyAddress}> <button style={{ marginLeft: '35px' }} className='save-button' id='save-name' onClick={handleCopyAddress}>
{copyStatus ? "Copied!" : "Copy Full Address"} {copyStatus ? 'Copied!' : 'Copy Full Address'}
</button> </button>
)} )}
</div> </div>
</li> </li>
<li className="settings-option disc"> <li className='settings-option disc'>Crypto Address</li>
Crypto Address <li className='settings-tip'>
{account?.author.address.endsWith('.eth')
? 'Your account address is already an ENS name.'
: 'Change your account address to an ENS name you own: in your ENS name page on ens.domains, click on "Records", "Edit Records", "Add record", add "plebbit-author-address" as record name, add your full address as value (copy it with the button above) and save.'}
</li> </li>
<li className="settings-tip"> <div className='settings-input'>
{account?.author.address.endsWith(".eth") ? "Your account address is already an ENS name." : ( <input
'Change your account address to an ENS name you own: in your ENS name page on ens.domains, click on "Records", "Edit Records", "Add record", add "plebbit-author-address" as record name, add your full address as value (copy it with the button above) and save.' className='settings-input'
)} style={{ marginLeft: '20px' }}
</li> placeholder='address.eth'
<div className="settings-input">
<input
className="settings-input"
style={{marginLeft: '20px'}}
placeholder="address.eth"
ref={ensRef} ref={ensRef}
value={ensName} value={ensName}
onChange={handleENSChange} onChange={handleENSChange}
disabled={checkedENS} disabled={checkedENS}
/> />
<button className="save-button" id="save-name" onClick={handleENSSave}> <button className='save-button' id='save-name' onClick={handleENSSave}>
Save Save
</button> </button>
<button className="save-button check-button" id="save-name" onClick={handleENSCheck}> <button className='save-button check-button' id='save-name' onClick={handleENSCheck}>
Check Check
</button> </button>
</div> </div>
{checkedENS && ensName === account?.signer?.address && ( {checkedENS && ensName === account?.signer?.address && (
<li className="settings-tip" style={{marginTop: '10px', color: 'green'}}> <li className='settings-tip' style={{ marginTop: '10px', color: 'green' }}>
{ensName} has been acquired by you correctly. {ensName} has been acquired by you correctly.
</li> </li>
)} )}
{checkedENS && ensName !== account?.signer?.address && ( {checkedENS && ensName !== account?.signer?.address && (
<li className="settings-tip" style={{marginTop: '10px', color: 'red'}}> <li className='settings-tip' style={{ marginTop: '10px', color: 'red' }}>
{ensName} has not been acquired by you yet. {ensName} has not been acquired by you yet.
</li> </li>
)} )}
<li className="settings-option disc"> <li className='settings-option disc'>Account Name</li>
Account Name <li className='settings-tip'>
</li>
<li className="settings-tip">
Change both your account name (default "Account 1") and display name (default "Anonymous"). This will not change your address. Change both your account name (default "Account 1") and display name (default "Anonymous"). This will not change your address.
</li> </li>
<li> <li>
<div className="settings-input"> <div className='settings-input'>
<input className="settings-input" style={{marginLeft: '20px'}} <input
type="text" ref={nameRef} defaultValue={account?.author.displayName} className='settings-input'
placeholder="Anonymous" style={{ marginLeft: '20px' }}
type='text'
ref={nameRef}
defaultValue={account?.author.displayName}
placeholder='Anonymous'
/> />
<button className="save-button" id="save-name" onClick={handleDisplayName}> <button className='save-button' id='save-name' onClick={handleDisplayName}>
Save Save
</button> </button>
</div> </div>
@@ -581,110 +556,94 @@ const SettingsModal = ({ isOpen, closeModal }) => {
</ul> </ul>
</ul> </ul>
<ul> <ul>
<li className="settings-cat-lbl"> <li className='settings-cat-lbl'>
<span className={`${expanded.includes(2) ? 'minus' : 'plus'}`} <span className={`${expanded.includes(2) ? 'minus' : 'plus'}`} onClick={() => toggleExpanded(2)} />
onClick={() => toggleExpanded(2)} <span className='settings-pointer' style={{ cursor: 'pointer' }} onClick={() => toggleExpanded(2)}>
/> IPFS Options
<span className="settings-pointer" style={{cursor: "pointer"}} </span>
onClick={() => toggleExpanded(2)} <div className='plebbit-options-buttons' style={{ display: expanded.includes(2) ? 'block' : 'none' }}>
>IPFS Options</span> <button className='save-button' onClick={handleSavePlebbitOptions}>
<div className="plebbit-options-buttons" Save
style={{ display: expanded.includes(2) ? 'block' : 'none' }} </button>
> <button className='reset-button' onClick={handleResetPlebbitOptions}>
<button className="save-button" Reset
onClick={handleSavePlebbitOptions}>Save</button> </button>
<button className="reset-button"
onClick={handleResetPlebbitOptions}>Reset</button>
</div> </div>
</li> </li>
<ul className="settings-cat" style={{ display: expanded.includes(2) ? 'block' : 'none', marginTop: '-10px' }}> <ul className='settings-cat' style={{ display: expanded.includes(2) ? 'block' : 'none', marginTop: '-10px' }}>
<li className="settings-option disc"> <li className='settings-option disc'>IPFS Gateway URLs</li>
IPFS Gateway URLs <li className='settings-tip'>Optional URLs of IPFS gateways.</li>
</li> <div className='settings-input'>
<li className="settings-tip"> <textarea placeholder='IPFS Gateway URLs' defaultValue={isElectron ? '' : account?.plebbitOptions?.ipfsGatewayUrls.join('\n')} ref={gatewayRef} />
Optional URLs of IPFS gateways.
</li>
<div className="settings-input">
<textarea placeholder="IPFS Gateway URLs"
defaultValue={isElectron ? '' : account?.plebbitOptions?.ipfsGatewayUrls.join('\n')}
ref={gatewayRef}
/>
</div> </div>
</ul> </ul>
<ul className="settings-cat" style={{ display: expanded.includes(2) ? 'block' : 'none' }}> <ul className='settings-cat' style={{ display: expanded.includes(2) ? 'block' : 'none' }}>
<li className="settings-option disc"> <li className='settings-option disc'>IPFS HTTP Clients Options</li>
IPFS HTTP Clients Options</li> <li className='settings-tip'>Optional URLs of IPFS APIs or IpfsHttpClientOptions, 'http://localhost:5001/api/v0' to use a local IPFS node.</li>
<li className="settings-tip">Optional URLs of IPFS APIs or IpfsHttpClientOptions, 'http://localhost:5001/api/v0' to use a local IPFS node.</li> <div className='settings-input'>
<div className="settings-input"> <textarea
<textarea placeholder="IPFS HTTP Clients Options" placeholder='IPFS HTTP Clients Options'
defaultValue={account?.plebbitOptions?.ipfsHttpClientsOptions ? account?.plebbitOptions?.ipfsHttpClientsOptions.join("\n") : ''} defaultValue={account?.plebbitOptions?.ipfsHttpClientsOptions ? account?.plebbitOptions?.ipfsHttpClientsOptions.join('\n') : ''}
ref={ipfsRef} ref={ipfsRef}
/> />
</div> </div>
</ul> </ul>
<ul className="settings-cat" style={{ display: expanded.includes(2) ? 'block' : 'none' }}> <ul className='settings-cat' style={{ display: expanded.includes(2) ? 'block' : 'none' }}>
<li className="settings-option disc"> <li className='settings-option disc'>PubSub HTTP Clients Options</li>
PubSub HTTP Clients Options</li> <li className='settings-tip'>
<li className="settings-tip">Optional URLs or IpfsHttpClientOptions used for pubsub publishing when ipfsHttpClientOptions isn't available, like in the browser.</li> Optional URLs or IpfsHttpClientOptions used for pubsub publishing when ipfsHttpClientOptions isn't available, like in the browser.
<div className="settings-input"> </li>
<textarea placeholder="PubSub HTTP Clients Options" <div className='settings-input'>
<textarea
placeholder='PubSub HTTP Clients Options'
defaultValue={isElectron ? '' : account?.plebbitOptions?.pubsubHttpClientsOptions.join('\n')} defaultValue={isElectron ? '' : account?.plebbitOptions?.pubsubHttpClientsOptions.join('\n')}
ref={pubsubRef} ref={pubsubRef}
/> />
</div> </div>
</ul> </ul>
<ul className="settings-cat" style={{ display: expanded.includes(2) ? 'block' : 'none' }}> <ul className='settings-cat' style={{ display: expanded.includes(2) ? 'block' : 'none' }}>
<li className="settings-option disc"> <li className='settings-option disc'>Data Path (Node Only)</li>
Data Path (Node Only)</li> <li className='settings-tip'>Optional folder path to create/resume the user and subplebbit databases.</li>
<li className="settings-tip">Optional folder path to create/resume the user and subplebbit databases.</li> <div className='settings-input'>
<div className="settings-input"> <textarea placeholder='Data Path (Node Only)' ref={dataPathRef} />
<textarea placeholder="Data Path (Node Only)"
ref={dataPathRef}
/>
</div> </div>
</ul> </ul>
</ul> </ul>
<ul> <ul>
<li className="settings-cat-lbl"> <li className='settings-cat-lbl'>
<span className={`${expanded.includes(3) ? 'minus' : 'plus'}`} <span className={`${expanded.includes(3) ? 'minus' : 'plus'}`} onClick={() => toggleExpanded(3)} />
onClick={() => toggleExpanded(3)} <span className='settings-pointer' style={{ cursor: 'pointer' }} onClick={() => toggleExpanded(3)}>
/> Blockchain Options
<span className="settings-pointer" style={{cursor: "pointer"}} </span>
onClick={() => toggleExpanded(3)} <div className='plebbit-options-buttons' style={{ display: expanded.includes(3) ? 'block' : 'none' }}>
>Blockchain Options</span> <button className='save-button' onClick={handleSaveChainProviders}>
<div className="plebbit-options-buttons" Save
style={{ display: expanded.includes(3) ? 'block' : 'none' }} </button>
> <button className='save-button' onClick={handleResetChainProviders}>
<button className="save-button" onClick={handleSaveChainProviders}>Save</button> Reset
<button className="save-button" onClick={handleResetChainProviders}>Reset</button> </button>
</div> </div>
</li> </li>
<ul className="settings-cat" style={{ display: expanded.includes(3) ? 'block' : 'none', marginTop: '-10px'}}> <ul className='settings-cat' style={{ display: expanded.includes(3) ? 'block' : 'none', marginTop: '-10px' }}>
<li className="settings-option disc">Ethereum RPC</li> <li className='settings-option disc'>Ethereum RPC</li>
<li className="settings-tip">Needed for .eth addresses.</li> <li className='settings-tip'>Needed for .eth addresses.</li>
<div className="settings-input"> <div className='settings-input'>
<textarea placeholder="Ethereum RPC URLs" <textarea placeholder='Ethereum RPC URLs' defaultValue={account?.plebbitOptions?.chainProviders?.['eth']?.urls.join('\n')} ref={ethereumRpcRef} />
defaultValue={account?.plebbitOptions?.chainProviders?.['eth']?.urls.join("\n")}
ref={ethereumRpcRef}
/>
</div> </div>
<li className="settings-option disc">Polygon RPC</li> <li className='settings-option disc'>Polygon RPC</li>
<li className="settings-tip">Needed for XPLEB NFTs.</li> <li className='settings-tip'>Needed for XPLEB NFTs.</li>
<div className="settings-input"> <div className='settings-input'>
<textarea placeholder="Polygon RPC URLs" <textarea placeholder='Polygon RPC URLs' defaultValue={account?.plebbitOptions?.chainProviders?.['matic']?.urls.join('\n')} ref={polygonRpcRef} />
defaultValue={account?.plebbitOptions?.chainProviders?.['matic']?.urls.join("\n")}
ref={polygonRpcRef}
/>
</div> </div>
</ul> </ul>
</ul> </ul>
<div> <div>
<button <button
className="cache-button" className='cache-button'
onClick={async () => { onClick={async () => {
if (window.confirm("Are you sure you want to clear the cache?")) { if (window.confirm('Are you sure you want to clear the cache?')) {
await deleteCaches(); await deleteCaches();
localStorage.setItem("cacheCleared", "true"); localStorage.setItem('cacheCleared', 'true');
window.location.reload(); window.location.reload();
} }
}} }}
@@ -695,8 +654,8 @@ const SettingsModal = ({ isOpen, closeModal }) => {
</div> </div>
</StyledModal> </StyledModal>
); );
} };
Modal.setAppElement("#root"); Modal.setAppElement('#root');
export default SettingsModal; export default SettingsModal;