Skip to content

Commit

Permalink
Merge pull request #328 from rulfo71/feature/10-custom-rpcs
Browse files Browse the repository at this point in the history
Feature/10 custom rpcs
  • Loading branch information
rulfo71 authored Mar 28, 2023
2 parents f428f1e + d765940 commit 9c05d42
Show file tree
Hide file tree
Showing 17 changed files with 486 additions and 90 deletions.
15 changes: 15 additions & 0 deletions apps/davi/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@
"language": "Language",
"languages": "Languages"
},
"customRPC": {
"customRPC": "Custom RPC",
"customRPCUrl": "Custom RPC URL",
"enterURL": "Enter URL",
"useDefaultValue": "use default value",
"saveChanges": "Save Changes",
"genericError": "Seems to be a problem with the url provided",
"wrongChain": "The url provided is not from chain {{chainName}}",
"decentralizeMode": "Decentralize mode",
"decentralizeModeTooltip": "This will disable subgraph and only use RPCs. Some functions will be lost"
},
"decentralizeMode": {
"decentralizeMode": "Decentralize mode",
"decentralizeModeTooltip": "This will disable subgraph and only use RPCs. Some functions will be lost"
},
"connections": {
"switchWalletTo": "Switch Wallet to {{chainName}}",
"connectWallet": "Connect Wallet",
Expand Down
60 changes: 60 additions & 0 deletions apps/davi/src/components/CustomRPC/CustomRPC.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Button } from 'components/primitives/Button';
import { ErrorLabel } from 'components/primitives/Forms/ErrorLabel';
import styled from 'styled-components';

export const IconWrapper = styled.div<{ size?: number }>`
${({ theme }) => theme.flexColumnNoWrap};
align-items: center;
justify-content: center;
& > img,
span {
height: ${({ size }) => (size ? size + 'px' : '24px')};
width: ${({ size }) => (size ? size + 'px' : '24px')};
}
${({ theme }) => theme.mediaWidth.upToMedium`
align-items: flex-end;
`};
`;

export const ChainName = styled.div`
display: flex;
align-items: center;
color: ${({ theme }) => theme.colors.text};
margin-left: 0.5rem;
`;

export const ChainContainer = styled.div`
width: 100%;
display: flex;
justify-content: left;
align-items: center;
border: none;
&:focus {
border: none;
}
`;

export const ErrorText = styled(ErrorLabel)`
margin-top: 0.5rem;
`;

export const CustomRPCModal = styled.div`
text-align: -webkit-center;
padding: 1rem;
`;

export const ChainOptionContainer = styled.div`
background-color: ${({ theme }) => theme.colors.bg1};
padding: 1rem;
border-radius: ${({ theme }) => theme.radii.curved};
margin: 1rem 0;
`;

export const SaveButton = styled(Button)`
width: 100%;
font-weight: 600;
padding: 0.5rem 0;
`;
15 changes: 15 additions & 0 deletions apps/davi/src/components/CustomRPC/CustomRPC.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useNetwork } from 'wagmi';
import { CustomRPCOption } from './components';
import { CustomRPCModal } from './CustomRPC.styled';

export const CustomRPC: React.FC = () => {
const { chains } = useNetwork();

return (
<CustomRPCModal>
{chains.map(chain => (
<CustomRPCOption key={chain.id} chain={chain} />
))}
</CustomRPCModal>
);
};
48 changes: 48 additions & 0 deletions apps/davi/src/components/CustomRPC/components/CustomRPCHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useTranslation } from 'react-i18next';
import {
Toggle,
ToggleContainer,
ToggleLabel,
} from 'components/primitives/Forms/Toggle';
import { ChainContainer, ChainName, IconWrapper } from '../CustomRPC.styled';
import { Flex } from 'components/primitives/Layout';
import { Chain } from 'wagmi';

interface ChainOptionProps {
chain: Chain;
icon: string;
isDefaultValue: boolean;
setIsDefaultValue: (isDefaultValue: boolean) => void;
}

export const CustomRPCHeader: React.FC<ChainOptionProps> = ({
chain,
icon,
isDefaultValue,
setIsDefaultValue,
}) => {
const { t } = useTranslation();

const handleChange = () => {
localStorage.setItem(`customRPC[${chain.id}]`, '');
setIsDefaultValue(!isDefaultValue);
};

return (
<Flex direction="row">
<ChainContainer>
<IconWrapper>{icon && <img src={icon} alt={'Icon'} />}</IconWrapper>
<ChainName> {chain.name}</ChainName>
</ChainContainer>
<ToggleContainer width="100%">
<ToggleLabel>{t('customRPC.useDefaultValue')}</ToggleLabel>
<Toggle
value={isDefaultValue}
onChange={handleChange}
small
name="toggle-is-default-value"
/>
</ToggleContainer>
</Flex>
);
};
128 changes: 128 additions & 0 deletions apps/davi/src/components/CustomRPC/components/CustomRPCOption.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { JsonRpcProvider } from '@ethersproject/providers';
import {
Control,
ControlLabel,
ControlRow,
} from 'components/primitives/Forms/Control';
import { Input } from 'components/primitives/Forms/Input';
import { Loading } from 'components/primitives/Loading';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { getChainIcon } from 'utils';
import { Chain } from 'wagmi';
import {
ChainOptionContainer,
ErrorText,
SaveButton,
} from '../CustomRPC.styled';
import { CustomRPCHeader } from '.';

interface CustomRPCOptionProps {
chain: Chain;
}

interface LocalStorageRPC {
key: string;
value: string;
}

export const CustomRPCOption: React.FC<CustomRPCOptionProps> = ({ chain }) => {
const [error, setError] = useState('');
const { t } = useTranslation();
const localStorageRPCKey = `customRPC[${chain.id}]`;

const [localStorageRPC, setLocalStorageRPC] = useState<LocalStorageRPC>({
key: localStorageRPCKey,
value: localStorage.getItem(localStorageRPCKey) ?? '',
});

const [isDefaultValue, setIsDefaultValue] = useState(!localStorageRPC.value);
const [rpcValue, setRPCValue] = useState(localStorageRPC.value);
const [isLoading, setIsLoading] = useState(false);

useEffect(() => {
if (isDefaultValue) {
setRPCValue('');
}
}, [isDefaultValue]);

useEffect(() => {
localStorage.setItem(localStorageRPC.key, localStorageRPC.value);
}, [localStorageRPC]);

const validateRPCValue = async rpcValue => {
const genericError = t('customRPC.genericError');
try {
setIsLoading(true);
const ethersTestProvider = new JsonRpcProvider(rpcValue);
const network = await ethersTestProvider.getNetwork();
if (!ethersTestProvider || !network) {
throw new Error();
}
setIsLoading(false);
if (network.chainId !== chain.id) {
setIsLoading(false);
setError(t('customRPC.wrongChain', { chainName: chain.name }));
return false;
}
} catch (e) {
setIsLoading(false);
setError(genericError);
setLocalStorageRPC({ ...localStorageRPC, value: '' });
return false;
}

return true;
};

const handleRPCSave = async chain => {
setError('');
if (!(await validateRPCValue(rpcValue.trim()))) {
return;
}
setError('');
setLocalStorageRPC({ ...localStorageRPC, value: rpcValue.trim() });
};

return (
<ChainOptionContainer>
<CustomRPCHeader
chain={chain}
icon={getChainIcon(chain.id)}
isDefaultValue={isDefaultValue}
setIsDefaultValue={setIsDefaultValue}
/>
{!isDefaultValue && (
<Control>
<>
<ControlLabel>{t('customRPC.customRPCUrl')}</ControlLabel>
<ControlRow>
<Input
value={rpcValue}
data-testid="rpc input"
onChange={e => setRPCValue(e.target.value)}
aria-label={'rpc input'}
placeholder={t('customRPC.enterURL')}
/>
</ControlRow>
<ErrorText>{error}</ErrorText>
</>
{isLoading ? (
<Loading loading></Loading>
) : (
<SaveButton
variant="tertiary"
disabled={
rpcValue.trim() === '' ||
rpcValue === localStorage.getItem(localStorageRPCKey)
}
onClick={() => handleRPCSave(chain)}
>
{t('customRPC.saveChanges')}
</SaveButton>
)}
</Control>
)}
</ChainOptionContainer>
);
};
2 changes: 2 additions & 0 deletions apps/davi/src/components/CustomRPC/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { CustomRPCHeader } from './CustomRPCHeader';
export { CustomRPCOption } from './CustomRPCOption';
53 changes: 53 additions & 0 deletions apps/davi/src/components/DecentralizeMode/DecentralizeMode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useTranslation } from 'react-i18next';
import { useState } from 'react';
import { ReactComponent as Info } from 'assets/images/info.svg';
import { Tooltip } from 'components/Tooltip';
import { Toggle, ToggleContainer } from 'components/primitives/Forms/Toggle';
import { StyledIcon } from 'components/primitives/StyledIcon';
import {
WalletModalItem,
WalletModalItemTitle,
WalletModalItemValue,
} from 'components/Web3Modals/WalletModal/WalletModal.styled';
import { BiShapePolygon } from 'react-icons/bi';

export const DecentralizeMode: React.FC = () => {
const { t } = useTranslation();

const localStorageDecentralizeMode =
localStorage.getItem('decentralizeMode') === 'true';
const [isDecentralizeMode, setIsDecentralizeMode] = useState(
localStorageDecentralizeMode
);

const handleDecentralizeModeChanged = () => {
console.log('handleDecentralizeModeChanged');

localStorage.setItem('decentralizeMode', String(!isDecentralizeMode));
setIsDecentralizeMode(!isDecentralizeMode);
};
return (
<WalletModalItem>
<WalletModalItemTitle>
<BiShapePolygon size={24} />
{t('decentralizeMode.decentralizeMode')}
<Tooltip
text={t('decentralizeMode.decentralizeModeTooltip')}
placement="bottom"
>
<StyledIcon src={Info} />
</Tooltip>
</WalletModalItemTitle>
<WalletModalItemValue>
<ToggleContainer marginRight="0.3rem">
<Toggle
value={isDecentralizeMode}
onChange={handleDecentralizeModeChanged}
small
name="toggle-is-default-value"
/>
</ToggleContainer>
</WalletModalItemValue>
</WalletModalItem>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,6 @@ export const VoteActionButton = styled(Button)`
width: 100%;
font-weight: 600;
font-size: 16px;
background-color: ${({ theme }) => theme.colors.yellow};
color: ${({ theme }) => theme.colors.bg4};
:hover:enabled {
background-color: ${({ theme }) => theme.colors.darkGreen1};
border: 1px solid ${({ theme }) => theme.colors.yellow};
color: ${({ theme }) => theme.colors.yellow};
opacity: 1;
}
:disabled {
background-color: transparent;
border: 1px solid ${({ theme }) => theme.colors.border1};
color: ${({ theme }) => theme.colors.grey};
opacity: 1;
}
`;

export const VoteActionButtonContainer = styled.div`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ const ProposalVoteCard = ({
<VoteActionButtonContainer>
<VoteActionButton
disabled={!selectedOption}
variant="tertiary"
onClick={() =>
handleVoteOnProposal({
hasNoVotingPower,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ exports[`ProposalVoteCard matches the snapshot 1`] = `
-ms-flex-pack: end;
justify-content: flex-end;
margin-top: 0px;
margin-right: 0px;
gap: 10px;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const Language = styled.div`
cursor: pointer;
`;

export const LanguageBar = styled.div`
export const WalletModalItem = styled.div`
display: flex;
justify-content: space-between;
padding: 0px 24px;
Expand All @@ -58,12 +58,12 @@ export const LanguageBar = styled.div`
cursor: pointer;
`;

export const LanguageTitle = styled.div`
export const WalletModalItemTitle = styled.div`
display: flex;
gap: 12px;
align-items: center;
`;
export const LanguageValue = styled(LanguageTitle)`
export const WalletModalItemValue = styled(WalletModalItemTitle)`
cursor: pointer;
`;

Expand Down
Loading

1 comment on commit 9c05d42

@vercel
Copy link

@vercel vercel bot commented on 9c05d42 Mar 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.