From bf4d0ac136a6c7a3d8e6e0b9bce979202d22dbff Mon Sep 17 00:00:00 2001 From: elehmandevelopment Date: Wed, 28 Feb 2024 15:11:10 -0700 Subject: [PATCH 1/4] refactor: update ao token support settings --- assets/_locales/en/messages.json | 4 ++ src/components/dashboard/SignSettings.tsx | 2 +- src/components/dashboard/Tokens.tsx | 75 ++++++++++++++++++----- src/settings/index.ts | 8 --- 4 files changed, 64 insertions(+), 25 deletions(-) diff --git a/assets/_locales/en/messages.json b/assets/_locales/en/messages.json index e88d2bd32..ba10f60ca 100644 --- a/assets/_locales/en/messages.json +++ b/assets/_locales/en/messages.json @@ -1669,5 +1669,9 @@ "ao_announcement_title": { "message": "AO testnet is now live!", "description": "ao announcement title text" + }, + "import_token": { + "message": "Import Token", + "description": "import token button description" } } diff --git a/src/components/dashboard/SignSettings.tsx b/src/components/dashboard/SignSettings.tsx index 8a12bde7c..09786c4e3 100644 --- a/src/components/dashboard/SignSettings.tsx +++ b/src/components/dashboard/SignSettings.tsx @@ -22,7 +22,7 @@ export default function SignSettings() { const currentSetting = await ExtensionStorage.get( "setting_sign_notification" ); - setSignSettingsState(currentSetting !== "false"); + setSignSettingsState(currentSetting); // Check if signatureAllowance is set, if not, initialize to 10 let allowance = await ExtensionStorage.get("signatureAllowance"); diff --git a/src/components/dashboard/Tokens.tsx b/src/components/dashboard/Tokens.tsx index f8edd8286..0499708ae 100644 --- a/src/components/dashboard/Tokens.tsx +++ b/src/components/dashboard/Tokens.tsx @@ -1,10 +1,14 @@ import { useStorage } from "@plasmohq/storage/hook"; import { ExtensionStorage } from "~utils/storage"; import { useLocation, useRoute } from "wouter"; -import { useEffect, useMemo } from "react"; +import { useEffect, useMemo, useState } from "react"; import type { Token } from "~tokens/token"; import { Reorder } from "framer-motion"; import TokenListItem from "./list/TokenListItem"; +import styled from "styled-components"; +import PermissionCheckbox from "~components/auth/PermissionCheckbox"; +import browser from "webextension-polyfill"; +import { Button, Spacer, Text } from "@arconnect/components"; export default function Tokens() { // tokens @@ -16,6 +20,21 @@ export default function Tokens() { [] ); + const [aoSettingsState, setaoSettingsState] = useState(true); + + useEffect(() => { + (async () => { + const currentSetting = await ExtensionStorage.get("setting_ao_support"); + setaoSettingsState(currentSetting); + })(); + }, []); + + const toggleaoSettings = async () => { + const newSetting = !aoSettingsState; + setaoSettingsState(newSetting); + await ExtensionStorage.set("setting_ao_support", newSetting); + }; + // router const [matches, params] = useRoute<{ id?: string }>("/tokens/:id?"); const [, setLocation] = useLocation(); @@ -43,20 +62,44 @@ export default function Tokens() { }, [tokens, activeTokenSetting]); return ( - - {tokens.map((token) => ( - - ))} - + +
+ + {browser.i18n.getMessage(!!aoSettingsState ? "enabled" : "disabled")} +
+ + {browser.i18n.getMessage("setting_ao_support_description")} + +
+ + + {tokens.map((token) => ( + + ))} + +
+ +
); } + +const Wrapper = styled.div` + display: flex; + flex-direction: column; + justify-content: space-between; + height: calc(100% - 64px); +`; diff --git a/src/settings/index.ts b/src/settings/index.ts index 9b91d0e7a..476cd1a7a 100644 --- a/src/settings/index.ts +++ b/src/settings/index.ts @@ -16,14 +16,6 @@ export const PREFIX = "setting_"; /** All settings */ const settings: Setting[] = [ - new Setting({ - name: "ao_support", - displayName: "setting_ao_support", - icon: LayersIcon, - description: "setting_ao_support_description", - type: "boolean", - defaultValue: true - }), new Setting({ name: "fee_multiplier", displayName: "setting_fee_multiplier", From cd64192ed10ac718d93b26adfcce31de3f247ffe Mon Sep 17 00:00:00 2001 From: elehmandevelopment Date: Wed, 28 Feb 2024 15:34:49 -0700 Subject: [PATCH 2/4] feat: import new token setting --- src/components/dashboard/Tokens.tsx | 11 ++++++++++- src/components/dashboard/subsettings/AddToken.tsx | 3 +++ src/routes/dashboard/index.tsx | 7 +++++-- 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 src/components/dashboard/subsettings/AddToken.tsx diff --git a/src/components/dashboard/Tokens.tsx b/src/components/dashboard/Tokens.tsx index 0499708ae..8c59517e1 100644 --- a/src/components/dashboard/Tokens.tsx +++ b/src/components/dashboard/Tokens.tsx @@ -46,6 +46,9 @@ export default function Tokens() { ); useEffect(() => { + if (activeTokenSetting === "new") { + return; + } if (!matches) return; const firstToken = tokens?.[0]; @@ -61,6 +64,10 @@ export default function Tokens() { setLocation("/tokens/" + firstToken.id); }, [tokens, activeTokenSetting]); + const addToken = () => { + setLocation("/tokens/new"); + }; + return (
@@ -92,7 +99,9 @@ export default function Tokens() { ))}
- +
); } diff --git a/src/components/dashboard/subsettings/AddToken.tsx b/src/components/dashboard/subsettings/AddToken.tsx new file mode 100644 index 000000000..5b274e0c9 --- /dev/null +++ b/src/components/dashboard/subsettings/AddToken.tsx @@ -0,0 +1,3 @@ +export default function AddContact() { + return <>{"Add Token :)"} ; +} diff --git a/src/routes/dashboard/index.tsx b/src/routes/dashboard/index.tsx index 0bf3c6c85..10c1caf25 100644 --- a/src/routes/dashboard/index.tsx +++ b/src/routes/dashboard/index.tsx @@ -37,6 +37,7 @@ import settings from "~settings"; import { PageType, trackPage } from "~utils/analytics"; import { formatSettingName } from "~utils/format"; import SignSettings from "~components/dashboard/SignSettings"; +import AddToken from "~components/dashboard/subsettings/AddToken"; export default function Settings({ params }: Props) { // router location @@ -148,10 +149,12 @@ export default function Settings({ params }: Props) { {activeSetting === "wallets" && activeSubSetting === "new" && ( )} - {activeSetting === "tokens" && activeSubSetting && ( + {activeSetting === "tokens" && activeSubSetting !== "new" && ( )} - + {activeSetting === "tokens" && activeSubSetting === "new" && ( + + )} {activeSetting === "contacts" && activeSubSetting && activeSubSetting.startsWith("new") && ( From f1b882763c48b4a34aca75785c1338c5a9af9c40 Mon Sep 17 00:00:00 2001 From: nicholas ma Date: Wed, 28 Feb 2024 19:46:17 -0800 Subject: [PATCH 3/4] feat: added ability to paste process ids --- assets/_locales/en/messages.json | 8 ++ src/components/dashboard/Tokens.tsx | 40 ++++++- src/components/dashboard/list/BaseElement.tsx | 42 ++++--- .../dashboard/list/TokenListItem.tsx | 32 +++++- .../dashboard/subsettings/AddToken.tsx | 105 +++++++++++++++++- src/routes/popup/send/index.tsx | 26 ++--- src/tokens/aoTokens/ao.ts | 15 +-- src/tokens/aoTokens/router.ts | 18 +-- src/tokens/index.ts | 17 +++ 9 files changed, 242 insertions(+), 61 deletions(-) diff --git a/assets/_locales/en/messages.json b/assets/_locales/en/messages.json index ba10f60ca..893b2682f 100644 --- a/assets/_locales/en/messages.json +++ b/assets/_locales/en/messages.json @@ -1673,5 +1673,13 @@ "import_token": { "message": "Import Token", "description": "import token button description" + }, + "token_already_added": { + "message": "Token Already Added", + "description": "token already imported" + }, + "token_imported": { + "message": "Token Succesfully Added", + "description": "token added" } } diff --git a/src/components/dashboard/Tokens.tsx b/src/components/dashboard/Tokens.tsx index 8c59517e1..eb0ab25ca 100644 --- a/src/components/dashboard/Tokens.tsx +++ b/src/components/dashboard/Tokens.tsx @@ -2,13 +2,14 @@ import { useStorage } from "@plasmohq/storage/hook"; import { ExtensionStorage } from "~utils/storage"; import { useLocation, useRoute } from "wouter"; import { useEffect, useMemo, useState } from "react"; -import type { Token } from "~tokens/token"; +import type { Token, TokenType } from "~tokens/token"; import { Reorder } from "framer-motion"; import TokenListItem from "./list/TokenListItem"; import styled from "styled-components"; import PermissionCheckbox from "~components/auth/PermissionCheckbox"; import browser from "webextension-polyfill"; -import { Button, Spacer, Text } from "@arconnect/components"; +import { Button, Label, Spacer, Text } from "@arconnect/components"; +import { useAoTokens } from "~tokens/aoTokens/ao"; export default function Tokens() { // tokens @@ -20,11 +21,26 @@ export default function Tokens() { [] ); + const [aoTokens] = useAoTokens(); + + const enhancedAoTokens = useMemo(() => { + return aoTokens.map((token) => ({ + id: token.id, + defaultLogo: token.Logo, + balance: token.balance, + ticker: token.Ticker, + type: "asset" as TokenType, + name: token.Name + })); + }, [aoTokens]); + const [aoSettingsState, setaoSettingsState] = useState(true); useEffect(() => { (async () => { - const currentSetting = await ExtensionStorage.get("setting_ao_support"); + const currentSetting = await ExtensionStorage.get( + "setting_ao_support" + ); setaoSettingsState(currentSetting); })(); }, []); @@ -97,9 +113,25 @@ export default function Tokens() { key={token.id} /> ))} + + {enhancedAoTokens.length > 0 && ( + <> + + {enhancedAoTokens.map((token) => ( + + ))} + + )} - diff --git a/src/components/dashboard/list/BaseElement.tsx b/src/components/dashboard/list/BaseElement.tsx index c74a3656c..dba7a2de0 100644 --- a/src/components/dashboard/list/BaseElement.tsx +++ b/src/components/dashboard/list/BaseElement.tsx @@ -13,10 +13,11 @@ export default function BaseElement({ active, img, dragControls, + ao = false, ...props }: Props & HTMLProps) { return ( - + {children}
@@ -31,35 +32,39 @@ export default function BaseElement({ export const setting_element_padding = ".8rem"; -const SettingWrapper = styled.div<{ active: boolean }>` +const SettingWrapper = styled.div<{ active: boolean; ao?: boolean }>` display: flex; align-items: center; justify-content: space-between; padding: ${setting_element_padding}; border-radius: 20px; overflow: hidden; - cursor: pointer; + cursor: ${(props) => (props.ao ? "default" : "pointer")}; background-color: rgba( ${(props) => props.theme.theme}, ${(props) => props.active ? (props.theme.displayTheme === "light" ? ".2" : ".1") : "0"} ); transition: all 0.23s ease-in-out; - - &:hover { - background-color: rgba( - ${(props) => - props.theme.theme + - ", " + - (props.active - ? props.theme.displayTheme === "light" - ? ".24" - : ".14" - : props.theme.displayTheme === "light" - ? ".14" - : ".04")} - ); - } + ${(props) => + !props.ao && + ` + &:hover { + background-color: rgba( + ${ + props.theme.theme + + ", " + + (props.active + ? props.theme.displayTheme === "light" + ? ".24" + : ".14" + : props.theme.displayTheme === "light" + ? ".14" + : ".04") + } + ); + } + `} `; const ContentWrapper = styled.div` @@ -122,6 +127,7 @@ interface Props { description: string | ReactNode; active?: boolean; img?: string; + ao?: boolean; dragControls?: DragControls; } diff --git a/src/components/dashboard/list/TokenListItem.tsx b/src/components/dashboard/list/TokenListItem.tsx index c1e118d59..9b7dd3dfb 100644 --- a/src/components/dashboard/list/TokenListItem.tsx +++ b/src/components/dashboard/list/TokenListItem.tsx @@ -11,8 +11,10 @@ import BaseElement from "./BaseElement"; import styled from "styled-components"; import { useGateway } from "~gateways/wayfinder"; import { concatGatewayURL } from "~gateways/utils"; +import aoLogo from "url:/assets/ecosystem/ao-logo.svg"; +import { getUserAvatar } from "~lib/avatar"; -export default function TokenListItem({ token, active }: Props) { +export default function TokenListItem({ token, active, ao }: Props) { // format address const formattedAddress = useMemo( () => formatAddress(token.id, 8), @@ -40,6 +42,10 @@ export default function TokenListItem({ token, active }: Props) { `${concatGatewayURL(token.gateway || gateway)}/${token.id}` ); } + if (ao) { + const logo = await getUserAvatar(token.defaultLogo); + return setImage(logo); + } // query community logo using Warp DRE const node = new DRENode(await getDreForToken(token.id)); @@ -58,7 +64,21 @@ export default function TokenListItem({ token, active }: Props) { // router const [, setLocation] = useLocation(); - return ( + return ao ? ( + + {formattedAddress} + ao logo +
+ } + active={active} + > + + + ) : ( props.theme.cardBorder}); + border-radius: 2px; +`; + const TokenLogo = styled.img.attrs({ alt: "token-logo", draggable: false @@ -111,5 +138,6 @@ const TokenType = styled.span` interface Props { token: Token; + ao?: boolean; active: boolean; } diff --git a/src/components/dashboard/subsettings/AddToken.tsx b/src/components/dashboard/subsettings/AddToken.tsx index 5b274e0c9..ee8fc1e66 100644 --- a/src/components/dashboard/subsettings/AddToken.tsx +++ b/src/components/dashboard/subsettings/AddToken.tsx @@ -1,3 +1,104 @@ -export default function AddContact() { - return <>{"Add Token :)"} ; +import { Input, Label, useInput, useToasts } from "@arconnect/components"; +import { Button, Select, Spacer, Text } from "@arconnect/components"; +import browser from "webextension-polyfill"; +import { useEffect, useState } from "react"; +import { useAo, type TokenInfo, useAoTokens } from "~tokens/aoTokens/ao"; +import { getTokenInfo } from "~tokens/aoTokens/router"; +import styled from "styled-components"; +import { isAddress } from "~utils/assertions"; +import { getAoTokens } from "~tokens"; +import { ExtensionStorage } from "~utils/storage"; +import { SubTitle } from "./ContactSettings"; + +export default function AddToken() { + const targetInput = useInput(); + const [token, setToken] = useState(); + const ao = useAo(); + const { setToast } = useToasts(); + + const onImportToken = async () => { + try { + const tokens = await getAoTokens(); + + if (tokens.find((token) => token.processId === targetInput.state)) { + setToast({ + type: "error", + content: browser.i18n.getMessage("token_already_added"), + duration: 3000 + }); + throw new Error("Token already added"); + } + + tokens.push({ ...token, processId: targetInput.state }); + await ExtensionStorage.set("ao_tokens", tokens); + setToast({ + type: "success", + content: browser.i18n.getMessage("token_imported"), + duration: 3000 + }); + } catch (err) { + console.log("err", err); + } + }; + + useEffect(() => { + const fetchTokenInfo = async () => { + try { + //TODO double check + isAddress(targetInput.state); + const tokenInfo = await getTokenInfo(targetInput.state, ao); + setToken(tokenInfo); + } catch (err) { + console.log("herr", err); + } + }; + fetchTokenInfo(); + }, [targetInput.state]); + + return ( + +
+ + {browser.i18n.getMessage("import_token")} + + + {token && ( + + TICKER: + {token.Ticker} + NAME: + {token.Name} + + )} +
+ +
+ ); } + +const Title = styled(Text).attrs({ + title: true, + noMargin: true +})` + font-weight: 600; + padding-bottom: 10px; +`; + +const TokenWrapper = styled.div` + padding: 36px 0; +`; + +const Wrapper = styled.div` + display: flex; + flex-direction: column; + justify-content: space-between; + height: 100%; +`; diff --git a/src/routes/popup/send/index.tsx b/src/routes/popup/send/index.tsx index 16943cb7e..15bd3e89f 100644 --- a/src/routes/popup/send/index.tsx +++ b/src/routes/popup/send/index.tsx @@ -542,20 +542,18 @@ export default function Send({ id }: Props) { key={i} /> ))} - {aoTokens - .filter((token) => token.balance > 0) - .map((token, i) => ( - updateSelectedToken(token.id)} - /> - ))} + {aoTokens.map((token, i) => ( + updateSelectedToken(token.id)} + /> + ))} {tokens diff --git a/src/tokens/aoTokens/ao.ts b/src/tokens/aoTokens/ao.ts index a9c4d85c6..a09868806 100644 --- a/src/tokens/aoTokens/ao.ts +++ b/src/tokens/aoTokens/ao.ts @@ -92,12 +92,10 @@ export function useAoTokens(): [TokenInfoWithBalance[], boolean] { ); const tokensWithBalances = useMemo( () => - tokens - .map((token) => ({ - ...token, - balance: balances.find((bal) => bal.id === token.id)?.balance || 0 - })) - .filter((token) => token.balance > 0), + tokens.map((token) => ({ + ...token, + balance: balances.find((bal) => bal.id === token.id)?.balance || 0 + })), [tokens, balances] ); @@ -126,6 +124,9 @@ export function useAoTokens(): [TokenInfoWithBalance[], boolean] { setLoading(true); try { + if (!aoSetting) { + return setTokens([]); + } setTokens( await Promise.all( ids.map(async (id) => ({ @@ -139,7 +140,7 @@ export function useAoTokens(): [TokenInfoWithBalance[], boolean] { setLoading(false); })(); - }, [ids, ao, loadingIDs]); + }, [ids, ao, loadingIDs, aoSetting]); useEffect(() => { (async () => { diff --git a/src/tokens/aoTokens/router.ts b/src/tokens/aoTokens/router.ts index 6554d4bc8..4a9e7de67 100644 --- a/src/tokens/aoTokens/router.ts +++ b/src/tokens/aoTokens/router.ts @@ -1,6 +1,7 @@ import { useEffect, useState } from "react"; import { useAo, type AoInstance, getTagValue, type TokenInfo } from "./ao"; import { ROUTER_PROCESS, type Message } from "./config"; +import { getAoTokens } from "~tokens"; export async function getTokenInfo( id: string, @@ -32,17 +33,6 @@ export async function getTokenInfo( return { Denomination: 1 }; } -export async function getTokens(ao: AoInstance) { - const res = await ao.dryrun({ - Id: "0000000000000000000000000000000000000000001", - Owner: "0000000000000000000000000000000000000000001", - process: ROUTER_PROCESS, - tags: [{ name: "Action", value: "Get-Tokens" }] - }); - - return JSON.parse(res.Output.data || "[]"); -} - export function useTokenIDs(): [string[], boolean] { // all token ids const [tokenIDs, setTokenIDs] = useState([]); @@ -58,9 +48,9 @@ export function useTokenIDs(): [string[], boolean] { setLoading(true); try { - const ids = await getTokens(ao); - // TODO: THIS IS ADDITIONALLY ADDED FOR TESTING PURPOSES - setTokenIDs([...ids, "HineOJKYihQiIcZEWxFtgTyxD_dhDNqGvoBlWj55yDs"]); + const aoTokens = await getAoTokens(); + const aoTokenIds = aoTokens.map((token) => token.processId); + setTokenIDs(aoTokenIds); } catch {} setLoading(false); diff --git a/src/tokens/index.ts b/src/tokens/index.ts index 820989a87..8dad339b6 100644 --- a/src/tokens/index.ts +++ b/src/tokens/index.ts @@ -12,6 +12,7 @@ import { type TokenState, type TokenType } from "./token"; +import type { TokenInfo } from "./aoTokens/ao"; /** Default tokens */ const defaultTokens: Token[] = [ @@ -52,6 +53,16 @@ export async function getTokens() { return tokens || defaultTokens; } +/** + * Get stored ao tokens + */ +export async function getAoTokens() { + const tokens = await ExtensionStorage.get< + (TokenInfo & { processId: string })[] + >("ao_tokens"); + + return tokens || []; +} /** * Add a token to the stored tokens @@ -108,6 +119,12 @@ export async function removeToken(id: string) { ); } +export async function removeAoToken(id: string) { + const tokens = await getAoTokens(); + const updatedTokens = tokens.filter((token) => token.processId !== id); + await ExtensionStorage.set("ao_tokens", updatedTokens); +} + /** * Get DRE node for a token */ From 77906f0e07b0f50410f9caaffb765c9d4e4026ca Mon Sep 17 00:00:00 2001 From: nicholas ma Date: Wed, 28 Feb 2024 22:45:23 -0800 Subject: [PATCH 4/4] chore: version bump beta --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e5d713fce..7c3b56b71 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "arconnect", "displayName": "ArConnect", - "version": "1.2.0", + "version": "1.3.1", "description": "__MSG_extensionDescription__", "author": "th8ta", "packageManager": "yarn@1.22.18",