Files
5chan/src/components/settings-modal/advanced-settings/advanced-settings.tsx
T

313 lines
10 KiB
TypeScript

import { memo, RefObject, useRef, useState } from 'react';
import { setAccount, useAccount, usePlebbitRpcSettings } from '@bitsocialhq/bitsocial-react-hooks';
import { useTranslation } from 'react-i18next';
import styles from './advanced-settings.module.css';
interface SettingsProps {
ipfsGatewayUrlsRef?: RefObject<HTMLTextAreaElement>;
mediaIpfsGatewayUrlRef?: RefObject<HTMLInputElement>;
pubsubProvidersRef?: RefObject<HTMLTextAreaElement>;
httpRoutersRef?: RefObject<HTMLTextAreaElement>;
ethRpcRef?: RefObject<HTMLTextAreaElement>;
solRpcRef?: RefObject<HTMLTextAreaElement>;
p2pRpcRef?: RefObject<HTMLInputElement>;
p2pDataPathRef?: RefObject<HTMLInputElement>;
}
const IPFSGatewaysSettings = ({ ipfsGatewayUrlsRef, mediaIpfsGatewayUrlRef }: SettingsProps) => {
const account = useAccount();
const { plebbitOptions, mediaIpfsGatewayUrl } = account || {};
const { ipfsGatewayUrls } = plebbitOptions || {};
const plebbitRpc = usePlebbitRpcSettings();
const isConnectedToRpc = plebbitRpc?.state === 'connected';
const ipfsGatewayUrlsDefaultValue = ipfsGatewayUrls?.join('\n');
return (
<div className={styles.ipfsGatewaysSettings}>
<div className={styles.ipfsGatewaysSetting}>
<textarea
defaultValue={ipfsGatewayUrlsDefaultValue}
ref={ipfsGatewayUrlsRef}
disabled={isConnectedToRpc}
autoCorrect='off'
autoComplete='off'
spellCheck='false'
rows={ipfsGatewayUrls?.length || 1}
/>
</div>
<span className={styles.settingTip}>media IPFS gateway</span>
<div>
<input
type='text'
defaultValue={mediaIpfsGatewayUrl}
ref={mediaIpfsGatewayUrlRef}
disabled={isConnectedToRpc}
autoCorrect='off'
autoCapitalize='off'
spellCheck='false'
/>
</div>
</div>
);
};
const PubsubProvidersSettings = ({ pubsubProvidersRef }: SettingsProps) => {
const account = useAccount();
const { plebbitOptions } = account || {};
const { pubsubHttpClientsOptions } = plebbitOptions || {};
const plebbitRpc = usePlebbitRpcSettings();
const isConnectedToRpc = plebbitRpc?.state === 'connected';
const pubsubProvidersDefaultValue = pubsubHttpClientsOptions?.join('\n');
return (
<div className={styles.pubsubProvidersSettings}>
<textarea
defaultValue={pubsubProvidersDefaultValue}
ref={pubsubProvidersRef}
disabled={isConnectedToRpc}
autoCorrect='off'
autoCapitalize='off'
autoComplete='off'
spellCheck='false'
rows={pubsubHttpClientsOptions?.length || 1}
/>
</div>
);
};
const HttpRoutersSettings = ({ httpRoutersRef }: SettingsProps) => {
const account = useAccount();
const { plebbitOptions } = account || {};
const { httpRoutersOptions } = plebbitOptions || {};
const plebbitRpc = usePlebbitRpcSettings();
const isConnectedToRpc = plebbitRpc?.state === 'connected';
const httpRoutersDefaultValue = httpRoutersOptions?.join('\n');
return (
<div className={styles.httpRoutersSettings}>
<textarea
defaultValue={httpRoutersDefaultValue}
ref={httpRoutersRef}
disabled={isConnectedToRpc}
autoCorrect='off'
autoCapitalize='off'
autoComplete='off'
spellCheck='false'
rows={httpRoutersOptions?.length || 1}
/>
</div>
);
};
const BlockchainProvidersSettings = ({ ethRpcRef, solRpcRef }: SettingsProps) => {
const account = useAccount();
const { plebbitOptions } = account || {};
const { chainProviders } = plebbitOptions || {};
const ethRpcDefaultValue = chainProviders?.['eth']?.urls.join('\n');
const solRpcDefaultValue = chainProviders?.['sol']?.urls.join('\n');
return (
<div className={styles.blockchainProvidersSettings}>
<span className={styles.settingTip}>Ethereum RPC, for .eth domains</span>
<div>
<textarea
defaultValue={ethRpcDefaultValue}
ref={ethRpcRef}
autoCorrect='off'
autoComplete='off'
spellCheck='false'
rows={chainProviders?.['eth']?.urls?.length || 1}
/>
</div>
<span className={styles.settingTip}>Solana RPC, for .sol domains</span>
<div>
<textarea
defaultValue={solRpcDefaultValue}
ref={solRpcRef}
autoCorrect='off'
autoComplete='off'
spellCheck='false'
rows={chainProviders?.['sol']?.urls?.length || 1}
/>
</div>
</div>
);
};
const P2pRPCSettings = ({ p2pRpcRef }: SettingsProps) => {
const [showInfo, setShowInfo] = useState(false);
const account = useAccount();
const { plebbitOptions } = account || {};
const { plebbitRpcClientsOptions } = plebbitOptions || {};
return (
<div className={styles.p2pRPCSettings}>
<div>
<input type='text' defaultValue={plebbitRpcClientsOptions} ref={p2pRpcRef} autoCorrect='off' autoCapitalize='off' spellCheck='false' />
<button onClick={() => setShowInfo(!showInfo)}>{showInfo ? 'X' : '?'}</button>
</div>
{showInfo && (
<div className={styles.p2pRpcSettingsInfo}>
use a P2P full node locally, or remotely with SSL
<br />
<ol>
<li>get secret auth key from the node</li>
<li>get IP address and port used by the node</li>
<li>
enter: <code>{`ws://<IP>:<port>/<secretAuthKey>`}</code>
</li>
<li>click save to connect</li>
</ol>
</div>
)}
</div>
);
};
const P2pDataPathSettings = ({ p2pDataPathRef }: SettingsProps) => {
const plebbitRpc = usePlebbitRpcSettings();
const { plebbitRpcSettings } = plebbitRpc || {};
const isConnectedToRpc = plebbitRpc?.state === 'connected';
const path = plebbitRpcSettings?.plebbitOptions?.dataPath || '';
return (
<div className={styles.p2pDataPathSettings}>
<div>
<input autoCorrect='off' autoCapitalize='off' spellCheck='false' type='text' defaultValue={path} disabled={!isConnectedToRpc} ref={p2pDataPathRef} />
</div>
</div>
);
};
const isElectron = window.electronApi?.isElectron === true;
const AdvancedSettings = () => {
const { t } = useTranslation();
const account = useAccount();
const { plebbitOptions } = account || {};
const ipfsGatewayUrlsRef = useRef<HTMLTextAreaElement>(null);
const mediaIpfsGatewayUrlRef = useRef<HTMLInputElement>(null);
const pubsubProvidersRef = useRef<HTMLTextAreaElement>(null);
const ethRpcRef = useRef<HTMLTextAreaElement>(null);
const solRpcRef = useRef<HTMLTextAreaElement>(null);
const httpRoutersRef = useRef<HTMLTextAreaElement>(null);
const p2pRpcRef = useRef<HTMLInputElement>(null);
const p2pDataPathRef = useRef<HTMLInputElement>(null);
const handleSave = async () => {
const ipfsGatewayUrls = ipfsGatewayUrlsRef.current?.value
.split('\n')
.map((url) => url.trim())
.filter((url) => url !== '');
const mediaIpfsGatewayUrl = mediaIpfsGatewayUrlRef.current?.value.trim();
const pubsubHttpClientsOptions = pubsubProvidersRef.current?.value
.split('\n')
.map((url) => url.trim())
.filter((url) => url !== '');
const ethRpcUrls = ethRpcRef.current?.value
.split('\n')
.map((url) => url.trim())
.filter((url) => url !== '');
const solRpcUrls = solRpcRef.current?.value
.split('\n')
.map((url) => url.trim())
.filter((url) => url !== '');
const httpRoutersOptions = httpRoutersRef.current?.value
.split('\n')
.map((url) => url.trim())
.filter((url) => url !== '');
const plebbitRpcClientsOptions = p2pRpcRef.current?.value.trim() ? [p2pRpcRef.current.value.trim()] : undefined;
const dataPath = p2pDataPathRef.current?.value.trim() || undefined;
const chainProviders: Record<string, { urls: string[] | undefined; chainId: number }> = {};
if (ethRpcUrls && ethRpcUrls.length > 0) {
chainProviders.eth = { urls: ethRpcUrls, chainId: 1 };
}
if (solRpcUrls && solRpcUrls.length > 0) {
chainProviders.sol = { urls: solRpcUrls, chainId: 101 };
}
try {
await setAccount({
...account,
mediaIpfsGatewayUrl,
plebbitOptions: {
...plebbitOptions,
ipfsGatewayUrls,
pubsubHttpClientsOptions,
chainProviders,
httpRoutersOptions,
plebbitRpcClientsOptions,
dataPath,
},
});
alert('Options saved, reloading...');
window.location.reload();
} catch (e) {
if (e instanceof Error) {
alert('Error saving options: ' + e.message);
console.log(e);
} else {
alert('Error');
}
}
};
return (
<div className={styles.content}>
<button className={styles.saveOptions} onClick={handleSave}>
{t('save_options')}
</button>
<div className={styles.category}>
<span className={styles.categoryTitle}>IPFS gateways:</span>
<span className={styles.categorySettings}>
<IPFSGatewaysSettings ipfsGatewayUrlsRef={ipfsGatewayUrlsRef} mediaIpfsGatewayUrlRef={mediaIpfsGatewayUrlRef} />
</span>
</div>
<div className={styles.category}>
<span className={styles.categoryTitle}>pubsub providers:</span>
<span className={styles.categorySettings}>
<PubsubProvidersSettings pubsubProvidersRef={pubsubProvidersRef} />
</span>
</div>
<div className={styles.category}>
<span className={styles.categoryTitle}>http routers:</span>
<span className={styles.categorySettings}>
<HttpRoutersSettings httpRoutersRef={httpRoutersRef} />
</span>
</div>
<div className={styles.category}>
<span className={styles.categoryTitle} style={{ marginBottom: '-5px' }}>
blockchain providers:
</span>
<span className={styles.categorySettings}>
<BlockchainProvidersSettings ethRpcRef={ethRpcRef} solRpcRef={solRpcRef} />
</span>
</div>
<div className={styles.category}>
<span className={styles.categoryTitle}>Node RPC:</span>
<span className={styles.categorySettings}>
<P2pRPCSettings p2pRpcRef={p2pRpcRef} />
</span>
</div>
{isElectron && (
<div className={styles.category}>
<span className={styles.categoryTitle}>p2p data path:</span>
<span className={styles.categorySettings}>
<P2pDataPathSettings p2pDataPathRef={p2pDataPathRef} />
</span>
</div>
)}
</div>
);
};
export default memo(AdvancedSettings);