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

View File

@ -10,14 +10,16 @@ import {
TextField,
List,
ListItem,
ListSubheader,
Typography,
Chip,
Avatar,
Tabs,
Tab,
} from "@material-ui/core";
import { StarOutline, Star } from "@material-ui/icons";
import { TokenIcon } from "./Swap";
import { useSwappableTokens } from "../context/TokenList";
import { useSwappableTokens, useTokenBase } from "../context/TokenList";
import { useMediaQuery } from "@material-ui/core";
const useStyles = makeStyles((theme) => ({
@ -54,12 +56,9 @@ export default function TokenDialog({
const [tokenFilter, setTokenFilter] = useState("");
const filter = tokenFilter.toLowerCase();
const styles = useStyles();
const {
swappableTokens,
swappableTokensSollet,
swappableTokensWormhole,
commonTokenBases,
} = useSwappableTokens();
const { swappableTokens, swappableTokensSollet, swappableTokensWormhole } =
useSwappableTokens();
const { tokenBase, addNewBase, tokenBaseMap, removeBase } = useTokenBase();
const displayTabs = !useMediaQuery("(max-width:450px)");
const selectedTokens =
tabSelection === 0
@ -103,13 +102,17 @@ export default function TokenDialog({
</DialogTitle>
<DialogContent className={styles.dialogContent} dividers={true}>
<List disablePadding>
<CommonBases
commonTokenBases={commonTokenBases}
onClick={(mint) => {
setMint(mint);
onClose();
}}
/>
{tokenBase?.length != 0 && (
<ListSubheader style={{ backgroundColor: "white" }}>
<CommonBases
commonTokenBases={tokenBase}
onClick={(mint) => {
setMint(mint);
onClose();
}}
/>
</ListSubheader>
)}
{tokens.map((tokenInfo: TokenInfo) => (
<TokenListItem
key={tokenInfo.address}
@ -118,6 +121,15 @@ export default function TokenDialog({
setMint(mint);
onClose();
}}
addNewBase={(token) => {
addNewBase(token);
}}
isCommonBase={
tokenBaseMap.get(tokenInfo.address.toString()) ? true : false
}
removeBase={(token) => {
removeBase(token);
}}
/>
))}
</List>
@ -159,19 +171,41 @@ export default function TokenDialog({
function TokenListItem({
tokenInfo,
onClick,
addNewBase,
removeBase,
isCommonBase,
}: {
tokenInfo: TokenInfo;
onClick: (mint: PublicKey) => void;
addNewBase: (token: TokenInfo) => void;
removeBase: (token: TokenInfo) => void;
isCommonBase: Boolean;
}) {
const mint = new PublicKey(tokenInfo.address);
return (
<ListItem
button
onClick={() => onClick(mint)}
style={{ padding: "10px 20px" }}
>
<TokenIcon mint={mint} style={{ width: "30px", borderRadius: "15px" }} />
<TokenName tokenInfo={tokenInfo} />
<ListItem>
<div
onClick={() => onClick(mint)}
style={{
padding: "10px 20px",
display: "flex",
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>
);
}
@ -193,12 +227,12 @@ function CommonBases({
commonTokenBases,
onClick,
}: {
commonTokenBases: TokenInfo[];
commonTokenBases: TokenInfo[] | undefined;
onClick: (mint: PublicKey) => void;
}) {
return (
<div style={{ padding: "0 20px 20px 20px" }}>
<h4>Common bases</h4>
<div style={{ padding: "0 20px 20px 20px", position: "sticky" }}>
<h4 style={{ margin: 0 }}>Common bases</h4>
{commonTokenBases?.map((tokenInfo: TokenInfo) => {
const mint = new PublicKey(tokenInfo.address);
return (
@ -208,7 +242,7 @@ function CommonBases({
variant="outlined"
label={tokenInfo?.symbol}
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 { SOL_MINT } from "../utils/pubkeys";
import { PublicKey } from "@solana/web3.js";
import { LocalStorage } from "../utils/localStorage";
interface TokenCommonBaseInfo extends TokenInfo {
isCommonBase: boolean;
}
type TokenListContext = {
tokenMap: Map<string, TokenInfo>;
@ -10,7 +15,10 @@ type TokenListContext = {
swappableTokens: TokenInfo[];
swappableTokensSollet: 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);
@ -99,13 +107,65 @@ export function TokenListContextProvider(props: any) {
];
}, [tokenList]);
let [tokenBase, setTokenBase] = useState<TokenCommonBaseInfo[] | undefined>(
undefined
);
let [addrList, setValues, removeValue] = LocalStorage("swapui-common-bases");
// Common token bases
const commonTokenBases = useMemo(() => {
const cb = props.commonBases?.map((add: PublicKey) => {
return tokenMap.get(add.toString());
useEffect(() => {
if (addrList == null) {
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;
}, [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 (
<_TokenListContext.Provider
value={{
@ -115,7 +175,10 @@ export function TokenListContextProvider(props: any) {
swappableTokens,
swappableTokensWormhole,
swappableTokensSollet,
commonTokenBases,
tokenBase,
addNewBase,
removeBase,
tokenBaseMap,
}}
>
{props.children}
@ -137,16 +200,22 @@ export function useTokenMap(): Map<string, TokenInfo> {
}
export function useSwappableTokens() {
const {
swappableTokens,
swappableTokensWormhole,
swappableTokensSollet,
commonTokenBases,
} = useTokenListContext();
const { swappableTokens, swappableTokensWormhole, swappableTokensSollet } =
useTokenListContext();
return {
swappableTokens,
swappableTokensWormhole,
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];
}