2023-04-20 10:03:36 +02:00
import React , { useState , useEffect , useRef } from "react" ;
2023-03-15 15:25:09 +01:00
import { Link , useLocation , useNavigate } from "react-router-dom" ;
import Modal from "react-modal" ;
2023-05-10 14:55:06 +02:00
import { deleteCaches , exportAccount , importAccount , setAccount , setActiveAccount , useAccount , useAccounts } from "@plebbit/plebbit-react-hooks" ;
2023-05-24 12:15:43 +02:00
import { StyledModal } from "../styled/modals/SettingsModal.styled" ;
import useGeneralStore from "../../hooks/stores/useGeneralStore" ;
import useError from "../../hooks/useError" ;
import useSuccess from "../../hooks/useSuccess" ;
import packageJson from '../../../package.json'
2023-04-24 20:00:11 +00:00
const { version } = packageJson
2023-03-15 15:25:09 +01:00
const SettingsModal = ({ isOpen , closeModal }) => {
2023-05-26 17:25:49 +02:00
const {
selectedStyle ,
} = useGeneralStore ( state => state );
2023-03-15 15:25:09 +01:00
const navigate = useNavigate ();
const location = useLocation ();
2023-04-18 22:21:28 +02:00
const [ expanded , setExpanded ] = useState ([]);
2023-05-10 14:17:24 +02:00
const [ accountJson , setAccountJson ] = useState ( null );
2023-04-19 16:00:31 +02:00
const [ errorMessage , setErrorMessage ] = useState ( null );
const [ successMessage , setSuccessMessage ] = useState ( null );
useError ( errorMessage , [ errorMessage ]);
useSuccess ( successMessage , [ successMessage ]);
2023-03-15 15:25:09 +01:00
2023-04-20 10:03:36 +02:00
const account = useAccount ();
2023-05-10 14:55:06 +02:00
const { accounts } = useAccounts ();
2023-04-20 10:03:36 +02:00
const gatewayRef = useRef ();
const ipfsRef = useRef ();
const pubsubRef = useRef ();
const dataPathRef = useRef ();
2023-05-10 14:17:24 +02:00
const importRef = useRef ();
2023-05-26 17:25:49 +02:00
const nameRef = useRef ();
2023-04-20 10:03:36 +02:00
2023-04-20 13:59:04 +02:00
const isValidURL = ( url ) => {
try {
new URL ( url );
return true ;
} catch {
return false ;
}
};
const handleSavePlebbitOptions = async () => {
const gatewayUrls = gatewayRef . current . value . split ( '\n' ). filter ( url => url . trim ());
const ipfsClientsOptions = ipfsRef . current . value . split ( '\n' ). filter ( url => url . trim ()) || undefined ;
const pubsubClientsOptions = pubsubRef . current . value . split ( '\n' ). filter ( url => url . trim ());
const invalidUrls = [
... gatewayUrls ,
...( ipfsClientsOptions || []),
... pubsubClientsOptions ,
]. filter (( url ) => ! isValidURL ( url ));
if ( invalidUrls . length > 0 ) {
setErrorMessage ( `Invalid URL(s): ${ invalidUrls . join ( ', ' ) } ` );
return ;
}
const plebbitOptions = {
ipfsGatewayUrls : gatewayUrls ,
ipfsHttpClientsOptions : ipfsClientsOptions ,
pubsubHttpClientsOptions : pubsubClientsOptions ,
};
try {
await setAccount ({ ... account , plebbitOptions });
2023-05-16 13:26:05 +02:00
localStorage . setItem ( "successToast" , "Settings Saved" );
2023-04-20 13:59:04 +02:00
window . location . reload ();
} catch ( error ) {
setErrorMessage ( error . message );
}
};
const handleResetPlebbitOptions = async () => {
setErrorMessage ( null );
setSuccessMessage ( null );
const defaultGatewayUrls = [
'https://ipfs.io' ,
'https://ipfsgateway.xyz' ,
'https://cloudflare-ipfs.com'
];
const defaultPubsubHttpClientsOptions = [ 'https://pubsubprovider.xyz/api/v0' ];
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 ,
},
});
2023-05-16 13:26:05 +02:00
localStorage . setItem ( "successToast" , "Settings Reset" );
2023-04-20 13:59:04 +02:00
window . location . reload ();
} catch ( error ) {
setErrorMessage ( error . message );
}
};
2023-04-19 16:20:43 +02:00
2023-03-15 15:25:09 +01:00
const handleCloseModal = () => {
2023-05-10 14:17:24 +02:00
setAccountJson ( null );
2023-05-03 17:35:38 +02:00
if ( location . pathname . endsWith ( "/settings" )) {
const newPath = location . pathname . slice ( 0 , - 9 );
closeModal ();
navigate ( newPath , { replace : true });
2023-03-15 15:25:09 +01:00
} else {
2023-05-03 17:35:38 +02:00
closeModal ();
2023-03-15 15:25:09 +01:00
}
2023-05-03 17:35:38 +02:00
};
2023-03-15 15:25:09 +01:00
2023-04-19 16:20:43 +02:00
2023-04-18 22:21:28 +02:00
const toggleExpanded = ( index ) => {
setExpanded (( prevExpanded ) => {
const newExpanded = [... prevExpanded ];
if ( newExpanded . includes ( index )) {
newExpanded . splice ( newExpanded . indexOf ( index ), 1 );
} else {
newExpanded . push ( index );
}
return newExpanded ;
});
};
2023-04-19 16:20:43 +02:00
2023-04-18 22:21:28 +02:00
const expandAll = () => {
if ( expanded . length === 3 ) {
setExpanded ([]);
} else {
setExpanded ([ 0 , 1 , 2 ]);
}
};
2023-04-19 16:20:43 +02:00
2023-04-19 16:00:31 +02:00
useEffect (() => {
if ( localStorage . getItem ( "cacheCleared" ) === "true" ) {
setSuccessMessage ( "Cache Cleared" );
localStorage . removeItem ( "cacheCleared" );
}
}, []);
2023-05-10 14:17:24 +02:00
const handleExport = async () => {
const activeAccountJson = await exportAccount ();
setAccountJson ( activeAccountJson );
};
const handleImport = async () => {
const accountJson = importRef . current . value ;
try {
2023-05-16 13:26:05 +02:00
const parsedJson = JSON . parse ( accountJson );
2023-05-10 14:17:24 +02:00
await importAccount ( accountJson );
2023-05-16 13:26:05 +02:00
setActiveAccount ( parsedJson . account ? . name );
setSuccessMessage ( "Account Imported" );
2023-05-10 14:17:24 +02:00
} catch ( error ) {
setErrorMessage ( error . message );
}
};
2023-05-10 14:55:06 +02:00
const handleAccountChange = ( e ) => {
setActiveAccount ( e . target . value );
};
2023-05-26 17:25:49 +02:00
const handleDisplayName = async () => {
const name = nameRef . current . value ;
try {
await setAccount ({
... account ,
name : name || account . name ,
author : {
... account . author ,
displayName : name ,
}});
setSuccessMessage ( "Account Name Saved" );
} catch ( error ) {
setErrorMessage ( error . message );
}
};
2023-04-18 22:21:28 +02:00
2023-03-15 15:25:09 +01:00
return (
< StyledModal
isOpen = { isOpen }
2023-05-03 17:35:38 +02:00
onRequestClose = { handleCloseModal }
2023-03-15 15:25:09 +01:00
contentLabel = "Settings"
2023-05-10 14:17:24 +02:00
style = {{ overlay : { backgroundColor : "rgba(0,0,0,.25)" }}}
2023-03-15 15:25:09 +01:00
selectedStyle = { selectedStyle }
2023-04-18 22:21:28 +02:00
expanded = { expanded }
2023-03-15 15:25:09 +01:00
>
< div className = "panel" >
< div className = "panel-header" >
2023-04-22 13:11:42 +02:00
< span id = "version" >
2023-04-24 20:00:11 +00:00
v { version }
2023-04-22 13:11:42 +02:00
</ span >
2023-03-15 15:25:09 +01:00
Settings
< Link to = "" onClick = { handleCloseModal }>
< span className = "icon" title = "close" />
</ Link >
</ div >
2023-04-18 22:21:28 +02:00
< div className = "all-div" >
[
< button className = "all-button"
onClick = { expandAll } style = {{ all : "unset" , cursor : "pointer" }}>
{ expanded . length === 3 ? "Collapse All Settings" : "Expand All Settings" }
</ button >
]
</ div >
2023-04-20 10:03:36 +02:00
{ /* <ul>
2023-04-18 22:21:28 +02:00
<li className="settings-cat-lbl">
<span className={`${expanded.includes(0) ? 'minus' : 'plus'}`}
onClick={() => toggleExpanded(0)}
/>
<span className="settings-pointer" style={{cursor: "pointer"}}
onClick={() => toggleExpanded(0)}
>Account</span>
</li>
<ul className="settings-cat" style={{ display: expanded.includes(0) ? 'block' : 'none' }}>
<li>
</li>
</ul>
2023-05-09 12:59:00 +02:00
</ul>*/ }
2023-04-18 22:21:28 +02:00
< ul >
< li className = "settings-cat-lbl" >
< span className = { ` ${ expanded . includes ( 1 ) ? 'minus' : 'plus' } ` }
onClick = {() => toggleExpanded ( 1 )}
/>
< span className = "settings-pointer" style = {{ cursor : "pointer" }}
onClick = {() => toggleExpanded ( 1 )}
2023-05-09 12:59:00 +02:00
> Account </ span >
< div className = "plebbit-options-buttons"
style = {{ display : expanded . includes ( 1 ) ? 'block' : 'none' }}
>
</ div >
2023-04-18 22:21:28 +02:00
</ li >
< ul className = "settings-cat" style = {{ display : expanded . includes ( 1 ) ? 'block' : 'none' }}>
2023-05-10 14:17:24 +02:00
< li className = "settings-option disc" >
2023-05-26 17:25:49 +02:00
Account Data
2023-05-10 14:55:06 +02:00
</ li >
2023-05-26 17:25:49 +02:00
< div className = "plebbit-options-buttons" >
< button className = "save-button"
onClick = { handleExport }> Export </ button >
< button className = "reset-button"
onClick = { handleImport }
> Import </ button >
</ div >
2023-05-10 14:17:24 +02:00
< li className = "settings-tip" >
To export , click "Export" , then save your account data displayed below in a safe place . To import , paste your account data into the box below , then click "Import" .
2023-04-18 22:21:28 +02:00
</ li >
2023-05-10 14:17:24 +02:00
< div className = "settings-input" >
< textarea ref = { importRef } value = { accountJson } />
</ div >
2023-05-26 17:25:49 +02:00
< li className = "settings-option disc" style = {{ marginTop : '15px' }}>
Account Address : u / { account ? . author . shortAddress }
2023-05-10 14:55:06 +02:00
</ li >
< li className = "settings-tip" >
Select a different account to use in the dropdown below .
</ li >
< li >
< div className = "settings-input" >
< select className = "settings-select"
value = { account ? . name }
onChange = { handleAccountChange }
>
{ accounts . map (( account ) => (
< option key = { account ? . name } value = { account ? . name }>
{ account ? . name }
</ option >
))}
</ select >
</ div >
</ li >
2023-05-26 17:25:49 +02:00
< li className = "settings-option disc" style = {{ marginTop : '15px' }}>
Account Name
</ li >
< li className = "settings-tip" >
Change both your account name ( default "Account 1" ) and display name ( default "Anonymous" ). This will not change your address .
</ li >
< li >
< div className = "settings-input" >
< input className = "settings-input" style = {{ marginLeft : '20px' }}
type = "text" ref = { nameRef } defaultValue = { account ? . author . displayName }
placeholder = "Anonymous"
/>
< button className = "save-button" id = "save-name"
onClick = { handleDisplayName }> Save </ button >
</ div >
</ li >
</ ul >
2023-04-18 22:21:28 +02:00
</ ul >
< ul >
< li className = "settings-cat-lbl" >
2023-04-19 16:33:49 +02:00
< span className = { ` ${ expanded . includes ( 2 ) ? 'minus' : 'plus' } ` }
onClick = {() => toggleExpanded ( 2 )}
/>
< span className = "settings-pointer" style = {{ cursor : "pointer" }}
onClick = {() => toggleExpanded ( 2 )}
> Plebbit Options </ span >
< div className = "plebbit-options-buttons"
2023-05-09 12:59:00 +02:00
style = {{ display : expanded . includes ( 2 ) ? 'block' : 'none' }}
2023-04-19 16:33:49 +02:00
>
2023-05-10 14:17:24 +02:00
< button className = "save-button"
onClick = { handleSavePlebbitOptions }> Save </ button >
< button className = "reset-button"
onClick = { handleResetPlebbitOptions }> Reset </ button >
2023-05-09 12:59:00 +02:00
</ div >
2023-04-18 22:21:28 +02:00
</ li >
< ul className = "settings-cat" style = {{ display : expanded . includes ( 2 ) ? 'block' : 'none' }}>
< li className = "settings-option disc" >
2023-05-10 14:55:06 +02:00
IPFS Gateway URLs
</ li >
< li className = "settings-tip" >
Optional URLs of IPFS gateways .
</ li >
2023-04-18 22:21:28 +02:00
< div className = "settings-input" >
2023-04-19 14:00:45 +02:00
< textarea placeholder = "IPFS Gateway URLs"
2023-04-20 13:59:04 +02:00
defaultValue = { account ? . plebbitOptions ? . ipfsGatewayUrls . join ( "\n" )}
2023-04-20 10:03:36 +02:00
ref = { gatewayRef }
2023-04-19 13:56:38 +02:00
/>
2023-04-18 22:21:28 +02:00
</ div >
</ ul >
< ul className = "settings-cat" style = {{ display : expanded . includes ( 2 ) ? 'block' : 'none' }}>
< li className = "settings-option disc" >
2023-04-20 10:03:36 +02:00
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 >
2023-04-18 22:21:28 +02:00
< div className = "settings-input" >
2023-04-21 10:00:01 +02:00
< textarea placeholder = "IPFS HTTP Clients Options"
2023-04-20 13:59:04 +02:00
defaultValue = { account ? . plebbitOptions ? . ipfsHttpClientsOptions ? account ? . plebbitOptions ? . ipfsHttpClientsOptions . join ( "\n" ) : '' }
2023-04-20 10:03:36 +02:00
ref = { ipfsRef }
/>
2023-04-18 22:21:28 +02:00
</ div >
</ ul >
< ul className = "settings-cat" style = {{ display : expanded . includes ( 2 ) ? 'block' : 'none' }}>
< li className = "settings-option disc" >
2023-04-20 10:03:36 +02:00
PubSub HTTP Clients Options </ li >
< li className = "settings-tip" > Optional URLs or IpfsHttpClientOptions used for pubsub publishing when ipfsHttpClientOptions isn 't available, like in the browser.</li>
2023-04-18 22:21:28 +02:00
<div className="settings-input">
2023-04-21 10:00:01 +02:00
<textarea placeholder="PubSub HTTP Clients Options"
2023-04-20 13:59:04 +02:00
defaultValue={account?.plebbitOptions?.pubsubHttpClientsOptions.join("\n")}
2023-04-20 10:03:36 +02:00
ref={pubsubRef}
/>
2023-04-19 13:56:38 +02:00
</div>
</ul>
<ul className="settings-cat" style={{ display: expanded.includes(2) ? ' block ' : ' none ' }}>
<li className="settings-option disc">
Data Path (Node Only)</li>
2023-04-19 14:00:45 +02:00
<li className="settings-tip">Optional folder path to create/resume the user and subplebbit databases.</li>
2023-04-19 13:56:38 +02:00
<div className="settings-input">
2023-04-21 10:00:01 +02:00
<textarea placeholder="Data Path (Node Only)"
2023-04-20 10:03:36 +02:00
ref={dataPathRef}
/>
2023-04-18 22:21:28 +02:00
</div>
</ul>
2023-04-20 10:03:36 +02:00
{/* <ul className="settings-cat" style={{ display: expanded.includes(2) ? ' block ' : ' none ' }} >
< li className = "settings-option disc" >
Chain Providers </ li >
< li className = "settings-tip" > Optional provider RPC URLs and chain IDs .</ li >
< ul >
< li className = "settings-option disc" > Ethereum </ li >
< li className = "settings-option disc" > Avalanche </ li >
< li className = "settings-option disc" > Polygon </ li >
</ ul >
</ ul > * /}
2023-04-18 22:21:28 +02:00
</ ul >
2023-04-19 13:56:38 +02:00
< div >
2023-04-19 16:00:31 +02:00
< button
className = "cache-button"
onClick = { async () => {
if ( window . confirm ( "Are you sure you want to clear the cache?" )) {
await deleteCaches ();
localStorage . setItem ( "cacheCleared" , "true" );
window . location . reload ();
}
}}
>
Clear Cache
</ button >
2023-04-19 13:56:38 +02:00
</ div >
2023-03-15 15:25:09 +01:00
</ div >
</ StyledModal >
);
}
Modal . setAppElement ( "#root" );
export default SettingsModal ;