Add and remove common base
This commit is contained in:
parent
9c53747071
commit
4b1de9c7e8
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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" }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
Loading…
Reference in New Issue