Add and remove common base

This commit is contained in:
Aadhinana 2021-09-23 16:15:42 +05:30
parent 9c53747071
commit 4b1de9c7e8
4 changed files with 175 additions and 45 deletions

View File

@ -1,4 +1,4 @@
import { useState } from "react"; import { useState, useMemo } from "react";
import { import {
PublicKey, PublicKey,
Keypair, Keypair,
@ -222,6 +222,16 @@ export function SwapTokenForm({
}) })
: amount; : amount;
const tokenDialog = useMemo(() => {
return (
<TokenDialog
setMint={setMint}
open={showTokenDialog}
onClose={() => setShowTokenDialog(false)}
/>
);
}, [showTokenDialog]);
return ( return (
<div className={styles.swapTokenFormContainer} style={style}> <div className={styles.swapTokenFormContainer} style={style}>
<div className={styles.swapTokenSelectorContainer}> <div className={styles.swapTokenSelectorContainer}>
@ -252,11 +262,7 @@ export function SwapTokenForm({
}, },
}} }}
/> />
<TokenDialog {tokenDialog}
setMint={setMint}
open={showTokenDialog}
onClose={() => setShowTokenDialog(false)}
/>
</div> </div>
); );
} }

View File

@ -10,14 +10,16 @@ import {
TextField, TextField,
List, List,
ListItem, ListItem,
ListSubheader,
Typography, Typography,
Chip, Chip,
Avatar, Avatar,
Tabs, Tabs,
Tab, Tab,
} from "@material-ui/core"; } from "@material-ui/core";
import { StarOutline, Star } from "@material-ui/icons";
import { TokenIcon } from "./Swap"; import { TokenIcon } from "./Swap";
import { useSwappableTokens } from "../context/TokenList"; import { useSwappableTokens, useTokenBase } from "../context/TokenList";
import { useMediaQuery } from "@material-ui/core"; import { useMediaQuery } from "@material-ui/core";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
@ -54,12 +56,9 @@ export default function TokenDialog({
const [tokenFilter, setTokenFilter] = useState(""); const [tokenFilter, setTokenFilter] = useState("");
const filter = tokenFilter.toLowerCase(); const filter = tokenFilter.toLowerCase();
const styles = useStyles(); const styles = useStyles();
const { const { swappableTokens, swappableTokensSollet, swappableTokensWormhole } =
swappableTokens, useSwappableTokens();
swappableTokensSollet, const { tokenBase, addNewBase, tokenBaseMap, removeBase } = useTokenBase();
swappableTokensWormhole,
commonTokenBases,
} = useSwappableTokens();
const displayTabs = !useMediaQuery("(max-width:450px)"); const displayTabs = !useMediaQuery("(max-width:450px)");
const selectedTokens = const selectedTokens =
tabSelection === 0 tabSelection === 0
@ -103,13 +102,17 @@ export default function TokenDialog({
</DialogTitle> </DialogTitle>
<DialogContent className={styles.dialogContent} dividers={true}> <DialogContent className={styles.dialogContent} dividers={true}>
<List disablePadding> <List disablePadding>
<CommonBases {tokenBase?.length != 0 && (
commonTokenBases={commonTokenBases} <ListSubheader style={{ backgroundColor: "white" }}>
onClick={(mint) => { <CommonBases
setMint(mint); commonTokenBases={tokenBase}
onClose(); onClick={(mint) => {
}} setMint(mint);
/> onClose();
}}
/>
</ListSubheader>
)}
{tokens.map((tokenInfo: TokenInfo) => ( {tokens.map((tokenInfo: TokenInfo) => (
<TokenListItem <TokenListItem
key={tokenInfo.address} key={tokenInfo.address}
@ -118,6 +121,15 @@ export default function TokenDialog({
setMint(mint); setMint(mint);
onClose(); onClose();
}} }}
addNewBase={(token) => {
addNewBase(token);
}}
isCommonBase={
tokenBaseMap.get(tokenInfo.address.toString()) ? true : false
}
removeBase={(token) => {
removeBase(token);
}}
/> />
))} ))}
</List> </List>
@ -159,19 +171,41 @@ export default function TokenDialog({
function TokenListItem({ function TokenListItem({
tokenInfo, tokenInfo,
onClick, onClick,
addNewBase,
removeBase,
isCommonBase,
}: { }: {
tokenInfo: TokenInfo; tokenInfo: TokenInfo;
onClick: (mint: PublicKey) => void; onClick: (mint: PublicKey) => void;
addNewBase: (token: TokenInfo) => void;
removeBase: (token: TokenInfo) => void;
isCommonBase: Boolean;
}) { }) {
const mint = new PublicKey(tokenInfo.address); const mint = new PublicKey(tokenInfo.address);
return ( return (
<ListItem <ListItem>
button <div
onClick={() => onClick(mint)} onClick={() => onClick(mint)}
style={{ padding: "10px 20px" }} style={{
> padding: "10px 20px",
<TokenIcon mint={mint} style={{ width: "30px", borderRadius: "15px" }} /> display: "flex",
<TokenName tokenInfo={tokenInfo} /> cursor: "pointer",
width: "100%",
}}
>
<TokenIcon
mint={mint}
style={{ width: "30px", borderRadius: "15px" }}
/>
<TokenName tokenInfo={tokenInfo} />
</div>
<Chip
variant="outlined"
label={isCommonBase ? <Star /> : <StarOutline />}
onClick={() =>
isCommonBase ? removeBase(tokenInfo) : addNewBase(tokenInfo)
}
/>
</ListItem> </ListItem>
); );
} }
@ -193,12 +227,12 @@ function CommonBases({
commonTokenBases, commonTokenBases,
onClick, onClick,
}: { }: {
commonTokenBases: TokenInfo[]; commonTokenBases: TokenInfo[] | undefined;
onClick: (mint: PublicKey) => void; onClick: (mint: PublicKey) => void;
}) { }) {
return ( return (
<div style={{ padding: "0 20px 20px 20px" }}> <div style={{ padding: "0 20px 20px 20px", position: "sticky" }}>
<h4>Common bases</h4> <h4 style={{ margin: 0 }}>Common bases</h4>
{commonTokenBases?.map((tokenInfo: TokenInfo) => { {commonTokenBases?.map((tokenInfo: TokenInfo) => {
const mint = new PublicKey(tokenInfo.address); const mint = new PublicKey(tokenInfo.address);
return ( return (
@ -208,7 +242,7 @@ function CommonBases({
variant="outlined" variant="outlined"
label={tokenInfo?.symbol} label={tokenInfo?.symbol}
onClick={() => onClick(mint)} onClick={() => onClick(mint)}
style={{ margin: "5px" }} style={{ margin: "0 1px" }}
/> />
); );
})} })}

View File

@ -1,7 +1,12 @@
import React, { useContext, useMemo } from "react"; import React, { useContext, useMemo, useState, useEffect } from "react";
import { TokenInfo } from "@solana/spl-token-registry"; import { TokenInfo } from "@solana/spl-token-registry";
import { SOL_MINT } from "../utils/pubkeys"; import { SOL_MINT } from "../utils/pubkeys";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import { LocalStorage } from "../utils/localStorage";
interface TokenCommonBaseInfo extends TokenInfo {
isCommonBase: boolean;
}
type TokenListContext = { type TokenListContext = {
tokenMap: Map<string, TokenInfo>; tokenMap: Map<string, TokenInfo>;
@ -10,7 +15,10 @@ type TokenListContext = {
swappableTokens: TokenInfo[]; swappableTokens: TokenInfo[];
swappableTokensSollet: TokenInfo[]; swappableTokensSollet: TokenInfo[];
swappableTokensWormhole: TokenInfo[]; swappableTokensWormhole: TokenInfo[];
commonTokenBases: TokenInfo[]; tokenBase: TokenInfo[] | undefined;
addNewBase: (token: TokenInfo) => void;
removeBase: (token: TokenInfo) => void;
tokenBaseMap: Map<string, TokenCommonBaseInfo>;
}; };
const _TokenListContext = React.createContext<null | TokenListContext>(null); const _TokenListContext = React.createContext<null | TokenListContext>(null);
@ -99,13 +107,65 @@ export function TokenListContextProvider(props: any) {
]; ];
}, [tokenList]); }, [tokenList]);
let [tokenBase, setTokenBase] = useState<TokenCommonBaseInfo[] | undefined>(
undefined
);
let [addrList, setValues, removeValue] = LocalStorage("swapui-common-bases");
// Common token bases // Common token bases
const commonTokenBases = useMemo(() => { useEffect(() => {
const cb = props.commonBases?.map((add: PublicKey) => { if (addrList == null) {
return tokenMap.get(add.toString()); addrList = props.commonBases ?? [];
}
if (props.commonBases) {
props.commonBases.forEach((add: any) => setValues(add.toString()));
addrList.concat(props.commonBases);
}
addrList = addrList.map((e: string) => new PublicKey(e.toString()));
const cb = addrList?.map((add: PublicKey) => {
const token = tokenMap.get(add.toString());
token.isCommonBase = true;
setValues(token.address);
return token;
}); });
setTokenBase(cb);
return cb; return cb;
}, [tokenList]); }, [props.commonBases]);
const addNewBase = (token: TokenInfo) => {
// Check if token already a common base
if (
tokenBase?.some((t) => token.address.toString() === t.address.toString())
) {
return;
}
const c: TokenCommonBaseInfo = { ...token, isCommonBase: true };
setValues(token.address);
setTokenBase((prevState) => [...(prevState as TokenCommonBaseInfo[]), c]);
};
const removeBase = (token: TokenInfo) => {
const index =
tokenBase?.findIndex(
(t) => token.address.toString() === t.address.toString()
) ?? -1;
// return if not found
if (index == -1) return;
const tempTokenBase = tokenBase?.slice();
tempTokenBase?.splice(index, 1);
setTokenBase(tempTokenBase);
removeValue(index);
};
// Token map for quick lookup.
const tokenBaseMap = useMemo(() => {
const tokenBaseMap = new Map();
tokenBase?.forEach((t: TokenCommonBaseInfo) => {
tokenBaseMap.set(t.address, t);
});
return tokenBaseMap;
}, [tokenBase]);
return ( return (
<_TokenListContext.Provider <_TokenListContext.Provider
value={{ value={{
@ -115,7 +175,10 @@ export function TokenListContextProvider(props: any) {
swappableTokens, swappableTokens,
swappableTokensWormhole, swappableTokensWormhole,
swappableTokensSollet, swappableTokensSollet,
commonTokenBases, tokenBase,
addNewBase,
removeBase,
tokenBaseMap,
}} }}
> >
{props.children} {props.children}
@ -137,16 +200,22 @@ export function useTokenMap(): Map<string, TokenInfo> {
} }
export function useSwappableTokens() { export function useSwappableTokens() {
const { const { swappableTokens, swappableTokensWormhole, swappableTokensSollet } =
swappableTokens, useTokenListContext();
swappableTokensWormhole,
swappableTokensSollet,
commonTokenBases,
} = useTokenListContext();
return { return {
swappableTokens, swappableTokens,
swappableTokensWormhole, swappableTokensWormhole,
swappableTokensSollet, swappableTokensSollet,
commonTokenBases, };
}
export function useTokenBase() {
const { tokenBase, addNewBase, tokenBaseMap, removeBase } =
useTokenListContext();
return {
tokenBase,
addNewBase,
tokenBaseMap,
removeBase,
}; };
} }

21
src/utils/localStorage.ts Normal file
View File

@ -0,0 +1,21 @@
export function LocalStorage(
name: string
): [any, (val: any) => void, (val: number) => void] {
const currentValue = localStorage.getItem(name);
let addrList = JSON.parse(currentValue as string);
function setValues(val: any): void {
addrList = addrList ? addrList : [];
if (addrList.indexOf(val) == -1) {
addrList.push(val);
localStorage.setItem(name, JSON.stringify(addrList));
}
}
function removeValue(index: number): void {
addrList.splice(index, 1);
localStorage.setItem(name, JSON.stringify(addrList));
}
return [addrList, setValues, removeValue];
}