From e08f1a4fd9f01e16a075f0fe4834da24bce69510 Mon Sep 17 00:00:00 2001 From: Yuru Shao Date: Thu, 16 Jan 2025 23:46:00 -0800 Subject: [PATCH] gui: improve responsiveness (#367) --- anchor/src/react/glam.tsx | 30 +++++-- .../src/app/(vault)/vault/create/page.tsx | 2 +- .../src/app/(vault)/vault/policies/page.tsx | 80 ++++++++++--------- .../src/app/(vault)/vault/trade/page.tsx | 2 +- .../src/app/(vault)/vault/wrap/page.tsx | 4 +- playground/src/components/PageAccess.tsx | 20 ++--- .../components/access/data-table-refresh.tsx | 3 + .../components/access/data-table-toolbar.tsx | 6 +- .../src/components/access/data-table.tsx | 2 +- .../src/components/ui/multiple-select.tsx | 8 +- 10 files changed, 90 insertions(+), 67 deletions(-) diff --git a/anchor/src/react/glam.tsx b/anchor/src/react/glam.tsx index 81b0de62..0df4e46a 100644 --- a/anchor/src/react/glam.tsx +++ b/anchor/src/react/glam.tsx @@ -11,7 +11,7 @@ import { import { useQuery } from "@tanstack/react-query"; import { atomWithStorage } from "jotai/utils"; -import type { StateModel } from "../models"; +import type { DelegateAcl, StateModel } from "../models"; import { GlamClient } from "../client"; import { useAtomValue, useSetAtom } from "jotai/react"; import { LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js"; @@ -36,9 +36,10 @@ interface TokenPrice { interface GlamProviderContext { glamClient: GlamClient; - activeGlamState?: GlamStateCache; vault: Vault; + activeGlamState?: GlamStateCache; glamStatesList: GlamStateCache[]; + delegateAcls: DelegateAcl[]; allGlamStates: StateModel[]; userWallet: UserWallet; prices: TokenPrice[]; @@ -127,6 +128,7 @@ export function GlamProvider({ const setActiveGlamState = useSetAtom(activeGlamStateAtom); const setGlamStatesList = useSetAtom(glamStatesListAtom); + const [delegateAcls, setDelegateAcls] = useState([] as DelegateAcl[]); const [vault, setVault] = useState({} as Vault); const [userWallet, setUserWallet] = useState({} as UserWallet); const wallet = useWallet(); @@ -162,7 +164,7 @@ export function GlamProvider({ if (activeGlamState?.pubkey && wallet?.publicKey) { console.log( "fetching vault data for active glam state:", - activeGlamState.pubkey.toBase58(), + activeGlamState.address, ); const vault = glamClient.getVaultPda(activeGlamState.pubkey); const balances = await fetchBalances(glamClient, vault); @@ -231,7 +233,23 @@ export function GlamProvider({ } refreshVaultHoldings(); - }, [allGlamStatesData, activeGlamState, wallet, cluster]); + }, [allGlamStatesData, wallet, cluster]); + + const refreshDelegateAcls = async () => { + if (activeGlamState?.pubkey) { + console.log( + "fetching delegate acls for active glam state:", + activeGlamState.address, + ); + const glamState = await glamClient.fetchState(activeGlamState?.pubkey); + console.log("delegate acls:", glamState.delegateAcls); + setDelegateAcls(glamState.delegateAcls); + } + }; + + useEffect(() => { + refreshDelegateAcls(); + }, [activeGlamState]); // // Fetch token prices https://station.jup.ag/docs/apis/price-api-v2 @@ -357,9 +375,10 @@ export function GlamProvider({ const value: GlamProviderContext = { glamClient, - activeGlamState, vault, + activeGlamState, glamStatesList: useAtomValue(glamStatesListAtom), + delegateAcls, allGlamStates, userWallet, jupTokenList, @@ -369,6 +388,7 @@ export function GlamProvider({ driftUser, refresh: async () => { refreshVaultHoldings(); + refreshDelegateAcls(); }, }; diff --git a/playground/src/app/(vault)/vault/create/page.tsx b/playground/src/app/(vault)/vault/create/page.tsx index 89bd9a68..7ae6c696 100644 --- a/playground/src/app/(vault)/vault/create/page.tsx +++ b/playground/src/app/(vault)/vault/create/page.tsx @@ -137,7 +137,7 @@ export default function Create() { return ( -
+
diff --git a/playground/src/app/(vault)/vault/policies/page.tsx b/playground/src/app/(vault)/vault/policies/page.tsx index 9cd8cff1..6424accf 100644 --- a/playground/src/app/(vault)/vault/policies/page.tsx +++ b/playground/src/app/(vault)/vault/policies/page.tsx @@ -97,46 +97,48 @@ export default function VaultPoliciesPage() { return ( - -
- ( - - Vault Assets - - - - - Select the assets allowed in the vault. - - - - )} - /> -
- - +
+ +
+ ( + + Vault Assets + + + + + Select the assets allowed in the vault. + + + + )} + /> +
+ + +
-
- + +
); } diff --git a/playground/src/app/(vault)/vault/trade/page.tsx b/playground/src/app/(vault)/vault/trade/page.tsx index a84eb4c8..03636882 100644 --- a/playground/src/app/(vault)/vault/trade/page.tsx +++ b/playground/src/app/(vault)/vault/trade/page.tsx @@ -1012,7 +1012,7 @@ export default function Trade() { const exactMode = swapForm.watch("exactMode"); return ( -
+
; export default function Wrap() { - const { activeGlamState, vault, userWallet, glamClient } = useGlam(); + const { activeGlamState, vault, userWallet, glamClient, refresh } = useGlam(); const [amountAsset, setAmountAsset] = useState("SOL"); const [direction, setDirection] = useState("wrap"); @@ -112,6 +112,8 @@ export default function Wrap() { }); } setIsTxPending(false); + + await refresh(); // refresh vault }; const handleClear = (event: React.MouseEvent) => { diff --git a/playground/src/components/PageAccess.tsx b/playground/src/components/PageAccess.tsx index 00f63ffb..93a4558d 100644 --- a/playground/src/components/PageAccess.tsx +++ b/playground/src/components/PageAccess.tsx @@ -16,15 +16,15 @@ export default function PageAccess({ }: { perms: "vault" | "mint" | "all"; }) { - const { allGlamStates, activeGlamState } = useGlam(); + const { delegateAcls, allGlamStates, activeGlamState, refresh } = useGlam(); const { getLabel } = usePubkeyLabels(); - const fund = (allGlamStates || []).find( + const state = (allGlamStates || []).find( (s) => s.idStr === activeGlamState?.address, ); const data = useMemo(() => { - if (!fund) return []; + if (!state) return []; let treeDataPermissions = vaultTreeDataPermissions; if (perms === "mint") { @@ -40,16 +40,16 @@ export default function PageAccess({ (lvl1: any) => lvl1.children?.map((node: any) => node.id) || [], ) || []; - const owner = fund.owner?.pubkey + const owner = state.owner?.pubkey ? [ { - pubkey: fund.owner.pubkey.toBase58(), + pubkey: state.owner.pubkey.toBase58(), label: "Owner", tags: flatPermissions, }, ] : []; - const delegates = (fund.delegateAcls || []).map((acl: any) => { + const delegates = (delegateAcls || []).map((acl: any) => { const pubkey = acl.pubkey.toBase58(); return { pubkey, @@ -58,14 +58,10 @@ export default function PageAccess({ }; }); return owner.concat(delegates); - }, [fund, getLabel]); + }, [state, getLabel]); const handleSuccess = useCallback(() => { - // Force a re-render when a key is modified - // This will cause getData to run again with the latest fund data - window.setTimeout(() => { - window.location.reload(); - }, 1000); + refresh(); }, []); return ( diff --git a/playground/src/components/access/data-table-refresh.tsx b/playground/src/components/access/data-table-refresh.tsx index 631c48bd..67aac97f 100644 --- a/playground/src/components/access/data-table-refresh.tsx +++ b/playground/src/components/access/data-table-refresh.tsx @@ -7,16 +7,19 @@ import { Button } from "@/components/ui/button"; interface DataTableRefreshProps { table: Table; + onClick?: () => Promise; } export function DataTableRefresh({ table, + onClick, }: DataTableRefreshProps) { return ( diff --git a/playground/src/components/access/data-table-toolbar.tsx b/playground/src/components/access/data-table-toolbar.tsx index c6d254e8..4b96aec6 100644 --- a/playground/src/components/access/data-table-toolbar.tsx +++ b/playground/src/components/access/data-table-toolbar.tsx @@ -48,7 +48,7 @@ export function DataTableToolbar({ const [treeData, setTreeData] = useState(treeDataPermissions); const { updateLabel } = usePubkeyLabels(); - const { glamClient, activeGlamState } = useGlam(); + const { glamClient, activeGlamState, refresh } = useGlam(); const toggleExpandCollapse = () => { setIsExpanded(!isExpanded); @@ -121,8 +121,6 @@ export function DataTableToolbar({ setTreeData(treeDataPermissions); closeButtonRef.current?.click(); - // Wait for blockchain confirmation and refresh - await new Promise((resolve) => setTimeout(resolve, 1000)); onSuccess?.(); } catch (e) { toast({ @@ -232,7 +230,7 @@ export function DataTableToolbar({ )}
- +
); } diff --git a/playground/src/components/access/data-table.tsx b/playground/src/components/access/data-table.tsx index ad6e7cf4..da4804de 100644 --- a/playground/src/components/access/data-table.tsx +++ b/playground/src/components/access/data-table.tsx @@ -120,7 +120,7 @@ export function DataTable({ setIsExpanded(!isExpanded); }; - const { glamClient, activeGlamState } = useGlam(); + const { glamClient, activeGlamState, refresh } = useGlam(); // Helper function to reset states const resetStates = () => { diff --git a/playground/src/components/ui/multiple-select.tsx b/playground/src/components/ui/multiple-select.tsx index 40c0db79..6a2c2733 100644 --- a/playground/src/components/ui/multiple-select.tsx +++ b/playground/src/components/ui/multiple-select.tsx @@ -51,6 +51,7 @@ function MultiSelect({ filterOption, }: MultiSelectProps) { const [open, setOpen] = React.useState(false); + const [batchSize, setBatchSize] = React.useState(100); const [searchInputValue, setSearchInputValue] = React.useState(""); const selectedSet = React.useMemo(() => new Set(selected), [selected]); @@ -61,7 +62,10 @@ function MultiSelect({ ); const filteredOptions = React.useMemo(() => { - if (!open) return []; + if (!open) { + setBatchSize(100); + return []; + } if (!searchInputValue) return availableOptions; return availableOptions.filter((option) => filterOption @@ -70,8 +74,6 @@ function MultiSelect({ ); }, [open, availableOptions, searchInputValue, filterOption]); - const [batchSize, setBatchSize] = React.useState(100); - React.useEffect(() => { if (batchSize < filteredOptions.length) { const timeout = setTimeout(() => {